package w3c.pics.parser;

import java.util.*;

/**
 * PICS Standard Library class for a rating policy in a profile.
 **/

public class Policy implements java.io.Serializable
{
  /*
    Describes the policy.  Can be one of "AcceptIf", "RejectIf", 
    "AcceptUnless", "RejectUnless", "AcceptByURL", or "RejectByURL".
    */

  String type;

  /*
    Contains an (optional) explanation of the policy. 
    */

  String explanation;

  /*
    Points to the PolicyAttrNode in the parse tree. 
    */

  ComplexNode tree;

  /* Describes the policy node type, can be one of R_URL, A_URL, 
     R_UN, A_UN, R_IF, A_IF. */

  int nodetype = -1;
  
  /* Holds extension clauses. */

  Vector extClauses = null;
  
  static final int R_URL = 0;
  static final int A_URL = 1;
  static final int R_UN = 2;
  static final int A_UN = 3;
  static final int R_IF = 4;
  static final int A_IF = 5;

  /**
   * Creates a new, empty Policy.
   */

  public Policy() {}

  /**
   * Returns the type of this Policy.
   * @return A String containing the type of Policy.
   **/

  public String getType()
  {
    return type;
  }
  
  /**
   * Returns the explanation sub-clause for this Policy.
   * @return A String containing an explanation of the Policy.
   **/

  public String getExplanation()
  {
    return explanation;
  }


  ComplexNode getTree()
  {
    return tree;
  }

  /**
   * Returns the type of this node.
   * @param type A String describing the type of Policy this is.
   **/

  public void setType(String type)
  {
    this.type = type;
  }

  /**
   * Changes the explanation for this Policy.
   * @param explanation A String explaining the Policy.
   **/

  public void setExplanation(String explanation)
  {
    this.explanation = explanation;
  }

  void setTree(ComplexNode tree)
  {
    this.tree = tree;
    // System.out.println("Policy.setTree");
    Node firstchild = tree.jjtGetChild(0);
    // System.out.println("firstchild:\n"+firstchild);
    if (firstchild instanceof PICSA_IF_Node) {
      this.nodetype = A_IF;
      // System.out.println("A_IF Node");
    }
    else if (firstchild instanceof PICSR_IF_Node) {
      this.nodetype = R_IF;
      // System.out.println("R_IF Node");
    }
    else if (firstchild instanceof PICSA_UN_Node) {
      this.nodetype = A_UN;
      // System.out.println("A_UN_Node");
    }
    else if (firstchild instanceof PICSR_UN_Node) {
      this.nodetype = R_UN;
      // System.out.println("R_UN Node");
    }
    else if (firstchild instanceof PICSA_URL_Node) {
      this.nodetype = A_URL;
      // System.out.println("A_URL_Node");
    }
    else if (firstchild instanceof PICSR_URL_Node) {
      this.nodetype = R_URL;
      // System.out.println("R_URL Node");
    }
  }

  private static int USE_DQUOTE = 0;
  private static int USE_SQUOTE = 1;

  int checkString(String input) {
    if (input.indexOf("\"")!=-1)
      return USE_SQUOTE;
    else
      return USE_DQUOTE;
  }

  /**
   * Returns the PICSRules 1.1 representation of this Policy clause.
   * @return A String containing the Policy in the PICSRules format.
   **/

  public String toString()
  {
    String result = new String();
    result += "Policy (" + type + " " + tree.getText();
    if (explanation!=null) {
      if (checkString(explanation)==USE_DQUOTE)
	result += " Explanation \"" + explanation +"\"";
      else
	result += " Explanation '" + explanation +"'";
    }
    if (extClauses!=null) {
      Enumeration exts = extClauses.elements();
      while (exts.hasMoreElements()) {
	result += " "+exts.nextElement();
      }
    }
    result +=")\n";
    return result;
  }

  /**
   * Generates a SQL query for labels matching the conditions specified in 
   * the current Policy
   * @param prof The Profile that this Policy came from
   * @param serviceURL The service URL for the table we are applying the SQL 
   * query to
   * @param serviceTableName The table database name
   * @param categories Single-value categories in this table
   * @param multiValueCategories Multi-value categories in the table
   * @return A String containing the SQL statement
   **/

  public String writeSQLQuery(Profile prof,
			      String serviceURL,
			      String serviceTableName,
			      Vector categories, 
			      Vector multiValueCategories,
			      boolean cont)
  {
    boolean andFlag, notFlag;
    String res = new String();
    switch (nodetype)
      {
      case R_URL:
      case A_URL:
	URLEvalNode urlNode = (URLEvalNode)tree.jjtGetChild(0);
	int urlCount = urlNode.jjtGetNumChildren();
	if (nodetype == R_URL)
	  res = res.concat("(NOT ");
	PICSFull_URL_Node fullURLNode = 
	  (PICSFull_URL_Node)urlNode.jjtGetChild(0);
	res += "forURL = \"" + fullURLNode.getText() + "\"";
	for (int i = 1; i < urlCount; i++)
	  {
	    fullURLNode = (PICSFull_URL_Node)urlNode.jjtGetChild(i);
	    res = res.concat(" OR ");
	    res += "forURL = \"" + fullURLNode.getText() + "\"";
	  }
	if (nodetype == R_URL)
	  res = res.concat(")");
	if (cont)
	  if (nodetype == R_URL)
	    res = res.concat(" AND (");
	  else
	    res = res.concat(" OR (");	
	break;

      default:
	switch (nodetype)
	  {
	  case R_IF: andFlag = true; notFlag = true; break;
	  case A_IF: andFlag = false; notFlag = false; break;
	  case R_UN: andFlag = true; notFlag = false; break;
	  case A_UN: andFlag = false; notFlag = true; break;
	  default:   
	    andFlag = true; 
	    notFlag = true;
	  }
	if (notFlag)
	  res += "(NOT (";
	res += 
	  writeExprQuery(((EvalNode)tree.jjtGetChild(0).
			  jjtGetChild(0).jjtGetChild(0)), prof, serviceURL, 
			 serviceTableName, categories, multiValueCategories);
	if (notFlag)
	  res += "))";
	if (cont)
	  if (andFlag)
	    res += " AND (";
	  else
	    res += " OR (";
	break;
      }
    return res;
  }
  
