package caevo;

import java.util.ArrayList;
import java.util.List;

import org.jdom.Element;
import org.jdom.Namespace;

import caevo.util.TreeOperator;
import edu.stanford.nlp.ling.CoreAnnotations;
import edu.stanford.nlp.ling.CoreLabel;
import edu.stanford.nlp.trees.LabeledScoredTreeFactory;
import edu.stanford.nlp.trees.Tree;
import edu.stanford.nlp.trees.TreeFactory;
import edu.stanford.nlp.trees.TypedDependency;

/**
 * Represents a sentence in a document, including the following data: - tokens -
 * events as TextEvent objects - times as Timex objects
 * 
 * @author chambers
 */
public class SieveSentence {
  private SieveDocument parent;
  private int sid;
  private String sentence;
  private String parseStr;
  private Tree parseTree;
  private String depsStr;
  private List<TypedDependency> deps;
  private List<CoreLabel> tokens;
  private List<TextEvent> events; // of TextEvent objects
  private List<Timex> timexes; // of Timex objects

  public SieveSentence(SieveDocument doc, int sid, String sentence,
      String strParse, String strDeps, List<CoreLabel> tokens,
      List<TextEvent> events, List<Timex> timexes) {
    this.parent = doc;
    this.sid = sid;
    this.sentence = sentence;
    this.parseStr = strParse;
    this.depsStr = strDeps;
    this.tokens = tokens;
    this.events = events;
    this.timexes = timexes;

    if (this.events == null)
      this.events = new ArrayList<TextEvent>();
    if (this.timexes == null)
      this.timexes = new ArrayList<Timex>();
  }

  public SieveSentence(Element el, Namespace ns) {
    if (el != null) {
      String sidStr = el.getAttributeValue(InfoFile.SID_ELEM);
      if (sidStr != null)
        sid = Integer.parseInt(sidStr);
      events = new ArrayList<TextEvent>();
      timexes = new ArrayList<Timex>();
      sentence = el.getChildTextTrim(InfoFile.SENT_ELEM, ns);
      parseStr = el.getChildTextTrim(InfoFile.PARSE_ELEM, ns);
      depsStr = el.getChildTextTrim(InfoFile.DEPS_ELEM, ns);
      tokens = null;

      if (el.getChild(InfoFile.TOKENS_ELEM, ns) != null) {
        tokens = new ArrayList<CoreLabel>();
        Element alltokens = el.getChild(InfoFile.TOKENS_ELEM, ns);
        List<Element> children = alltokens.getChildren(InfoFile.TOKEN_ELEM, ns);
        for (Element child : children)
          tokens.add(stringToCoreLabel(child.getText()));
      }

      Element evel = el.getChild("events", ns);
      if (evel != null) {
        List children = evel.getChildren(TextEvent.NAME_ELEM, ns);
        for (Object obj : children)
          events.add(new TextEvent(sid, (Element) obj));
      }

      evel = el.getChild(InfoFile.TIMEXES_ELEM, ns);
      if (evel != null) {
        List children = evel.getChildren(Timex.TIMEX_ELEM, ns);
        for (Object obj : children) {
          Timex newtimex = new Timex((Element) obj);
          newtimex.setSid(sid);
          timexes.add(newtimex);
        }
      }
    }
  }

  public void addEvents(List<TextEvent> newEvents) {
    if (newEvents != null) {
      if (events == null)
        events = new ArrayList<TextEvent>();
      events.addAll(newEvents);
    }
  }

  public void addTimexes(List<Timex> newTimexes) {
    if (newTimexes != null) {
      if (timexes == null)
        timexes = new ArrayList<Timex>();

      for (Timex timex : newTimexes)
        timex.setSid(this.sid);
      timexes.addAll(newTimexes);
    }
  }

  public static SieveSentence fromXML(Element el) {
    Namespace ns = Namespace.getNamespace(SieveDocuments.INFO_NS);
    return new SieveSentence(el, ns);
  }

