package jetbrains.mps.build.util;

/*Generated by MPS */

import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.module.SModule;
import jetbrains.mps.project.AbstractModule;
import java.io.File;

public class RelativePathHelper {
  private String myBasePath;
  public RelativePathHelper(String basePath) {
    myBasePath = normalizePath(basePath, true);
  }

  /**
   * Transition method to avoid uses of Context.defaultContext().getRelativePathHelper() when we know for sure module is not transient
   */
  @Nullable
  public static RelativePathHelper forModule(SModule module) {
    if (module == null || ((AbstractModule) module).getDescriptorFile() == null || module.isPackaged()) {
      return null;
    }
    String basePath = ((AbstractModule) module).getDescriptorFile().getParent().getPath();
    return new RelativePathHelper(basePath);
  }

  public boolean isRelative(String fullPath) {
    return normalizePath(fullPath, false).startsWith(myBasePath);
  }

  public String makeRelative(String fullPath) throws PathException {
    if ((fullPath == null || fullPath.length() == 0)) {
      return "";
    }
    final String normalized = normalizePath(fullPath, false);
    if (normalized.startsWith(myBasePath)) {
      return normalized.substring(myBasePath.length());
      // XXX should I check for myBasePath == fullPath + '/'?
    }
    // The purpose of the code below is to keep this class purely string/Path-based, without need to access FS or care about file existence.
    try {
      String[] base = myBasePath.split("/");
      String[] target = normalized.split("/");
      int commonLength = 0;
      while (commonLength < target.length && commonLength < base.length && target[commonLength].equals(base[commonLength])) {
        commonLength++;
      }
      // XXX why not return normalized, but exception?
      if (commonLength == 0) {
        throw new PathException(String.format("No common path element found for '%s' and '%s'", myBasePath, normalized));
      }
      if (base.length == target.length && target.length == commonLength) {
        // though there's a check, above, that covers equal paths scenario, we may face normalizedPath that is the same as base path but technically not
        // equal due to trailing slash, e.g. RPH("base/").makeRelative("base")
        return "";
      }

      StringBuilder relative = new StringBuilder();
      assert base.length >= commonLength;
      for (int i = base.length - commonLength; i > 0; i--) {
        relative.append("../");
      }
      assert target.length >= commonLength;
      for (int i = commonLength; i < target.length; i++) {
        relative.append(target[i]).append('/');
      }
      // if original requested path didn't end with slash, keep relative without slash as well
      if (normalized.charAt(normalized.length() - 1) != '/' && relative.charAt(relative.length() - 1) == '/') {
        relative.setLength(relative.length() - 1);
      }

      return relative.toString();
    } catch (Exception ex) {
      throw new PathException(ex, ex.getMessage());
    }
  }

  public String makeAbsolute(String shortPath) throws PathException {
    if ((shortPath == null || shortPath.length() == 0)) {
      return myBasePath;
    }
    shortPath = normalizePath(shortPath, false);
    if (shortPath.startsWith("./")) {
      shortPath = shortPath.substring(2);
    } else if (shortPath.charAt(0) == '/') {
      shortPath = shortPath.substring(1);
    }
    try {
      File res = new File(myBasePath, shortPath);
      return normalizePath(res.getCanonicalPath(), false);
    } catch (Exception ex) {
      throw new PathException(ex, ex.getMessage());
    }
  }

  public String getBasePath() {
    return myBasePath;
  }


  /**
   * Translates backslashes in the path, if any, to regular slashed, and appends a trailing one if requested.
   * Doesn't require file to exists, operates with strings only.
   * Accepts null path, treats it as empty path; never returns null
   */
  public static String normalizePath(String path, boolean addSlash) {
    if (path == null) {
      path = "";
    }
    path = path.replace('\\', '/');
    if (addSlash && (path.length() == 0 || path.charAt(path.length() - 1) != '/')) {
      path = path + '/';
    }
    return path;
  }


  public static class PathException extends Exception {
    /*package*/ PathException(String message) {
      super(message);
    }
    public PathException(Throwable cause, String message) {
      super(message, cause);
    }
  }
}
