package com.jayantkrish.jklol.cvsm.ccg;

import java.util.List;

import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.jayantkrish.jklol.ccg.SyntacticCategory;
import com.jayantkrish.jklol.ccg.lambda.ConstantExpression;
import com.jayantkrish.jklol.ccg.lambda.Expression;
import com.jayantkrish.jklol.ccg.lambda.ExpressionParser;
import com.jayantkrish.jklol.util.CsvParser;

public class SemTypePattern implements CategoryPattern {

  private static final long serialVersionUID = 1L;
  
  private final SyntacticCategory patternCategory;
  private final Expression template;
  
  private static final String WORD_SEP = "_";
  private static final String WORD_PATTERN = "<word>";

  public SemTypePattern(SyntacticCategory patternCategory, Expression template) {
    this.patternCategory = Preconditions.checkNotNull(patternCategory);
    this.template = Preconditions.checkNotNull(template);
  }

  public static SemTypePattern parseFrom(String string) {
    String[] parts = new CsvParser(',', '"', CsvParser.NULL_ESCAPE).parseLine(string);
    Preconditions.checkArgument(parts.length == 2);
    
    return new SemTypePattern(SyntacticCategory.parseFrom(parts[0]),
        ExpressionParser.lambdaCalculus().parseSingleExpression(parts[1]));
  }

  @Override
  public boolean matches(List<String> words, SyntacticCategory category) {
    return hasSameSemanticType(patternCategory, category, true);
  }

  public static boolean hasSameSemanticType(SyntacticCategory pattern,
      SyntacticCategory target, boolean useDirectionality) {
    if (pattern.isAtomic()) {
      if (target.isAtomic()) {
        return true;
      } else {
        return false;
      }
    } else {
      if (target.isAtomic()) {
        return false; 
      } else {
        return (!useDirectionality || pattern.getDirection().equals(target.getDirection())) && 
            hasSameSemanticType(pattern.getArgument(), target.getArgument(), useDirectionality) &&
            hasSameSemanticType(pattern.getReturn(), target.getReturn(), useDirectionality);
      }
    }
  }

  @Override
  public Expression getLogicalForm(List<String> words, SyntacticCategory category) {
    String parameterName = Joiner.on(WORD_SEP).join(words);
    Expression result = template;
    for (ConstantExpression expression : template.getFreeVariables()) {
      String name = expression.getName();
      String newName = name;
      if (name.contains(WORD_PATTERN)) {
        newName = name.replace(WORD_PATTERN, parameterName);
      }
      // TODO: possibly include part of speech tags, etc.
      if (!newName.equals(name)) {
        result = result.renameVariable(expression, new ConstantExpression(newName));
      }
    }

    return result;
  }
}

@@ -609,16 +609,61 @@
 mplate;%0A
+  private final boolean matchOnSemanticType;%0A
   %0A  pri
@@ -837,16 +837,51 @@
 template
+,%0A      boolean matchOnSemanticType
 ) %7B%0A    
@@ -1002,24 +1002,76 @@
 (template);%0A
+    this.matchOnSemanticType = matchOnSemanticType;%0A
   %7D%0A%0A  publi
@@ -1260,9 +1260,9 @@
  == 
-2
+3
 );%0A 
@@ -1407,16 +1407,46 @@
 arts%5B1%5D)
+,%0A        parts%5B3%5D.equals(%22T%22)
 );%0A  %7D%0A%0A
@@ -1528,24 +1528,57 @@
 category) %7B%0A
+    if (matchOnSemanticType) %7B%0A  
     return h
@@ -1630,16 +1630,91 @@
  true);%0A
+    %7D else %7B%0A      return patternCategory.isUnifiableWith(category);%0A    %7D%0A
   %7D%0A%0A  p

generalizing the syntactic pattern matchers