import java.io.BufferedReader;

import Controllers.Controller;
import Exceptions.BaseException;
import Models.Expressions.ArithmeticExpression;
import Models.Expressions.ReadHeap;
import Models.Expressions.RelationalExpression;
import Models.Expressions.ValueExpression;
import Models.Expressions.VariableExpression;
import Models.Program.GenericDictionary;
import Models.Program.GenericQueue;
import Models.Program.GenericStack;
import Models.Program.Heap;
import Models.Program.ProgramState;
import Models.Statements.AssignmentStatement;
import Models.Statements.CloseReadFileStatement;
import Models.Statements.CompoundStatement;
import Models.Statements.ForkStatement;
import Models.Statements.IStatement;
import Models.Statements.IfStatement;
import Models.Statements.NewStatement;
import Models.Statements.OpenReadFileStatement;
import Models.Statements.PrintStatement;
import Models.Statements.ReadFileStatement;
import Models.Statements.VariableDeclarationStatement;
import Models.Statements.WhileStatement;
import Models.Statements.WriteHeap;
import Models.Types.BooleanType;
import Models.Types.IntegerType;
import Models.Types.ReferenceType;
import Models.Types.StringType;
import Models.Values.BooleanValue;
import Models.Values.IValue;
import Models.Values.IntegerValue;
import Models.Values.StringValue;
import Repositories.IRepository;
import Repositories.Repository;
import Views.TextMenu;
import Views.Commands.ExitCommand;
import Views.Commands.RunExampleCommand;

