package org.unicode.cldr.tool;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.unicode.cldr.util.CLDRFile;
import org.unicode.cldr.util.CLDRFile.Status;
import org.unicode.cldr.util.CldrUtility;
import org.unicode.cldr.util.CldrUtility.SimpleLineComparator;
import org.unicode.cldr.util.Factory;
import org.unicode.cldr.util.LocaleIDParser;
import org.unicode.cldr.util.SimpleFactory;
import org.unicode.cldr.util.XPathParts;

import com.ibm.icu.dev.test.util.BagFormatter;

public class PivotData {
  public static final boolean DEBUG = true;

  private static Matcher fileMatcher;
  
  public static void main(String[] args) throws IOException {
    System.out.println("WARNING: Must be done in 3 phases. -DPhase=1, then -DPhase=2, then -DPhase=3" + CldrUtility.LINE_SEPARATOR +
        "These are Lang+Script+Region, then Lang+Region, then Lang+Script" + CldrUtility.LINE_SEPARATOR +
    "Inspect and check-in after each phase");
    fileMatcher = Pattern.compile(CldrUtility.getProperty("FILE", ".*")).matcher("");
    int phase = Integer.parseInt(CldrUtility.getProperty("phase", null));
    Set<LocaleIDParser.Level> conditions = null;
    switch(phase) {
      case 1: conditions = EnumSet.of(LocaleIDParser.Level.Language, 
          LocaleIDParser.Level.Script,
          LocaleIDParser.Level.Region
      );
      break;
      case 2: conditions = EnumSet.of(LocaleIDParser.Level.Language, 
          LocaleIDParser.Level.Region
      );
      break;
      case 3: conditions = EnumSet.of(LocaleIDParser.Level.Language, 
          LocaleIDParser.Level.Script
      );
      break;
    }

    try {
      Factory cldrFactory = Factory.make(CldrUtility.MAIN_DIRECTORY, ".*");
      PivotData pd = new PivotData(cldrFactory, CldrUtility.MAIN_DIRECTORY, CldrUtility.GEN_DIRECTORY + "pivot/");
      pd.pivotGroup(cldrFactory, conditions);
    } finally {
      System.out.println("DONE");
    }
  }

  private void pivotGroup(Factory cldrFactory, Set<LocaleIDParser.Level> conditions) throws IOException {
    CLDRFile supplementalMetadata = cldrFactory.make("supplementalMetadata", false);
    if (false) for (Iterator it = supplementalMetadata.iterator(); it.hasNext();) {
      System.out.println(it.next());
    }
    String defaultContentList = supplementalMetadata.getFullXPath("//supplementalData/metadata/defaultContent", true);

    XPathParts parts = new XPathParts();
    parts.set(defaultContentList);
    String list = parts.getAttributeValue(-1, "locales");
    String[] items = list.split("\\s+");
    for (String item : items) {
      if (!fileMatcher.reset(item).matches()) {
        continue;
      }
      if (lidp.set(item).getLevels().equals(conditions)) {
        writePivot(item);
      }
    }
  }
  
  private LocaleIDParser lidp = new LocaleIDParser();
  
  private Factory factory;
  private String outputDirectory;

  private String sourceDirectory;
  
  PivotData(Factory factory, String sourceDirectory, String targetDirectory) {
    this.factory = factory;
    this.sourceDirectory = sourceDirectory;
    this.outputDirectory = targetDirectory;
  }
  
