package com.github.mustachejava.codes;

import com.github.mustachejava.*;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * Extending a template through in-place replacement of the overridden codes.
 * <p/>
 * User: sam
 * Date: 11/27/11
 * Time: 10:39 AM
 */
public class ExtendCode extends PartialCode {

  private final DefaultMustacheFactory mf;

  public ExtendCode(TemplateContext tc, DefaultMustacheFactory mf, Mustache codes, String name) throws MustacheException {
    super(tc, mf, codes, "<", name);
    this.mf = mf;
  }

  private Code[] replaceCodes(Code[] supercodes, Map<String, ExtendNameCode> replaceMap, Set<Code> seen) {
    Code[] newcodes = supercodes.clone();
    for (int i = 0; i < supercodes.length; i++) {
      Code code = supercodes[i];
      if (seen.add(code)) {
        if (code instanceof ExtendNameCode) {
          ExtendNameCode enc = (ExtendNameCode) code;
          ExtendNameCode extendReplaceCode = replaceMap.get(enc.getName());
          if (extendReplaceCode != null) {
            ExtendNameCode newcode = (ExtendNameCode) (newcodes[i] = (Code) extendReplaceCode.clone());
            // We need to set the appended text of the new code to that of the old code
            newcode.appended = enc.appended;
          } else {
            enc.setCodes(replaceCodes(enc.getCodes(), replaceMap, seen));
          }
        } else {
          Code[] codes = code.getCodes();
          if (codes != null) {
            code.setCodes(replaceCodes(codes, replaceMap, seen));
          }
        }
        seen.remove(code);
      }
    }
    return newcodes;
  }

  @Override
  public synchronized void init() {
    filterText();
    Map<String, ExtendNameCode> replaceMap = new HashMap<String, ExtendNameCode>();
    for (Code code : mustache.getCodes()) {
      if (code instanceof ExtendNameCode) {
        // put name codes in the map
        ExtendNameCode erc = (ExtendNameCode) code;
        replaceMap.put(erc.getName(), erc);
        erc.init();
      } else if (code instanceof WriteCode) {
        // ignore text
      } else {
        // fail on everything else
        throw new IllegalArgumentException(
                "Illegal code in extend section: " + code.getClass().getName());
      }
    }
    Mustache original = mf.compilePartial(partialName());
    partial = (Mustache) original.clone();
    Code[] supercodes = partial.getCodes();
    // recursively replace named sections with replacements
    Set<Code> seen = new HashSet<Code>();
    seen.add(partial);
    partial.setCodes(replaceCodes(supercodes, replaceMap, seen));
  }

}
