package org.studiorailgun.conversation.parser.depend;

import java.util.LinkedList;
import java.util.List;

import org.studiorailgun.conversation.parser.depend.Argument.ArgumentType;

import edu.stanford.nlp.ling.IndexedWord;
import edu.stanford.nlp.semgraph.SemanticGraph;
import edu.stanford.nlp.trees.GrammaticalRelation;

/**
 * Parses the macro structure of the sentence
 */
public class Clause {

    /**
     * The type of a clause
     */
    public static enum ClauseType {

        /**
         * A main clause
         */
        MAIN,

        /**
         * A conjugate clause
         */
        CONJUGATE,

        /**
         * A clause that depends on the main clause
         */
        DEPENDENT,

        /**
         * A paratactic clause
         */
        PARATACTIC,
    }
    
    /**
     * The type of this clause
     */
    ClauseType type;

    /**
     * The predicate of the sentence
     */
    Predicate predicate;

    /**
     * The arguments of the sentence
     */
    List<Argument> arguments = new LinkedList<Argument>();

    /**
     * The subject of the structure
     */
    Argument subject;

    /**
     * The adjuncts of the sentence
     */
    List<Adjunct> adjuncts = new LinkedList<Adjunct>();

    /**
     * The list of clauses
     */
    List<Clause> clauses = new LinkedList<Clause>();

    /**
     * The coordinator for this clause
     */
    Coordinator coordinator;

    /**
     * Parses the macro structure of a semantic graph
     * @param graph The graph
     * @return The macro structure
     */
    public static Clause parse(SemanticGraph graph){
        if(graph.getRoots().size() != 1){
            throw new Error("Unable to parse sentences with roots != 1!");
        }
        IndexedWord root = graph.getFirstRoot();
        Clause rVal = Clause.parse(graph,root, ClauseType.MAIN);
        // throw new Error("\n" + graph);
        return rVal;
    }

    /**
     * Parses the macro structure of a semantic graph
     * @param graph The graph
     * @param root The root to parse from
     * @return The macro structure
     */
    private static Clause parse(SemanticGraph graph, IndexedWord root, ClauseType type){
        Clause rVal = new Clause();
        rVal.type = type;
        List<IndexedWord> children = graph.getChildList(root);

        //the root is (typically) the predicate
        Predicate pred = new Predicate(root);
        rVal.predicate = pred;

        //parse all arguments
        for(IndexedWord child : children){
            GrammaticalRelation relation = graph.reln(root, child);
            switch(relation.getLongName()){

                //subjects
                case "nominal subject": {
                    Argument arg = new Argument(child, ArgumentType.NOMINAL);
                    rVal.arguments.add(arg);
                    rVal.subject = arg;
                } break;

                //predicates
                case "copula": {
                    //this means the root is a noun, but this related word is turning it into a copular predicate
                } break;

                //a prepositional adjunct (oblique)
                case "obl_preposition": {
                    Adjunct adj = new Adjunct(child);
                    rVal.adjuncts.add(adj);
                } break;

                //direct objects
                case "direct object": {
                    Argument arg = new Argument(child, ArgumentType.NOMINAL);
                    rVal.arguments.add(arg);
                } break;

                //indirect objects
                case "indirect object": {
                    Argument arg = new Argument(child, ArgumentType.NOMINAL);
                    rVal.arguments.add(arg);
                } break;

                //adverb modifying the root predicate
                case "adverbial modifier": {
                    pred.addAdverb(new Adverb(child));
                } break;

                //certain cases of "there" and "it" in sentences
                //when these cases occur, they are signifying that the predicate is existential
                case "expletive": {
                    pred.setExistential(true);
                } break;

                //the word coordinating this clause
                case "coordination": {
                    rVal.coordinator = new Coordinator(child);
                } break;

                //A subject of a dependent clause in a compound sentence
                case "compound modifier":{
                    Argument arg = new Argument(child, ArgumentType.COMPOUND_MODIFIER);
                    rVal.arguments.add(arg);
                    rVal.subject = arg;
                } break;

                //An argument of a verb or adjective
                case "xclausal complement": {
                    Argument arg = new Argument(child, ArgumentType.CLAUSAL_COMPLEMENT);
                    rVal.arguments.add(arg);
                } break;

                //an auxiliary verb for the predicate
                case "auxiliary": {
                    pred.setAuxiliary(child);
                } break;

                //An adverbial clause modifier
                case "advcl_preposition": {
                    Adjunct adj = new Adjunct(child);
                    rVal.adjuncts.add(adj);
                } break;

                //Something that is combined with the verb to create a new meaning
                //eg "turn" -> "turn up"
                case "phrasal verb particle": {
                    pred.setPhrasalVerbParticle(child);
                } break;


                //
                //clauses
                //

                //a collapsed clause
                case "conj_collapsed": {
                    Clause clause = Clause.parse(graph, child, ClauseType.CONJUGATE);
                    rVal.clauses.add(clause);
                } break;

                //this is a dependent clause that is functioning as an argument
                case "clausal complement": {
                    Argument arg = new Argument(child, ArgumentType.CLAUSAL_COMPLEMENT);
                    rVal.arguments.add(arg);
                } break;

                /**
                 * A dependent clause
                 */
                case "dependent": {
                    Clause clause = Clause.parse(graph, child, ClauseType.DEPENDENT);
                    rVal.clauses.add(clause);
                } break;

                /**
                 * A paratactic construct (A clause that is very loosely related to the main clause)
                 */
                case "parataxis": {
                    Clause clause = Clause.parse(graph, child, ClauseType.PARATACTIC);
                    rVal.clauses.add(clause);
                } break;




                //
                // Misc
                //


                //cases to ignore
                case "punctuation": {
                    continue;
                }

                //unhandled cases
                default: {
                    throw new Error("Unsupported relation type! " + relation.getLongName() + "\n" + "for " + child.originalText() + "\n" + graph);
                }
            }
        }
        // throw new Error("\n" + graph);
        return rVal;
    }

    public Predicate getPredicate() {
        return predicate;
    }

    public List<Argument> getArguments() {
        return arguments;
    }

    public Argument getSubject() {
        return subject;
    }

    public List<Adjunct> getAdjuncts() {
        return adjuncts;
    }

    public List<Clause> getClauses(){
        return clauses;
    }
    
    public Coordinator getCoordinator(){
        return coordinator;
    }

    

}
