package chocopy.pa2;

import chocopy.common.analysis.SymbolTable;
import chocopy.common.analysis.types.ClassVType;
import chocopy.common.analysis.types.ClassValueType;
import chocopy.common.analysis.types.Type;
import chocopy.common.analysis.types.ValueType;
import chocopy.common.astnodes.ClassType;
import chocopy.common.astnodes.Program;
import java.util.ArrayList;
/** Top-level class for performing semantic analysis. */
public class StudentAnalysis {

    /**
     * Perform semantic analysis on PROGRAM, adding error messages and type annotations. Provide
     * debugging output iff DEBUG. Returns modified tree.
     */
    
    public static 
        boolean subClassOf(Type p, Type c, SymbolTable<Type> sym){
        String pName = p.className();
        if(pName!=null && pName.equals("object"))
            return true;
        if(c instanceof ClassValueType)
            c = sym.get(c.className());
        if(c instanceof ClassVType){
            ClassVType child  = (ClassVType) c;

            String typename = child.className;
            while(typename!=null){
                if(typename.equals(pName))
                    return true;
                child = child.super_class;
                if(child!=null)
                    typename = child.className;
                else
                    return false;
            }
            return false;
        } else return p.equals(c);
    }
    private static void extractInhPath(Type ty, ArrayList<Type> res){
        if(ty == null)
        {   
            res.add(Type.OBJECT_TYPE);
            return;
        }
        if(ty instanceof ClassVType){
            ClassVType t1  = (ClassVType) ty;
            String typename = t1.className;
            while(typename!=null){
                res.add(new ClassValueType(typename));
                t1 = t1.super_class;
                if(t1 != null)
                    typename = t1.className();
                else break;
            }
        } else res.add(ty);
        if(!res.get(res.size() - 1).equals(Type.OBJECT_TYPE))
            res.add(Type.OBJECT_TYPE);
    }
    public static Type lowestCommonType(Type p, Type c, SymbolTable<Type> sym){
        if(p instanceof ClassValueType)
            p = sym.get(p.className());
        if(c instanceof ClassValueType)
            c = sym.get(c.className());
        ArrayList<Type> inhPath1 = new ArrayList<Type>(), 
                inhPath2 = new ArrayList<Type>();
        extractInhPath(p, inhPath1);
        extractInhPath(c, inhPath2);
        int l1 = inhPath1.size(), l2 = inhPath2.size(), 
            len = l1 < l2 ? l1 : l2;
        int i = 1;
        for(; i <= len; ++ i){
            if(!inhPath1.get(l1 - i).equals(inhPath2.get(l2 - i)))
                break;
        }
        return inhPath1.get(l1 - i + 1);
    }
    public static Program process(Program program, boolean debug) {
        if (program.hasErrors()) {
            return program;
        }

        DeclarationAnalyzer declarationAnalyzer = new DeclarationAnalyzer(program.errors);
        program.dispatch(declarationAnalyzer);
        SymbolTable<Type> globalSym = declarationAnalyzer.getGlobals();

        if (!program.hasErrors()) {
            TypeChecker typeChecker = new TypeChecker(globalSym, program.errors);
            program.dispatch(typeChecker);
        }
        // System.out.println(program);
        return program;
    }
}