public class Interpreter {
    public static void main(String[] args) throws BaseException{
        IStatement ex1= new CompoundStatement(new VariableDeclarationStatement("v", new IntegerType()), new CompoundStatement(new AssignmentStatement("v", new ValueExpression(new IntegerValue((2)))), new PrintStatement(new VariableExpression(("v")))));
        ProgramState programState1 = new ProgramState(new GenericStack<IStatement>(), new GenericDictionary<String,IValue>(), new GenericQueue<IValue>(), new GenericDictionary<StringValue, BufferedReader>(), new Heap(),ex1);
        IRepository repository1 = new Repository("log1.txt");
        repository1.addProgramState(programState1);
        Controller controller1 = new Controller(repository1, true);

        IStatement ex2 = new CompoundStatement( new VariableDeclarationStatement("a",new IntegerType()),
        new CompoundStatement(new VariableDeclarationStatement("b",new IntegerType()),
        new CompoundStatement(new AssignmentStatement("a", new ArithmeticExpression('+',new ValueExpression(new IntegerValue(2)),new
        ArithmeticExpression('*',new ValueExpression(new IntegerValue(3)), new ValueExpression(new IntegerValue(5))))),
            new CompoundStatement(new AssignmentStatement("b",new ArithmeticExpression('+',new VariableExpression("a"), new ValueExpression(new
        IntegerValue(1)))), new PrintStatement(new VariableExpression("b"))))));
        ProgramState programState2 = new ProgramState(new GenericStack<IStatement>(), new GenericDictionary<String,IValue>(), new GenericQueue<IValue>(), new GenericDictionary<StringValue, BufferedReader>(), new Heap(),ex2);
        IRepository repository2 = new Repository("log2.txt");
        repository2.addProgramState(programState2);
        Controller controller2 = new Controller(repository2, true);

        IStatement ex3 = new CompoundStatement(new VariableDeclarationStatement("a",new BooleanType()),
        new CompoundStatement(new VariableDeclarationStatement("v", new IntegerType()),
        new CompoundStatement(new AssignmentStatement("a", new ValueExpression(new BooleanValue(true))),
        new CompoundStatement(new IfStatement(new VariableExpression("a"),new AssignmentStatement("v",new ValueExpression(new
        IntegerValue(2))), new AssignmentStatement("v", new ValueExpression(new IntegerValue(3)))), new PrintStatement(new
        VariableExpression("v"))))));
        ProgramState programState3 = new ProgramState(new GenericStack<IStatement>(), new GenericDictionary<String,IValue>(), new GenericQueue<IValue>(), new GenericDictionary<StringValue, BufferedReader>(), new Heap(),ex3);
        IRepository repository3 = new Repository("log3.txt");
        repository3.addProgramState(programState3);
        Controller controller3 = new Controller(repository3, true);
        
        IStatement ex4 = new CompoundStatement(new VariableDeclarationStatement("varf", new StringType()), new CompoundStatement(new AssignmentStatement("varf", new ValueExpression(new StringValue("test.in"))), new CompoundStatement(new OpenReadFileStatement(new VariableExpression("varf")), new CompoundStatement(new VariableDeclarationStatement("varc", new IntegerType()), new CompoundStatement(new ReadFileStatement(new VariableExpression("varf"), "varc"), new CompoundStatement(new PrintStatement(new VariableExpression("varc")), new CompoundStatement(new ReadFileStatement(new VariableExpression("varf"), "varc"), new CompoundStatement(new PrintStatement(new VariableExpression("varc")), new CloseReadFileStatement(new VariableExpression("varf"))))))))));
        ProgramState programState4 = new ProgramState(new GenericStack<IStatement>(), new GenericDictionary<String,IValue>(), new GenericQueue<IValue>(), new GenericDictionary<StringValue, BufferedReader>(), new Heap(),ex4);
        IRepository repository4 = new Repository("log4.txt");
        repository4.addProgramState(programState4);
        Controller controller4 = new Controller(repository4, true);

        IStatement ex5 = new CompoundStatement(new IfStatement(new RelationalExpression(new ValueExpression(new IntegerValue(1)), new ValueExpression(new IntegerValue(2)), "<"), new PrintStatement(new ValueExpression(new IntegerValue(1))),new PrintStatement(new ValueExpression(new IntegerValue(2)))), new PrintStatement(new ValueExpression(new IntegerValue(3))));
        ProgramState programState5 = new ProgramState(new GenericStack<IStatement>(), new GenericDictionary<String, IValue>(), new GenericQueue<IValue>(), new GenericDictionary<StringValue, BufferedReader>(), new Heap(),ex5);
        IRepository repository5 = new Repository("log5.txt");
        repository5.addProgramState(programState5);
        Controller controller5 = new Controller(repository5, true);

        IStatement ex6 = new CompoundStatement(new VariableDeclarationStatement("v", new ReferenceType(new IntegerType())), new CompoundStatement(new NewStatement("v", new ValueExpression(new IntegerValue(20))), new CompoundStatement(new VariableDeclarationStatement("a", new ReferenceType(new ReferenceType(new IntegerType()))), new CompoundStatement(new NewStatement("a", new VariableExpression("v")), new CompoundStatement(new NewStatement("v", new ValueExpression(new IntegerValue(30))), new PrintStatement(new ReadHeap(new ReadHeap(new VariableExpression("a")))))))));
        ProgramState programState6 = new ProgramState(new GenericStack<IStatement>(), new GenericDictionary<String, IValue>(), new GenericQueue<IValue>(), new GenericDictionary<StringValue, BufferedReader>(), new Heap(),ex6);
        IRepository repository6 = new Repository("log6.txt");
        repository6.addProgramState(programState6);
        Controller controller6 = new Controller(repository6, true);

        IStatement ex7 = new CompoundStatement(new VariableDeclarationStatement("v", new ReferenceType(new IntegerType())), new CompoundStatement(new NewStatement("v", new ValueExpression(new IntegerValue(10))), new CompoundStatement(new NewStatement("v", new ValueExpression(new IntegerValue(20))), new PrintStatement(new VariableExpression("v")))));
        ProgramState programState7 = new ProgramState(new GenericStack<IStatement>(), new GenericDictionary<String, IValue>(), new GenericQueue<IValue>(), new GenericDictionary<StringValue, BufferedReader>(), new Heap(),ex7);
        IRepository repository7 = new Repository("log7.txt");
        repository7.addProgramState(programState7);
        Controller controller7 = new Controller(repository7, true);

        IStatement ex8 = new CompoundStatement(new VariableDeclarationStatement("v", new IntegerType()), new CompoundStatement(new AssignmentStatement("v", new ValueExpression(new IntegerValue(4))), new CompoundStatement(new WhileStatement(new CompoundStatement(new PrintStatement(new VariableExpression("v")), new AssignmentStatement("v", new ArithmeticExpression('-', new VariableExpression("v"), new ValueExpression(new IntegerValue(1))))), new RelationalExpression(new VariableExpression("v"), new ValueExpression(new IntegerValue(0)), ">")), new PrintStatement(new VariableExpression("v")))));
        ProgramState programState8 = new ProgramState(new GenericStack<IStatement>(), new GenericDictionary<String, IValue>(), new GenericQueue<IValue>(), new GenericDictionary<StringValue, BufferedReader>(), new Heap(),ex8);
        IRepository repository8 = new Repository("log8.txt");
        repository8.addProgramState(programState8);
        Controller controller8 = new Controller(repository8, true);

        IStatement ex9 = new CompoundStatement(new VariableDeclarationStatement("v", new IntegerType()), new CompoundStatement(new VariableDeclarationStatement("a", new ReferenceType(new IntegerType())), new CompoundStatement(new AssignmentStatement("v", new ValueExpression(new IntegerValue(10))), new CompoundStatement(new NewStatement("a", new ValueExpression(new IntegerValue(22))), new CompoundStatement(new ForkStatement(new CompoundStatement(new WriteHeap("a", new ValueExpression(new IntegerValue(30))), new CompoundStatement(new AssignmentStatement("v", new ValueExpression(new IntegerValue(32))), new CompoundStatement(new PrintStatement(new VariableExpression("v")), new PrintStatement(new ReadHeap(new VariableExpression("a"))))))), new CompoundStatement(new PrintStatement(new VariableExpression("v")), new PrintStatement(new ReadHeap(new VariableExpression("a")))))))));
        ProgramState programState9 = new ProgramState(new GenericStack<IStatement>(), new GenericDictionary<String, IValue>(), new GenericQueue<IValue>(), new GenericDictionary<StringValue, BufferedReader>(), new Heap(),ex9);
        IRepository repository9 = new Repository("log9.txt");
        repository9.addProgramState(programState9);
        Controller controller9 = new Controller(repository9, true);

        IStatement badTypes = new CompoundStatement(new VariableDeclarationStatement("v", new IntegerType()), new AssignmentStatement("v", new ValueExpression(new BooleanValue(true))));
        ProgramState badProgramState = new ProgramState(new GenericStack<IStatement>(), new GenericDictionary<String, IValue>(), new GenericQueue<IValue>(), new GenericDictionary<StringValue, BufferedReader>(), new Heap(),badTypes);
        try{
            IRepository badRepository = new Repository("logBad.txt");
            badRepository.addProgramState(badProgramState);
        }
        catch(BaseException exception){
            System.out.println(exception.getMessage());
        }
        TextMenu menu = new TextMenu();
        menu.addCommand(new RunExampleCommand("1", "Run example 1", controller1));
        menu.addCommand(new RunExampleCommand("2", "Run example 2", controller2));
        menu.addCommand(new RunExampleCommand("3", "Run example 3", controller3));
        menu.addCommand(new RunExampleCommand("4", "Run example 4", controller4));
        menu.addCommand(new RunExampleCommand("5", "Run example 5", controller5));
        menu.addCommand(new RunExampleCommand("6", "Run example 6", controller6));
        menu.addCommand(new RunExampleCommand("7", "Run example 7", controller7));
        menu.addCommand(new RunExampleCommand("8", "Run example 8", controller8));
        menu.addCommand(new RunExampleCommand("9", "Run example 9", controller9));
        menu.addCommand(new ExitCommand("e", "Exit"));
        menu.show();
        
    }
}
