package jayhorn.solver.princess;

import ap.parser.IAtom;
import ap.parser.ITerm;
import ap.terfor.preds.Predicate;
import jayhorn.solver.ProverExpr;
import jayhorn.solver.ProverFun;
import jayhorn.solver.ProverTupleExpr;
import jayhorn.solver.ProverType;
import scala.collection.mutable.ArrayBuffer;

class PredicateFun implements ProverFun {

	private final Predicate pred;
        private final ProverType[] argTypes;

	PredicateFun(Predicate pred, ProverType[] argTypes) {
		this.pred = pred;
                this.argTypes = argTypes;
	}

	public ProverExpr mkExpr(ProverExpr[] args) {
            try {
            checkArgTypes(argTypes, args);
            } catch(RuntimeException e) {
              System.err.println("Warning: " + e);
              e.printStackTrace();
            }

            ProverExpr[] flatArgs = ProverTupleExpr.flatten(args);
            
            final ArrayBuffer<ITerm> argsBuf = new ArrayBuffer<ITerm>();
            for (int i = 0; i < flatArgs.length; ++i) {
                argsBuf.$plus$eq(((PrincessProverExpr)flatArgs[i]).toTerm());
            }

            return new FormulaExpr(new IAtom(pred, argsBuf.toSeq()));
	}

    protected static void checkArgTypes(ProverType[] argTypes,
                                        ProverExpr[] args) {
      if (argTypes == null)
        return;
      if (argTypes.length != args.length)
        throw new RuntimeException("Wrong number of arguments: expected " +
                                   argTypes.length + " but got " +
                                   args.length);
      for (int i = 0; i < argTypes.length; ++i)
        if (!argTypes[i].equals(args[i].getType()))
          throw new RuntimeException("Wrong argument type: expected " +
                                     argTypes[i] + " but got " +
                                     args[i] + " of type " +
                                     args[i].getType());
    }

    public String toString() {
        return pred.toString();
    }

    public String toSMTLIBDeclaration() {
        StringBuffer res = new StringBuffer();
        res.append("(declare-fun " + pred.name() + "(");
        String sep = "";
        for (int i = 0; i < pred.arity(); ++i) {
            res.append(sep);
            res.append("Int");
            sep = " ";
        }
        res.append(") Bool)");
        return res.toString();
    }

    public int hashCode() {
        return pred.hashCode() + 13;
    }
    
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        PredicateFun other = (PredicateFun) obj;
        if (pred == null) {
            if (other.pred != null)
                return false;
        } else if (!pred.equals(other.pred))
            return false;
        return true;
    }

}
