package com.github.mustachejava;

import com.github.mustachejava.codes.DefaultCode;
import com.github.mustachejava.codes.DefaultMustache;
import com.github.mustachejava.codes.ExtendCode;
import com.github.mustachejava.codes.ExtendNameCode;
import com.github.mustachejava.codes.IterableCode;
import com.github.mustachejava.codes.NotIterableCode;
import com.github.mustachejava.codes.PartialCode;
import com.github.mustachejava.codes.ValueCode;
import com.github.mustachejava.codes.WriteCode;
import com.github.mustachejava.util.Node;

import java.io.Writer;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;

/**
 * The default implementation that builds up Code lists
 */
public class DefaultMustacheVisitor implements MustacheVisitor {
  protected static Logger logger = Logger.getLogger(DefaultMustacheVisitor.class.getSimpleName());

  private static final Code EOF = new DefaultCode() {
    @Override
    public Node invert(Node node, String text, AtomicInteger position) {
      return text.length() == position.get() ? node : null;
    }
  };

  protected final List<Code> list = new LinkedList<Code>();
  private final Map<String, PragmaHandler> handlers = new HashMap<String, PragmaHandler>() {{
    put("implicit-iterator", new PragmaHandler() {
      @Override
      public Code handle(TemplateContext tc, String pragma, String args) {
        return new DefaultCode() {
          @Override
          public Writer execute(Writer writer, Object[] scopes) {
            return super.execute(writer, scopes);
          }
        };
      }
    });
  }};

  protected DefaultMustacheFactory df;

  public DefaultMustacheVisitor(DefaultMustacheFactory df) {
    this.df = df;
  }

  public void addPragmaHandler(String pragma, PragmaHandler handler) {
    handlers.put(pragma.toLowerCase(), handler);
  }

  @Override
  public Mustache mustache(TemplateContext templateContext) {
    return new DefaultMustache(templateContext, df, list.toArray(new Code[list.size()]), templateContext.file());
  }

  @Override
  public void iterable(TemplateContext templateContext, String variable, Mustache mustache) {
    list.add(new IterableCode(templateContext, df, mustache, variable));
  }

  @Override
  public void notIterable(TemplateContext templateContext, String variable, Mustache mustache) {
    list.add(new NotIterableCode(templateContext, df, mustache, variable));
  }

  @Override
  public void name(TemplateContext templateContext, String variable, Mustache mustache) {
    list.add(new ExtendNameCode(templateContext, df, mustache, variable));
  }

  @Override
  public void partial(TemplateContext tc, final String variable) {
    TemplateContext partialTC = new TemplateContext("{{", "}}", tc.file(), tc.line(), tc.startOfLine());
    list.add(new PartialCode(partialTC, df, variable));
  }

  @Override
  public void value(TemplateContext tc, final String variable, boolean encoded) {
    list.add(new ValueCode(tc, df, variable, encoded));
  }

  @Override
  public void write(TemplateContext tc, final String text) {
    if (text.length() > 0) {
      int size = list.size();
      if (size > 0) {
        Code code = list.get(size - 1);
        code.append(text);
      } else {
        list.add(new WriteCode(tc, df, text));
      }
    }
  }

  @Override
  public void pragma(TemplateContext tc, String pragma, String args) {
    PragmaHandler pragmaHandler = handlers.get(pragma.toLowerCase());
    if (pragmaHandler == null) {
      // By default, warn that no pragmas are understood
      logger.warning("Unimplemented pragma: " + pragma);
    } else {
      Code code = pragmaHandler.handle(tc, pragma, args);
      if (code != null) {
        list.add(code);
      }
    }
  }

  @Override
  public void eof(TemplateContext templateContext) {
    list.add(EOF);
  }

  @Override
  public void extend(TemplateContext templateContext, String variable, Mustache mustache) {
    list.add(new ExtendCode(templateContext, df, mustache, variable));
  }

}
