package experiment.hierarchy.axiomsets.gg;

import experiment.ont.*;
import experiment.util.*;
import experiment.structure.*;
import experiment.structure.nodes.*;
import experiment.iso.gg.*;
import experiment.subIso.*;
import experiment.regularities.axiomsets.gg.*;


import java.io.*;
import java.util.*;
import java.util.stream.*;
import org.semanticweb.owlapi.util.*;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.model.parameters.Imports;
import org.semanticweb.owlapi.model.parameters.*;
import org.semanticweb.owlapi.formats.OWLXMLDocumentFormat;
import org.semanticweb.owlapi.formats.RDFXMLDocumentFormat;
import org.semanticweb.owlapi.model.*;
import org.semanticweb.owlapi.reasoner.*;
import org.semanticweb.HermiT.ReasonerFactory;
import java.util.logging.Logger;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.*;
import org.semanticweb.elk.owlapi.ElkReasonerFactory;
import org.semanticweb.owlapi.change.AxiomChangeData;
import org.semanticweb.owlapi.search.EntitySearcher; 

import java.nio.file.*;

import org.jgrapht.*;
import org.jgrapht.graph.*;
import org.jgrapht.traverse.*;


import uk.ac.manchester.cs.owlapi.modularity.SyntacticLocalityModuleExtractor;
import uk.ac.manchester.cs.owlapi.modularity.ModuleType;


public class SetRegularityHierarchy {

    public static void main(String[] args) throws IOException , Exception{

        String ontFilePath = args[0]; 
        String output = args[1];

        File ontFile = new File(ontFilePath);
        OntologyLoader ontLoader = new OntologyLoader(ontFile, false);
        OWLOntology ont = ontLoader.getOntology(); 

        String ontologyName = Paths.get(ontFilePath).getFileName().toString();

        ClassFrameGGMiner frameMiner = new ClassFrameGGMiner(ont);
        Map<ClassFrameGroundGeneralisation,Set<ClassFrameGroundGeneralisation>> reg2instH = frameMiner.getRegularity2instance();

        Map<ClassFrameGroundGeneralisation,Set<OWLClassExpression>> reg2inst = new HashMap<>();
        for (Map.Entry<ClassFrameGroundGeneralisation, Set<ClassFrameGroundGeneralisation>>
                entry : reg2instH.entrySet()) {
            Set<OWLClassExpression> ces = new HashSet<>();
            for(ClassFrameGroundGeneralisation f : entry.getValue()){
                ces.add(f.getClassExpression());
            }
            reg2inst.put(entry.getKey(), ces); 
        } 

        SetRegularityHierarchy hierarchy = new SetRegularityHierarchy(reg2inst); 
        hierarchy.writeGraphWithInstances(output);
    }

    private Map<HierarchyNode,ClassFrameGroundGeneralisation> node2frame;
    private Map<ClassFrameGroundGeneralisation,HierarchyNode> frame2node; 
    private Map<ClassFrameGroundGeneralisation, Set<OWLClassExpression>> frame2instance;

    private Map<Integer, HierarchyNode> id2node;
    private Map<HierarchyNode, Integer> node2id;

    private Set<HierarchyNode> nodes;
    private Set<HierarchyNode> roots;

    private TreeMap<Integer,Set<HierarchyNode>> specificity2node;

    public SetRegularityHierarchy(Map<ClassFrameGroundGeneralisation, Set<OWLClassExpression>> reg2inst){
        this.node2frame = new HashMap<>();
        this.frame2node = new HashMap<>();
        this.frame2instance = new HashMap<>();

        this.id2node = new HashMap<>();
        this.node2id = new HashMap<>();

        this.nodes = new HashSet<>();
        this.roots = new HashSet<>(); 

        this.specificity2node = new TreeMap<>();

        int id = 1;

        for (Map.Entry<ClassFrameGroundGeneralisation, Set<OWLClassExpression>> entry : reg2inst.entrySet()) {
            HierarchyNode n = new HierarchyNode(entry.getKey(), entry.getValue(), id);
            this.id2node.put(id,n);
            this.node2id.put(n,id);
            this.node2frame.put(n,entry.getKey());
            this.frame2node.put(entry.getKey(), n);
            this.frame2instance.put(entry.getKey(), entry.getValue()); 
            this.nodes.add(n);
            int specificity = entry.getKey().size();
            this.specificity2node.putIfAbsent(specificity, new HashSet<>());
            this.specificity2node.get(specificity).add(n);
            id++;
        } 
        this.buildHierarchy();
    }

    private void buildHierarchy(){
        for(Map.Entry<Integer,Set<HierarchyNode>> entry : this.specificity2node.entrySet()){
            Integer specificity = entry.getKey();
            Set<HierarchyNode> nodes2insert = entry.getValue();

            for(HierarchyNode n : nodes2insert){
                boolean insertionPointFound = false;
                for(HierarchyNode r : this.roots){
                    if(r.insertFromAbove(n)){
                        insertionPointFound = true;
                    }
                }
                if(!insertionPointFound){
                    this.roots.add(n);
                } 
            }
        } 
    } 

    public Set<HierarchyNode> getNodes(){
        return this.nodes;
    }

    public Set<HierarchyNode> getRoots(){
        return this.roots;
    }

    public void writeGraphWithInstances(String output){
        IOHelper.writeAppend("digraph gname {", output + "/graph");

        for(HierarchyNode node : this.nodes){
            String label = node.getID() + " [label=\"" + node.getID() + "\\n" + node.getInstances().size() + "\"]";
            IOHelper.writeAppend(label, output + "/graph");
        } 

        writeGraph(output);

        IOHelper.writeAppend("}", output + "/graph"); 
    }

    public void writeGraph(String output){
        for(HierarchyNode node : this.nodes){
            for(HierarchyNode c : node.getChildren()){ 
                String edge = node.getID() + " -> " + c.getID();
                IOHelper.writeAppend(edge, output + "/graph");
            }
        }
    }

    public int getClassFrameID(ClassFrameGroundGeneralisation f){
        HierarchyNode node = this.frame2node.get(f);
        int id = this.node2id.get(node); 
        return id;
    }
}
