package de.dhbwstuttgart.strucTypes4.typeVars;

import de.dhbwstuttgart.strucTypes4.algo.MappingAltNeu;
import de.dhbwstuttgart.strucTypes4.algo.ResultTuple;
import de.dhbwstuttgart.strucTypes4.typePlaceHolder.TPH;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * Created by sebastian on 14.04.17.
 */
public class TypeVarStore {


    private HashMap<TypeVarAbstract, List<TPH>> map = new HashMap<>();



    public TypeVarAbstract storeTPH(TPH tph) {

        if (getTypeVarByTPH(tph) == null) {
            TypeVar tvNeu = TypeVarFactory.makeTypeVar();
            addTuple(tvNeu, tph);
            return tvNeu;
        }
        else {
            return getTypeVarByTPH(tph);
        }
    }


    public TypeVarAbstract makeFreshTypeVar() {
        TypeVar neu = TypeVarFactory.makeTypeVar();
        List<TPH> tphList = new ArrayList<>();
        map.put(neu,tphList);
        return neu;
    }



    public void addTuple(TypeVarAbstract tv, TPH tph) {


        // TypVar bereits vorhandne
        if (map.containsKey(tv)) {
            if (map.get(tv) != null ) {
                map.get(tv).add(tph);
            }
            else {
                List<TPH> tphl = new ArrayList<>();
                tphl.add(tph);
                map.put(tv,tphl);
            }
        }


        // TypVar noch nicht vorhanden
        else if (! map.containsKey(tv)) {
            List<TPH> tphl = new ArrayList<>();
            tphl.add(tph);
            map.put(tv,tphl);
        }
    }


    public List<TPH> getTPHByTypeVar(TypeVarAbstract tv) {
        return map.get(tv);
    }

    public TypeVarAbstract getTypeVarByTPH(TPH tph) {
        // TPH ist bereits einer TypVar zugeordnet
        for (TypeVarAbstract typeVar : map.keySet()) {
            if (map.get(typeVar).contains(tph)) {
                return typeVar;
            }
        }
        // TPH ist noch keiner TypVar zugerodent
        return  null;
    }



    // Erstellt neue Typvariablen für bestehenden Syntax
    // -> nicht mehr benötigt
    public TypeVarStore freshVariables() {
        TypeVarStore typeVarStore = new TypeVarStore();

        for (TypeVarAbstract tvarOld : map.keySet()) {
            TypeVar tvNew = TypeVarFactory.makeTypeVar();
            typeVarStore.map.put(tvNew,map.get(tvarOld));
        }
        return typeVarStore;
    }


    public ResultTuple freshVariableAndMapping() {

       MappingAltNeu mappingAltNeu = new MappingAltNeu();
       TypeVarStore typeVarStore = new TypeVarStore();


        for (TypeVarAbstract tvarOld : map.keySet()) {

            // Wenn der Typ bereits bekannt ist, wird dieser nicht ersetzt.
            if (tvarOld.getClass().equals(TypeVarType.class)) {
                TypeVarAbstract tvNew = tvarOld;
                typeVarStore.map.put(tvNew,map.get(tvarOld));
                mappingAltNeu.addTypeVar(tvarOld,tvNew);
            }
            else {
                TypeVar tvNew = TypeVarFactory.makeTypeVar();
                typeVarStore.map.put(tvNew,map.get(tvarOld));
                mappingAltNeu.addTypeVar(tvarOld,tvNew);
            }
        }
        return new ResultTuple(mappingAltNeu,typeVarStore);

    }



    public boolean replaceTypeVar(TypeVarAbstract old, TypeVarAbstract neu) {

        if (map.containsKey(old)) {
            List<TPH> tphList = map.get(old);
            map.remove(old);
            map.put(neu, tphList);
            return true;
        }
        else {
            return false;
        }
    }



    @Override
    public String toString() {
        String res = "--- TypeVarStore ---\n";
        for (TypeVarAbstract tvar : map.keySet() ) {
            res = res + tvar.toString() + " : " +  map.get(tvar).toString() + "\n";
        }
        return String.format(res);
    }
}



