package TP2.llvm;

import java.util.ArrayList;

import TP2.asd.Interface.*;
import TP2.llvm.Interface.*;


public class ProgramLLVM {

    //Program
    public static record ProgramLLVMImp(ArrayList<DeclarGlobalLLVMImp> declarGlobal ,ArrayList<DefineLLVM> fonctions) implements ProgLLVM{
        public <H, S> S accept(ProgramLLVMVisitor<H, S> v, H h) {
            return v.visitProgramLLVM(this, h);
        }
        
        public String prettyprinter(){
            PrettyprinterLLVM_Visitor ppVisitor = new PrettyprinterLLVM_Visitor();
            return this.accept(ppVisitor, "");
        }
    }

    
    //Define
    public static record DefineLLVMImp(String name, TypeLLVM type, ArrayList<VarLLVMImp> params, ArrayList<InstructionLLVM> instrs) implements DefineLLVM{
        public <H, S> S accept(DefineLLVMVisitor<H, S> v, H h) {
            return v.visitDefineLLVM(this, h);
        }
    }


    //Instructon :
        //Label
    public static record LabelLLVMImp(String name) implements InstructionLLVM{
        @Override
        public <H, S> S accept(InstructionLLVMVisitor<H, S> v, H h) {
            return v.visitLabelLLVM(this, h);
        }
    }

    public static record BrLLVMImp(String label) implements InstructionLLVM{
        @Override
        public <H, S> S accept(InstructionLLVMVisitor<H, S> v, H h) {
            return v.visitBrLLVM(this, h);
        }
    }

    public static record BrCondLLVMImp(VarLLVMImp var, String label, String labelElse) implements InstructionLLVM{
        @Override
        public <H, S> S accept(InstructionLLVMVisitor<H, S> v, H h) {
            return v.visitBrCondLLVM(this, h);
        }
    } 

     
    public static record AssignLLVMImp(VarLLVMImp var, ExpressionLLVM e) implements InstructionLLVM{
        @Override
        public <H, S> S accept(InstructionLLVMVisitor<H, S> v, H h) {
            return v.visitAssignLLVM(this, h);
        }
    }

    public static record ReturnLLVMImp(TypeLLVM type, ExpressionLLVM e) implements InstructionLLVM{
        @Override
        public <H, S> S accept(InstructionLLVMVisitor<H, S> v, H h) {
            return v.visitReturnLLVM(this, h);
        }
    }

    public static record StoreLLVMImp(TypeLLVM type, ExpressionLLVM e, ValLLVM var) implements InstructionLLVM{
        @Override
        public <H, S> S accept(InstructionLLVMVisitor<H, S> v, H h) {
            return v.visitStoreLLVM(this, h);
        }
    }

    public static record CallVoidLLVMImp(DefineLLVMImp f, ArrayList<ValLLVM> params, String str) implements InstructionLLVM{
        @Override
        public <H, S> S accept(InstructionLLVMVisitor<H, S> v, H h) {
            return v.visitCallVoidLLVM(this, h);
        }
    }

    public static record PrintLLVMImp(DeclarGlobalLLVMImp fmt,ArrayList<ValLLVM> l) implements  InstructionLLVM{
        @Override
        public <H, S> S accept(InstructionLLVMVisitor<H, S> v, H h) {
            return v.visitPrintLLVM(this, h);
        }
    }

    public static record ScanLLVMImp(DeclarGlobalLLVMImp fmt,ArrayList<ValLLVM> l) implements InstructionLLVM{ 

        @Override
        public <H, S> S accept(InstructionLLVMVisitor<H, S> v, H h) {
            return v.visitScanLLVM(this, h);
        }
    }

    public static record DeclarGlobalLLVMImp(VarLLVMImp var,TypeLLVM type,String str, int size) implements InstructionLLVM{

        @Override
        public <H, S> S accept(InstructionLLVMVisitor<H, S> v, H h) {
            return v.visitDeclarGlobalLLVM(this, h);
        }
    }