  /**
   * Move all of the contents in the localeID to the parent. Maintain, however,
   * the same nominal contents by moving all the replaced contents (inherited or
   * not) to the other children.
   * 
   * @param localeID
   *          to modify
   * @return Return list of locale IDs written out
   * @throws IOException 
   */
  public int writePivot(String localeID) throws IOException {
    int countChanges = 0;
    // first, find my parent and all of my siblings
    CLDRFile me = factory.make(localeID, false);
    if (me.getFullXPath("//ldml/alias",true) != null) {
      throw new IllegalArgumentException("File cannot be completely aliased: " + localeID);
    }
    String parentID = LocaleIDParser.getParent(localeID);
    
    if (DEBUG) System.out.format("LocaleID: %s, %s" + CldrUtility.LINE_SEPARATOR, localeID, parentID);
    
    // find all the unique paths that I have, where the value or fullpath is different from the parent.
    // AND the parent has the path
    
    Set<String> uniquePaths = new TreeSet<String>();
    CLDRFile resolvedParent = factory.make(parentID, true);
    if (resolvedParent.getFullXPath("//ldml/alias",true) != null) {
      throw new IllegalArgumentException("File cannot be completely aliased: " + localeID);
    }

    for (Iterator<String> it = me.iterator(); it.hasNext();) {
      String path = it.next();
      if (path.startsWith("//ldml/identity")) continue;
      String fullPath = me.getFullXPath(path);
      String value = me.getStringValue(path);
      String oldFullXPath = resolvedParent.getFullXPath(path);
      if (oldFullXPath == null) {
        uniquePaths.add(path);
        continue;
      }
      String oldValue = resolvedParent.getStringValue(path);
      Status status = new Status();
      if (!fullPath.equals(oldFullXPath) || !value.equals(oldValue)) {
        if (fullPath.contains("[@casing") != oldFullXPath.contains("[@casing")) {
          // only do if parent's value is "real"
          if (resolvedParent.getSourceLocaleID(path,status).equals(parentID)) {
            throw new IllegalArgumentException("Mismatched casing: " + localeID + ", " + parentID + " For:" + CldrUtility.LINE_SEPARATOR + fullPath + CldrUtility.LINE_SEPARATOR + oldFullXPath);
          }
        }
        uniquePaths.add(path);
      }
    }
    
    // if there are no unique paths our work here is done
    if (uniquePaths.size() == 0) {
      if (DEBUG) System.out.format("LocaleID: %s is EMPTY, no changes necessary" + CldrUtility.LINE_SEPARATOR, localeID);
      return countChanges;
    }
    
    // now find all the siblings. These are all the locales that have the same parent, and are one-level down
    Set<String> siblings = lidp.set(localeID).getSiblings(factory.getAvailable());
    siblings.remove(localeID); // remove myself
    
    if (DEBUG) System.out.format("Siblings: %s" + CldrUtility.LINE_SEPARATOR, siblings);
    
    // we now have a list of siblings. 
    // Create and write a new CLDRFile that is an empty me
    
    CLDRFile newFile = SimpleFactory.makeFile(localeID);
    writeFile(newFile);
    if (DEBUG) System.out.format("%s changes in: %s" + CldrUtility.LINE_SEPARATOR, uniquePaths.size(), localeID);
    
    // now add the different paths to the copy of the parent, and write out
    
    newFile = (CLDRFile) factory.make(parentID, false).cloneAsThawed();
    //System.out.println("parent " + size(CLDRFile.make(parentID).iterator()));
    //System.out.println("clone " + size(newFile.iterator()));
    int deltaChangeCount = addPathsAndValuesFrom(newFile, uniquePaths, me, true);
    countChanges += deltaChangeCount;
    if (DEBUG) System.out.format("%s changes in: %s" + CldrUtility.LINE_SEPARATOR, deltaChangeCount, parentID);
    writeFile(newFile);
    
    //  now add the parent's values for the paths to the siblings, and write out
    for (String id : siblings) {
      newFile = (CLDRFile) factory.make(id, false).cloneAsThawed();
      if (newFile.getFullXPath("//ldml/alias",true) != null) {
        System.out.println("Skipping completely aliased file: " + id);
        continue;
      }
      deltaChangeCount = addPathsAndValuesFrom(newFile, uniquePaths, resolvedParent, false);
      countChanges += deltaChangeCount;
      if (DEBUG) System.out.format("%s changes in: %s" + CldrUtility.LINE_SEPARATOR, deltaChangeCount, id);
      writeFile(newFile);
    }
    
    return countChanges;
  }


  private int size(Iterator name) {
    int count = 0;
    while (name.hasNext()) {
      Object x = name.next();
      count++;
    }
    return count;
  }

  private int addPathsAndValuesFrom(CLDRFile toModify, Set<String> uniquePaths, CLDRFile toAddFrom, boolean override) {
    int changeCount = 0;
    XPathParts parts = new XPathParts();
    for (String path : uniquePaths) {
      String fullPath = null, value = null, oldFullXPath = null, oldValue = null;
      fullPath = toAddFrom.getFullXPath(path);
      if (fullPath == null) continue;
      value = toAddFrom.getStringValue(path);
      oldFullXPath = toModify.getFullXPath(path);
      if (!override && oldFullXPath != null) {
        continue; // don't override unless specified
      }
      oldValue = toModify.getStringValue(path);
      try {
        if (!fullPath.equals(oldFullXPath) || !value.equals(oldValue)) {
          if (oldFullXPath != null && fullPath.contains("[@casing") != oldFullXPath.contains("[@casing")) {
            throw new IllegalArgumentException("Mismatched casing: " + toAddFrom.getLocaleID() + ", " + toModify.getLocaleID());
          }
          if (override && oldFullXPath != null) {
            Map<String,String> attributes = parts.set(oldFullXPath).getAttributes(-1);
            String alt = attributes.get("alt");
            if (alt == null) {
              attributes.put("alt", "proposed-x999");
            } else if (alt.contains("proposed")){
              attributes.put("alt", alt + "-x999");
            } else {
              attributes.put("alt", alt + "-proposed-x999");
            }
            oldFullXPath = parts.toString();
            toModify.add(oldFullXPath,oldValue);
          }
          toModify.add(fullPath,value);
          ++changeCount;
        }
      } catch (RuntimeException e) {
        throw e; // for debuggin
      }
    }
    return changeCount;
  }
  
  SimpleLineComparator lineComparer = new SimpleLineComparator(
      //SimpleLineComparator.SKIP_SPACES + 
      SimpleLineComparator.TRIM +
      SimpleLineComparator.SKIP_EMPTY +
      SimpleLineComparator.SKIP_CVS_TAGS);

  private void writeFile(CLDRFile newFile) throws IOException {
    String id = newFile.getLocaleID();
    PrintWriter out = BagFormatter.openUTF8Writer(outputDirectory, id + ".xml");
    newFile.write(out);
    out.println();
    out.close();
    CldrUtility.generateBat(sourceDirectory, id + ".xml", outputDirectory, id + ".xml", lineComparer);
  }

}