package TP2.asd;

import TP2.asd.Interface.*;
import TP2.asd.Program.*;

public class PrettyprinterVisitor implements    ProgramVisitor<String,String>,
                                                FunctionVisitor<String,String>,
                                                DeclVisitor<String,String>,
                                                InstrVisitor<String,String>,
                                                ExprVisitor<String,String>,
                                                TypeVisitor<String,String>
                                                {

    static String INDENT = "   ";

    //PROGRAM

    @Override
    public String visitProgram(ProgramImp prog, String indent) {
        String str ="";
        for(int i = 0; i<prog.fonctions().size(); i++){
            str += prog.fonctions().get(i).accept(this,INDENT);
            if(i<prog.fonctions().size()-1) str += "\n";
        }
        return str;
    }

    //PROTOTYPE
    @Override
    public String visitPrototype(PrototypeImp proto, String indent){
        String str= indent + "PROTO "+proto.type().accept(this, "")+ " "+ proto.nom() + "(";
        for(int i=0; i<proto.params().size();i++){
            str+=proto.params().get(i).accept(this, "");
            if((i<proto.params().size()-1)){
                str+=", ";
            }
        }
        return str+")";
    }

    //FUNCTION
    @Override
    public String visitFunction(FunctionImp fun, String indent) {
        String str = indent+"FUNC " + fun.type().accept(this,"")+ " " + fun.nom() +"(";
        for(int i=0; i<fun.params().size();i++){
            str+=fun.params().get(i).accept(this, "");
            if((i<fun.params().size()-1)){
                str+=", ";
            }
        }
        str+= ") ";
        str += fun.instruction().accept(this,indent)+"\n";
        return str;
    }

    //DELCARATION
    @Override
    public String visitDeclaration(DeclarationImp instr, String indent) {
        String str = indent +instr.t().accept(this,"") + " ";
            for(int i = 0; i<instr.s().size();i++){
                str += instr.s().get(i).accept(this, indent);
                if(i<instr.s().size()-1) str += ",";
            }
            return str;
    }

    @Override 
    public String visitVarDecl(VarDeclImp instr, String indent){
        String str = "";
        if(instr.size()==null){
            str+= instr.nom();
        }else{
            str+= instr.nom()+ "["+instr.size().accept(this, indent)+"]";
        }
        return str;
    }
    //INSTRUCTION 
    @Override
    public String visitReturn(Return_instrImp instr, String indent) {
        return indent+"RETURN " + instr.e().accept(this,"");
    }

    @Override
    public String visitBloc(BlocImp instr, String indent) {
        String str = "{\n";
            for(int i = 0; i<instr.instrs().size(); i++){
                str+=instr.instrs().get(i).accept(this, indent+INDENT)+"\n";
            }
        str += indent+"}";
        return str;
    }

    @Override
    public String visitBlocDec(BlocDecImp instr, String indent) {
        String str = "{\n";

            for(int i = 0; i<instr.decls().size(); i++){
                str+=instr.decls().get(i).accept(this, indent+INDENT)+"\n";
            }
            for(int i = 0; i<instr.instrs().size(); i++){
                str+=instr.instrs().get(i).accept(this, indent+INDENT)+"\n";
            }
        str += indent+"}";
        return str;
    }

    @Override
    public String visitAssign(AssignImp instr, String indent) {
        return indent + instr.t()+ " := " + instr.e().accept(this,"");
    }

    @Override
    public String visitPrint(PrintImp instr, String indent) {
        String str = indent + "PRINT ";
        for(int i = 0; i<instr.t().size(); i++){
            Object o = instr.t().get(i);
            if(o instanceof String){
                str += "\"" + instr.t().get(i) +"\"";
            }
            else if(o instanceof Expression){
                str += ((Expression)instr.t().get(i)).accept(this,"");
            }
            if(i<instr.t().size()-1) str += ", ";
        }
        return str;
    }

    @Override
    public String visitIfThen(IfThenImp instr, String indent) {
        String str = indent + "IF ";
        str +=(instr.e().accept(this, ""));
        str +=" THEN ";
        str+= instr.i1().accept(this, "");
        return str;
    }

    @Override
    public String visitIfThenElse(IfThenElseImp instr, String indent) {
        String str = indent + "IF ";
        str +=(instr.e().accept(this, ""));
        str +=" THEN ";
        str+= instr.i1().accept(this, "");
        str +=" ELSE ";
        str+= instr.i2().accept(this, "");
        return str;
    }

    @Override
    public String visitRead(ReadImp instr, String indent) {
        String str = indent+"READ ";
        for(int i = 0; i<instr.t().size(); i++){
            str += instr.t().get(i).accept(this,indent);
            if(i<instr.t().size()-1) str += ", ";
        }
        return str;
    }

    @Override
    public String visitWhile(WhileImp instr, String indent) {
        String str = indent+"WHILE ";
        str += (instr.e().accept(this, indent))+"\n";
        str+= indent+"DO ";
        str+= instr.i1().accept(this, indent)+"\n";
        str+= indent+"DONE";
        return str;
    }

    @Override
    public String visitVoidFunction(VoidFunctionImp instr, String h) {
        String str = h+instr.nom()+ "(";
        for(int i=0;i<instr.expr().size();i++){
            str+= instr.expr().get(i).accept(this,h);
            if(i<instr.expr().size()-1){
                str+=", ";
            }
        }
        str+=")";
        return str;
    }

    //EXPRESSION
    @Override
    public String visitConst(ConstImp e, String indent) {
        return e.c()+"";
    }

    @Override
    public String visitBinOp(BinopExpressionImp e, String indent) {
        String opStr = "?";
            switch(e.op()){
                case PLUS:  opStr = "+"; break;
                case DIV:   opStr = "/"; break;
                case MINUS: opStr = "-"; break;
                case MOD:   opStr = "%"; break;
                case TIMES: opStr = "*"; break;
                default:break;
            }
            return "(" + e.e1().accept(this,"") +" "+ opStr +" " + e.e2().accept(this,"") + ")";
    }

    @Override
    public String visitCall(CallImp instr,String indent){
        String str = indent + instr.fName() + "(";
        for(int i=0; i<instr.params().size();i++){
            str += instr.params().get(i).accept(this, "");
            if(i<instr.params().size()-1) str += ",";
        }
        return str+")";
    }


    //TYPE

    @Override
    public String visitInt(Type_intImp t, String h) {
        return "INT";
    }

    @Override
    public String visitVoid(Type_voidImp t, String h) {
        return "VOID";
    }

    @Override
    public String visitVar(VarImp e, String h) {
        return e.name();
    }
    
}