  private String writeExprQuery(EvalNode node,
				  Profile prof,
				  String serviceURL,
				  String serviceTableName,
				  Vector categories, 
				  Vector multiValueCategories)
  {
    String res = new String();
    if (node instanceof PICSS_Expr)
      {
	int num = node.jjtGetNumChildren();
	String service = ((PICSServiceNode)node.jjtGetChild(0)).getText();
	String profileService = 
	  prof.getFullServiceName(((PICSServiceNode)node.jjtGetChild(0)).
				  getText());
	if (!profileService.equals(serviceURL))
	  {
	    res += "forURL IS NULL";
	    return res;
	  }
	if (num == 1)
	  res += "forURL IS NOT NULL";
	else if (num == 2)
	  {
	    String category = 
	      ((PICSCategoryNode)node.jjtGetChild(1)).getText();
	    if (categories.contains(category))
	      res += "forURL IS NOT NULL";
	    else if (multiValueCategories.contains(category))
	      res += "forURL IN (SELECT * FROM " + serviceTableName + "_" + 
		category + ")";
	    else
	      res += "forURL IS NULL";
	  }
	else if (num == 4)
	  {
	    String category = 
	      ((PICSCategoryNode)node.jjtGetChild(1)).getText();
	    String operator = 
	      ((PICSOperatorNode)node.jjtGetChild(2)).getText();
	    String constant = 
	      ((PICSConstantNode)node.jjtGetChild(3)).getText();
	    if (categories.contains(category))
	      res += category + "_ " + operator + " " + constant;
	    else if (multiValueCategories.contains(category))
	      {
		res += "forURL IN (SELECT * FROM " + serviceTableName + "_" + 
		  category + " WHERE ";
		if (operator.equals(">"))
		  res += "beta > " + constant + ")";
		else if (operator.equals("<"))
		  res += "alpha < " + constant + ")";
		else if (operator.equals("="))
		  res += "alpha <= " + constant + " AND beta >= " + constant + 
		    ")";
		else if (operator.equals(">="))
		  res += "beta >= " + constant + ")";
		else if (operator.equals("<="))
		  res += "beta <= " + constant + ")";
	      }
	    else
	      res += "forURL IS NULL";
	  }
	else
	  res += "forURL IS NULL";
      }
    else if ((node instanceof PICSOr_Expr) || (node instanceof PICSAnd_Expr))
      {
	boolean orFlag = false;
	if (node instanceof PICSOr_Expr)
	  orFlag = true;
	int childCount = ((PICSOr_Expr)node).jjtGetNumChildren();
	System.out.println("childCount = " + childCount);
	if (childCount > 1)
	  res += "(";
	res += "(";
	res += writeExprQuery((EvalNode)node.jjtGetChild(0).jjtGetChild(0), 
			      prof, 
			      serviceURL, 
			      serviceTableName, 
			      categories, 
			      multiValueCategories);	
	res += ")";
	for (int i = 2 ; i < childCount; i += 2)
	  {
	    if (orFlag)
	      res += " OR (";
	    else
	      res += " AND (";
	    res += 
	      writeExprQuery((EvalNode)node.jjtGetChild(i).jjtGetChild(0), 
				  prof, 
				  serviceURL, 
				  serviceTableName, 
				  categories, 
				  multiValueCategories);
	    res += ")";
	  }
	if (childCount > 1)
	  res += ")";
      }
    else if (node instanceof PICSDegen_Expr)
      res += "forURL IS NOT NULL";
    else
	res += node.getClass().getName(); //DEBUG
    return res;
  }

  /**
   * Evaluates a group of labels against this Policy.
   * @param URL A String containing the URL that the labels are rating.
   * @param labellist Array of Label objects to be evaluated against
   * @param silist Table of serviceinfo information that the parent Profile 
   * object owns.
   * @return A boolean indicating if the labels are valid.
   **/

  public boolean isValid(String URL, Vector labellist, Hashtable silist) {
    switch (nodetype) {
    case R_URL:
    case A_URL:
      URLEvalNode urlnode = (URLEvalNode)tree.jjtGetChild(0);
      return urlnode.isValid(URL);
    case R_UN:
    case A_UN:
    case R_IF:
    case A_IF:
      EvalNode pnode = (EvalNode)((Node)tree.jjtGetChild(0)).jjtGetChild(0);
      boolean retval = pnode.isValid(labellist, silist);
      return retval;
    default:
      return false;
    }
  }

  /**
   * Adds an extension clause to the Policy
   * @param ext The extension clause to add
   **/

  public void addExtClause(String ext) {
    if (extClauses==null) {
      extClauses = new Vector();
    }
    extClauses.addElement(ext);
  }

}



