package caevo.sieves;

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

import caevo.SieveDocument;
import caevo.SieveDocuments;
import caevo.Timex;
import caevo.tlink.TLink;
import caevo.tlink.TimeTimeLink;
import caevo.util.Pair;

/**
 * TimeTimeSieve orders date and time expressions by their Timex normalized
 * values - Currently does not consider durations or sets - Durations require
 * start and end times that we currently don't have - Only orders FUTURE_REF
 * with times in the past, and PAST_REF with times in the future - Ignores
 * PRESENT_REF due to inconsistent annotations of "now" - Currently, all
 * imprecision is due to incorrect annotations - Also orders all times with
 * document creation time
 * 
 * Chambers note: make sure DCT times are 24-hour periods as that is the
 * annotation standard This sieve uses what it gets, so change to 24-hours
 * before calling it.
 * 
 * Current results on various data sets: - Train: 0.920 160 of 174 - Dev: 0.882
 * 15 of 17
 * 
 * @author Bill McDowell
 */
public class TimeTimeSieve implements Sieve {

  public List<TLink> annotate(SieveDocument doc, List<TLink> currentTLinks) {
    List<TLink> proposed = new ArrayList<TLink>();

    List<TLink> sentencePairLinks = annotateBySentencePair(doc);
    List<TLink> creationTimeLinks = annotateByCreationTime(doc);

    if (sentencePairLinks != null)
      proposed.addAll(sentencePairLinks);
    if (creationTimeLinks != null)
      proposed.addAll(creationTimeLinks);

    return proposed;
  }

  public List<TLink> annotateBySentencePair(SieveDocument doc) {
    List<List<Timex>> sentenceTimexes = doc.getTimexesBySentence();
    List<TLink> proposed = new ArrayList<TLink>();
    Timex creationTime = (doc.getDocstamp() == null
        || doc.getDocstamp().isEmpty()) ? null : doc.getDocstamp().get(0);

    for (int s = 0; s < sentenceTimexes.size(); s++) {
      for (int t1 = 0; t1 < sentenceTimexes.get(s).size(); t1++) {
        for (int t2 = t1 + 1; t2 < sentenceTimexes.get(s).size(); t2++) {
          TLink link = this.orderTimexes(sentenceTimexes.get(s).get(t1),
              sentenceTimexes.get(s).get(t2), creationTime);
          if (link != null)
            proposed.add(link);
        }

        if (s + 1 < sentenceTimexes.size()) {
          for (int t2 = 0; t2 < sentenceTimexes.get(s + 1).size(); t2++) {
            TLink link = this.orderTimexes(sentenceTimexes.get(s).get(t1),
                sentenceTimexes.get(s + 1).get(t2), creationTime);
            if (link != null)
              proposed.add(link);
          }
        }
      }
    }

    return proposed;
  }

  public List<TLink> annotateByCreationTime(SieveDocument doc) {
    if (doc.getDocstamp() == null || doc.getDocstamp().isEmpty())
      return null;

    Timex creationTime = doc.getDocstamp().get(0);

    List<List<Timex>> allTimexes = doc.getTimexesBySentence();
    List<TLink> proposed = new ArrayList<TLink>();

    for (List<Timex> timexes : allTimexes) {
      for (Timex timex : timexes) {
        TLink link = this.orderTimexes(creationTime, timex, creationTime);
        if (link != null)
          proposed.add(link);
      }
    }

    return proposed;
  }

  private TLink orderTimexes(Timex t1, Timex t2, Timex ct) {
    if (t1 == null || t2 == null)
      return null;
    if (t1.getType() != Timex.Type.DATE && t1.getType() != Timex.Type.TIME)
      return null;
    if (t2.getType() != Timex.Type.DATE && t2.getType() != Timex.Type.TIME)
      return null;

    // System.out.println("Checking time-time: " + t1 + "\t" + t2);

    if (t1.isReference() || t2.isReference()) {
      if (ct == null)
        return null;

      int t1Ct = 0, t2Ct = 0;

      if (t1.isReference()) {
        if (t1.isFutureReference())
          t1Ct = 1;
        else if (t1.isPastReference())
          t1Ct = -1;
        else
          return null;// t1Ct = 0;
      } else {
        TLink t1CtLink = orderTimexes(t1, ct, null);
        if (t1CtLink == null || t1CtLink.getRelation() == TLink.Type.VAGUE)
          return null;
        else if (t1CtLink.getRelation() == TLink.Type.BEFORE)
          t1Ct = -1;
        else if (t1CtLink.getRelation() == TLink.Type.AFTER)
          t1Ct = 1;
        else
          return null;// t1Ct = 0;
      }

      if (t2.isReference()) {
        if (t2.isFutureReference())
          t2Ct = 1;
        else if (t2.isPastReference())
          t2Ct = -1;
        else
          return null;// t2Ct = 0;
      } else {
        TLink t2CtLink = orderTimexes(t2, ct, null);
        if (t2CtLink == null || t2CtLink.getRelation() == TLink.Type.VAGUE)
          return null;
        else if (t2CtLink.getRelation() == TLink.Type.BEFORE)
          t2Ct = -1;
        else if (t2CtLink.getRelation() == TLink.Type.AFTER)
          t2Ct = 1;
        else
          return null;// t2Ct = 0;
      }

      if (t1Ct < t2Ct)
        return new TimeTimeLink(t1.getTid(), t2.getTid(), TLink.Type.BEFORE);
      else if (t2Ct < t1Ct)
        return new TimeTimeLink(t1.getTid(), t2.getTid(), TLink.Type.AFTER);
      else
        return null;
    }

    Pair<Calendar, Calendar> interval1 = null;
    Pair<Calendar, Calendar> interval2 = null;

    interval1 = t1.getRange(ct);
    interval2 = t2.getRange(ct);

    // System.out.println("\tintervals: " + interval1 + "\t" + interval2);

    if (interval1 == null || interval2 == null)
      return null;

    int startStart = interval1.first().compareTo(interval2.first());
    int startEnd = interval1.first().compareTo(interval2.second());
    int endStart = interval1.second().compareTo(interval2.first());
    int endEnd = interval1.second().compareTo(interval2.second());

    // System.out.println(startStart + " " + t1.getTid() + " " + t2.getTid() + "
    // "+ interval1.first().getTime().toString() + " " +
    // interval1.first().getTimeInMillis() + " " +
    // interval2.first().getTime().toString() +
    // interval2.first().getTimeInMillis());
    TLink.Type lType = TLink.Type.VAGUE;

    if (startStart == 0 && endEnd == 0)
      lType = TLink.Type.SIMULTANEOUS;
    else if (endStart <= 0)
      lType = TLink.Type.BEFORE;
    else if (startEnd >= 0)
      lType = TLink.Type.AFTER;
    else if (startStart < 0 && endEnd > 0)
      lType = TLink.Type.INCLUDES;
    else if (startStart > 0 && endEnd < 0)
      lType = TLink.Type.IS_INCLUDED;
    else if (startStart > 0 && startEnd < 0 && endEnd > 0)
      lType = TLink.Type.VAGUE;
    else if (startStart < 0 && endStart > 0 && endEnd < 0)
      lType = TLink.Type.VAGUE;
    else
      return null;

    // System.out.println(t1.getValue() + "\t" + t2.getValue() + "\t" + lType);

    return new TimeTimeLink(t1.getTid(), t2.getTid(), lType);
  }

  /**
   * No training. Just rule-based.
   */
  public void train(SieveDocuments trainingInfo) {
    // no training
  }

}