  public Element toXML() {
    Namespace ns = Namespace.getNamespace(SieveDocuments.INFO_NS);

    Element entry = new Element(SieveDocuments.ENTRY_ELEM, ns);
    entry.setAttribute(SieveDocuments.SID_ELEM, String.valueOf(sid));
    entry.setAttribute(SieveDocuments.FILE_ELEM, parent.getDocname());
    Element sentElem = new Element(SieveDocuments.SENT_ELEM, ns);
    sentElem.addContent(sentence);
    Element tokensElem = new Element(SieveDocuments.TOKENS_ELEM, ns);
    if (tokens != null) {
      for (CoreLabel token : tokens) {
        Element tokenElem = new Element(SieveDocuments.TOKEN_ELEM, ns);
        tokenElem.addContent(generateCoreLabelString(token));
        tokensElem.addContent(tokenElem);
      }
      // tokensElem.addContent(tokens);
    }
    Element parseElem = new Element(SieveDocuments.PARSE_ELEM, ns);
    parseElem.setText(parseStr);

    // Add the events vector
    Element eventsElem = new Element(SieveDocuments.EVENTS_ELEM, ns);
    if (events != null)
      for (TextEvent te : events)
        eventsElem.addContent(te.toElement(ns));

    // Add the timex vector
    Element timexesElem = new Element(SieveDocuments.TIMEXES_ELEM, ns);
    if (timexes != null)
      for (Timex te : timexes)
        timexesElem.addContent(te.toElement(ns));

    // Add the typed dependencies
    Element depsElem = new Element(SieveDocuments.DEPS_ELEM, ns);
    depsElem.setText(depsStr);

    entry.addContent(sentElem);
    entry.addContent(tokensElem);
    entry.addContent(parseElem);
    entry.addContent(depsElem);
    entry.addContent(eventsElem);
    entry.addContent(timexesElem);

    return entry;
  }

  /**
   * Takes the strings from a .info file and puts them into CoreLabel objects
   * that preserve the original characters. Each line in the string is one
   * label, three parts per line.
   */
  public static CoreLabel stringToCoreLabel(String text) {
    // System.out.println("stringToCoreLabel text: " + text);
    text = text.substring(1, text.length() - 1);
    String[] triple = text.split("\" \"");
    CoreLabel label = new CoreLabel();
    label.set(CoreAnnotations.BeforeAnnotation.class, triple[0]);
    label.set(CoreAnnotations.OriginalTextAnnotation.class, triple[1]);
    label.set(CoreAnnotations.AfterAnnotation.class,
        (triple.length > 2 ? triple[2] : ""));
    // System.out.println("\t" + label);

    return label;
  }

  /**
   * Takes the strings from a .info file and puts them into CoreLabel objects
   * that preserve the original characters. Each line in the string is one
   * label, three parts per line.
   */
  public static List<CoreLabel> stringToCoreLabels(String text) {
    List<CoreLabel> localtokens = new ArrayList<CoreLabel>();
    String[] parts = text.split("\n");
    for (String part : parts)
      localtokens.add(stringToCoreLabel(part));
    return localtokens;
  }

  private String generateCoreLabelString(CoreLabel cl) {
    StringBuffer buf = new StringBuffer();
    buf.append('"');
    buf.append(cl.getString(CoreAnnotations.BeforeAnnotation.class));
    buf.append("\" \"");
    buf.append(cl.getString(CoreAnnotations.OriginalTextAnnotation.class));
    buf.append("\" \"");
    buf.append(cl.getString(CoreAnnotations.AfterAnnotation.class));
    buf.append("\"");
    return buf.toString();
  }

  public Tree getParseTree() {
    if (parseTree == null) {
      TreeFactory tf = new LabeledScoredTreeFactory();
      parseTree = TreeOperator.stringToTree(parseStr, tf);
    }
    return parseTree;
  }

  public List<TypedDependency> getDeps() {
    if (deps == null)
      deps = TreeOperator.stringToDependencies(depsStr);
    return deps;
  }

  public void setParent(SieveDocument doc) {
    parent = doc;
  }

  public int sid() {
    return sid;
  }

  public String sentence() {
    return sentence;
  }

  public List<CoreLabel> tokens() {
    return tokens;
  }

  public List<TextEvent> events() {
    return events;
  }

  public List<Timex> timexes() {
    return timexes;
  }
}