    //Expression :
    public static record BinOpLLVMImp(TypeLLVM type,Op op, ValLLVM val1,ValLLVM val2) implements ExpressionLLVM{
        @Override
        public <H, S> S accept(ExpressionLLVMVisitor<H, S> v, H h) {
            return v.visitBinOpLLVM(this, h);
        }

        @Override
        public TypeLLVM getType() {
            return type;
        }
    }


    public static record allocaLLVMImp(TypeLLVM type) implements ExpressionLLVM{
        @Override
        public <H, S> S accept(ExpressionLLVMVisitor<H, S> v, H h) {
            return v.visitAllocaLLVM(this, h);
        }

        @Override
        public TypeLLVM getType() {
            return type;
        }
    }


    public static record LoadLLVMImp(ValLLVM val) implements ExpressionLLVM{
        @Override
        public <H, S> S accept(ExpressionLLVMVisitor<H, S> v, H h) {
            return v.visitLoadLLVM(this, h);
        }

        @Override
        public TypeLLVM getType() {
            return val().getType();
        }
    }

    public static record IcmpLLVMImp(ValLLVM val1, ValLLVM val2) implements ExpressionLLVM{
        @Override
        public <H, S> S accept(ExpressionLLVMVisitor<H, S> v, H h) {
            return v.visitIcmpLLVM(this, h);
        }

        @Override
        public TypeLLVM getType() {
            return new BooleanLLVMImp();
        }

    }

    public static record CallLLVMImp(DefineLLVMImp f, ArrayList<ValLLVM> params, String str) implements ExpressionLLVM{
        @Override
        public <H, S> S accept(ExpressionLLVMVisitor<H, S> v, H h) {
            return v.visitCallLLVM(this, h);
        }

        @Override
        public TypeLLVM getType() {
            return f.type();
        }
    }

    public static record GetElementPtr(VarLLVMImp ptrVar, ExpressionLLVM i) implements ExpressionLLVM{

        @Override
        public <H, S> S accept(ExpressionLLVMVisitor<H, S> v, H h) {
            return v.visitGetElementPtrLLVM(this, h);
        }

        @Override
        public TypeLLVM getType() {
            return ptrVar.type();
        }

    }

    //Val

    public static record ValLLVMImp(TypeLLVM type, int val) implements ValLLVM{
        @Override
        public <H, S> S accept(ExpressionLLVMVisitor<H, S> v, H h) {
            return v.visitValLLVM(this, h);
        }

        @Override
        public TypeLLVM getType() {
            return type;
        }
    }


    public static record VarLLVMImp(TypeLLVM type, String nom, Boolean isGlobal) implements ValLLVM{
        @Override
        public <H, S> S accept(ExpressionLLVMVisitor<H, S> v, H h) {
            return v.visitVarLLVM(this, h);
        }

        @Override
        public TypeLLVM getType() {
            return type;
        }
    }


    public static record IntLLVMImp() implements TypeLLVM{
        @Override
        public <H, S> S accept(TypeLLVMVisitor<H, S> v, H h) {
            return v.visitIntLLVM(this, h);
        }
    }


    public static record VoidLLVMImp() implements TypeLLVM{
        @Override
        public <H, S> S accept(TypeLLVMVisitor<H, S> v, H h) {
            return v.visitVoidLLVM(this, h);
        }
    }

    public static record BooleanLLVMImp() implements TypeLLVM{
        @Override
        public <H, S> S accept(TypeLLVMVisitor<H, S> v, H h) {
            return v.visitBooleanLLVM(this, h);
        }
    }

    public static record CharLLVMImp() implements TypeLLVM{
        @Override
        public <H, S> S accept(TypeLLVMVisitor<H, S> v, H h) {
            return v.visitCharLLVM(this, h);
        }
    }

    public static record PointerLLVMImp(TypeLLVM type) implements TypeLLVM{
        public <H, S> S accept(TypeLLVMVisitor<H, S> v, H h) {
            return v.visitPointerLLVM(this, h);
        }
    }

    public static record ArrayLLVMImp(TypeLLVM type, Expression size) implements TypeLLVM{
        public <H,S> S accept(TypeLLVMVisitor<H, S> v, H h) {
            return v.visitArrayLLVM(this, h);
        }
    }
}   
