import java.util.TreeMap;
import java.util.ArrayList;

public class JavaExample
{

    public static void main(String [] args)
    {
        Class aClass = new Class("A", "Object",
                                 new ArrayList<Field>(),
                                 new Constructor("A", new ArrayList<Argument>(), new ArrayList<String>(), new ArrayList<String>()),
                                 new ArrayList<Method>());
        Class bClass = new Class("B", "Object",
                                 new ArrayList<Field>(),
                                 new Constructor("B", new ArrayList<Argument>(), new ArrayList<String>(), new ArrayList<String>()),
                                 new ArrayList<Method>());
        Class pairClass = new Class("Pair", "Object",
                                    new ArrayList<Field>() {{ add(new Field("Object", "fst")); add(new Field("Object", "snd")); }},
                                    new Constructor("Pair",
                                                    new ArrayList<Argument>() {{ add(new Argument("Object", "fst")); add(new Argument("Object", "snd")); }},
                                                    new ArrayList<String>(),
                                                    new ArrayList<String>() {{ add("fst"); add("snd"); }}),
                                    new ArrayList<Method>() {{
                                            add(new Method("Pair", "setfst", new ArrayList<Argument>() {{ add(new Argument("Object", "newfst")); }},
                                                                     new New("Pair", new ArrayList<Expression>() {{
                                                                                 add(new Variable("newfst"));
                                                                                 add(new FieldAccess(new Variable("this"), "snd")); }}))); }});

        Expression exampleExpression = new FieldAccess(new Cast("Pair", new FieldAccess( new New("Pair", new ArrayList<Expression>() {{
                                add(new New("Pair", new ArrayList<Expression>() {{
                                                add(new New("A", new ArrayList<Expression>()));
                                                add(new New("B", new ArrayList<Expression>()));
                                            }}));
                                add(new New("A", new ArrayList<Expression>()));
                            }} ), "fst")), "snd");

        ClassTable exampleClassTable = new ClassTable();
        exampleClassTable.classTable.put(aClass.className, aClass);
        exampleClassTable.classTable.put(bClass.className, bClass);
        exampleClassTable.classTable.put(pairClass.className, pairClass);

        exampleExpression.typecheck(exampleClassTable, new TreeMap<>());
    }
}

class Field {
    String fieldType;
    String fieldName;

    Field(String fieldType, String fieldName) {
        this.fieldType = fieldType;
        this.fieldName = fieldName;
    }
}

class Argument {
    String argumentType;
    String argumentName;

    Argument(String argumentType, String argumentName) {
        this.argumentType = argumentType;
        this.argumentName = argumentName;
    }
}

class MethodType {
    ArrayList<String> argumentTypes;
    String returnType;

    MethodType(ArrayList<String> argumentTypes, String returnType) {
        this.argumentTypes = argumentTypes;
        this.returnType = returnType;
    }
}

abstract class Expression {
    String typecheck(ClassTable classTable, TreeMap<String, String> context) {
        return null;
    }
}

class Variable extends Expression {
    String name;

    Variable(String name) {
        this.name = name;
    }
}

class FieldAccess extends Expression {
    Expression lhs;
    String fieldName;

    FieldAccess(Expression lhs, String fieldName) {
        this.lhs = lhs;
        this.fieldName = fieldName;
    }
}

class MethodInvocation extends Expression {
    Expression lhs;
    String methodName;
    ArrayList<Expression> arguments;

    MethodInvocation(Expression lhs, String methodName, ArrayList<Expression> arguments) {
        this.lhs = lhs;
        this.methodName = methodName;
        this.arguments = arguments;
    }
}

class New extends Expression {
    String className;
    ArrayList<Expression> constructorArguments;

    New(String className, ArrayList<Expression> constructorArguments) {
        this.className = className;
        this.constructorArguments = constructorArguments;
    }
}

class Cast extends Expression {
    String className;
    Expression expression;

    Cast(String className, Expression expression) {
        this.className = className;
        this.expression = expression;
    }
}

class Class {
    String className;
    String parentClassName;
    ArrayList<Field> fields;
    Constructor constructor;
    ArrayList<Method> methods;

    Class(String className, String parentClassName, ArrayList<Field> fields, Constructor constructor, ArrayList<Method> methods) {
        this.className = className;
        this.parentClassName = parentClassName;
        this.fields = fields;
        this.constructor = constructor;
        this.methods = methods;
    }
}

class Constructor {
    String className;
    ArrayList<Argument> arguments;
    ArrayList<String> parentFieldNames;
    ArrayList<String> fieldNames;

    Constructor(String className, ArrayList<Argument> arguments, ArrayList<String> parentFieldNames, ArrayList<String> fieldNames) {
        this.className = className;
        this.arguments = arguments;
        this.parentFieldNames = parentFieldNames;
        this.fieldNames = fieldNames;
    }
}

class Method {
    String returnType;
    String methodName;
    ArrayList<Argument> arguments;
    Expression body;

    Method(String returnType, String methodName, ArrayList<Argument> arguments, Expression body) {
        this.returnType = returnType;
        this.methodName = methodName;
        this.arguments = arguments;
        this.body = body;
    }
}


class ClassTable {
    TreeMap<String, Class> classTable;

    ClassTable() {
        classTable = new TreeMap<>();
    }

    Boolean subType(String className, String parentClassName) {
        return null;
    }
    ArrayList<Field> fields(String className) {
        return null;
    }
    MethodType methodType(String className) {
        return null;
    }
}


