/*
 * CommandChecker.java
 *
 * Created on 18 de Junho de 2005, 14:48
 *
 * To change this template, choose Tools | Options and locate the template under
 * the Source Creation and Management node. Right-click the template and choose
 * Open. You can then make changes to the template in the Source Editor.
 */

package net.sourceforge.czt.typecheck.circus;

import java.util.ArrayList;
import net.sourceforge.czt.circustools.ast.ActionSignature;
import net.sourceforge.czt.typecheck.circus.impl.ActionInfo;
import static net.sourceforge.czt.typecheck.circus.util.GlobalDefs.*;

import java.util.List;
import net.sourceforge.czt.z.ast.*;
import net.sourceforge.czt.circus.ast.*;
import net.sourceforge.czt.circus.visitor.*;

/**
 *
 * @author Manuela
 */
public class CommandChecker
  extends Checker
  implements CircusCommandVisitor,
             AssignmentCommandVisitor,
             SpecStmtCommandVisitor,
             IfGuardedCommandVisitor,
             VarDeclCommandVisitor,
             ParamCommandVisitor
{
  
  //a Z decl checker
  protected net.sourceforge.czt.typecheck.z.DeclChecker zDeclChecker_;

  /** Creates a new instance of CommandChecker */
  public CommandChecker(TypeChecker typeChecker)
  {
    super(typeChecker);
    zDeclChecker_ =
      new net.sourceforge.czt.typecheck.z.DeclChecker(typeChecker);
  }
  
  // ParCommand ::= Command
  public Object visitCircusCommand(CircusCommand term)
  {
    return factory().createActionSignature();
  }

  // Command ::= N+ := Expression+
  //ok - verificado em 15/09/2005 ās 15:15
  public Object visitAssignmentCommand(AssignmentCommand term)
  {
    List<RefName> vars = term.getLeftVars();
    List<Expr> exprs = term.getExprs();

    if(vars.size() != exprs.size()) {
      Object [] params = {currentAction().getWord(), currentProcess().getWord()};
      error(term, ErrorMessage.DIFF_NUM_ASSIG_COMMAND_ERROR, params);
    }
    else {
      List<String> leftVars = new ArrayList<String>();
      int i = 0;

      for(RefName leftVar : vars) {
        Expr rigthExpr = exprs.get(i);
        if(!leftVars.contains(leftVar.getWord())) {
          leftVars.add(leftVar.getWord());
          if(!isLocalVar(leftVar)) {
            Object [] params = {currentAction().getWord(), currentProcess().getWord(), leftVar.getWord()};
            error(term, ErrorMessage.IS_NOT_LOCAL_VAR_NAME_IN_ASSIG_COMMAND, params);
            break;
          }
          Type varType = typeEnv().getType(leftVar);
          Type exprType = (Type)rigthExpr.accept(exprChecker());
          if (unify(unwrapType(varType), unwrapType(exprType)) != SUCC) {
            Object [] params = {currentAction().getWord(), currentProcess().getWord(), varType, exprType, i+1};
            error(term, ErrorMessage.ASSIG_COMMAND_ERROR, params);
            break;
          }   

        } else {
            Object [] params = {currentAction().getWord(), currentProcess().getWord(), leftVar.getWord()};
            error(term, ErrorMessage.DUPLICATE_VAR_NAME_IN_ASSIG_COMMAND, params);
            break;
        }
        i++;
      }
    }
        
    ActionSignature actionSignature = factory().createActionSignature();
    addActionAnn(term, actionSignature);

    return actionSignature;
  }

  // Command ::= N+ : [Predicate, Predicate]
  // Command ::= [Predicate]
  // Command ::= {Predicate}
  //ok - verificado em 15/09/2005 ās 15:17
  public Object visitSpecStmtCommand(SpecStmtCommand term)
  {
    List<RefName> frameVars = term.getFrame();
    Pred pre = term.getPre();
    Pred post = term.getPost();
    
    List<String> frame = new ArrayList<String>();
    
    for(RefName frameVar : frameVars) {
      if(!frame.contains(frameVar.getWord())) {
        frame.add(frameVar.getWord());
        if(!isLocalVar(frameVar)) {
          Object [] params = {currentAction().getWord(), currentProcess().getWord(), frameVar.getWord()};
          error(term, ErrorMessage.IS_NOT_LOCAL_VAR_NAME_IN_SPEC_COMMAND, params);
          break;
        }
      } else {
          Object [] params = {currentAction().getWord(), currentProcess().getWord(), frameVar.getWord()};
          error(term, ErrorMessage.DUPLICATE_VAR_NAME_IN_FRAME_OF_SPEC_COMMAND, params);
          break;
      }
    }
    pre.accept(predChecker());
    post.accept(predChecker());
    
    ActionSignature actionSignature = factory().createActionSignature();
    addActionAnn(term, actionSignature);
    
    return actionSignature;
  }

  // Command ::= if GuardedActions fi
  //ok - verificado em 15/09/2005 ās 15:35
  public Object visitIfGuardedCommand(IfGuardedCommand term)
  {
    ActionSignature actionSignature = factory().createActionSignature();
    List<GuardedAction> gActions = term.getGuardedAction();
    
    for(GuardedAction gAction : gActions) {
      Pred pred = gAction.getPred();
      CircusAction action = gAction.getCircusAction();
      pred.accept(predChecker());
      ActionSignature sig = (ActionSignature)action.accept(actionChecker());
      ActionSignature sigTemp = cloneActionSignature(sig);
      actionSignature = joinActionSignature(actionSignature, sigTemp);
    }
    
    addActionAnn(term, actionSignature);

    return actionSignature;
  }

  // Command ::= var Declaration @ Action
  //ok - vericado em 15/09/2005 ās 16:04
  public Object visitVarDeclCommand(VarDeclCommand term)
  {
    List<NameTypePair> allPairs = new ArrayList<NameTypePair>();
    
    List<Decl> decls = term.getDeclarations();
    CircusAction action = term.getCircusAction();
    
    //<<15/06/2006>>
    localCircTypeEnv().enterScopeLocalVars();
    //
    typeEnv().enterScope();
  
    List paramsError = new ArrayList<Object>();
    paramsError.add(currentAction().getWord());
    paramsError.add(currentProcess().getWord());
    for(Decl d : decls){
      if (!(d instanceof VarDecl))
          throw new UnsupportedOperationException("Variable declaration command accepts only VarDecl!");
      VarDecl decl = (VarDecl)d;
      List<NameTypePair> pairs = (List<NameTypePair>)decl.accept(declChecker());
      allPairs = checkDecls(allPairs, pairs, term, ErrorMessage.REDECLARED_PARAM_IN_PROCESS, paramsError);
    }
    addVars(allPairs);
    //<<15/06/2006>>
    localCircTypeEnv().addLocalVars(allPairs);
    //
/*    
    if(currentAction() != null) {
      ActionInfo actionInfo = localCircTypeEnv().getActionInfo(currentAction());
      actionInfo.getLocalVars().addAll(allPairs);
    }
*/
    ActionSignature actionSig = (ActionSignature)action.accept(actionChecker());
    
    typeEnv().exitScope();
    // <<15/06/2006>>
    localCircTypeEnv().exitScopeLocalVars();
    //
    ActionSignature actionSignature = cloneActionSignature(actionSig);
    actionSignature.setLocalVarsSignature(factory().createSignature(allPairs));

    addActionAnn(term, actionSignature);
    
    return actionSignature;
  }

  // ParCommand ::= (QualifiedDeclaration @ Command)
  // Falta testar!!
  public Object visitParamCommand(ParamCommand term)
  {
    List<NameTypePair> allPairs = new ArrayList<NameTypePair>();
    
    List<QualifiedDecl> decls = term.getQualifiedDecl();
    CircusCommand command = term.getCircusCommand();

    typeEnv().enterScope();
    //<<15/06/2006>>
    localCircTypeEnv().enterScopeLocalVars();
    //
    
    List paramsError = new ArrayList<Object>();
    paramsError.add(currentAction().getWord());
    paramsError.add(currentProcess().getWord());
    for(QualifiedDecl qDecl : decls) {
      List<NameTypePair> pairs = (List<NameTypePair>)qDecl.accept(declChecker());
      allPairs = checkDecls(allPairs, pairs, term, ErrorMessage.REDECLARED_PARAM_IN_PARCOMMAND, paramsError);
    }
    addVars(allPairs);
    //<<15/06/2006>>
    localCircTypeEnv().addLocalVars(allPairs);
    //
    
    ActionSignature actionSig = (ActionSignature)command.accept(commandChecker());
    
    //<<15/06/2006>>
    localCircTypeEnv().exitScopeLocalVars();
    //
    typeEnv().exitScope();
    
    ActionSignature actTemp = cloneActionSignature(actionSig);
    actTemp.setLocalVarsSignature(factory().createSignature(allPairs));
    
    ActionSignature actionSignature = factory().createActionSignature();
    actionSignature = joinActionSignature(actionSig, actTemp);    
    
    addActionAnn(term, actionSignature);
    
    return actionSignature;
  }

}
