using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using U.Graph.HelperMethods;
using U.ListOperations;
using U.StringOperations;

namespace U.Graph
{
    public class Graph
    {
        private int[,] incidenceMatrix;   // N x N matrix with edgeIndex or -1 entries
        private Node[] nodes;
        private Arc[] arcs;
        private Path[] paths;


        #region CONSTRUCTORS
        public Graph() { }
        public Graph(Node[] nodes, Arc[] arcs, Path[] paths)
        {
            this.nodes = nodes;
            this.arcs = arcs;
            this.paths = paths; if (paths == null) { this.paths = new Path[0]; }
            GraphValidity.check(this);
            GraphInitialization.initialize(this);
            createIncidenceMatrix();
        }
        public Graph(List<string> args)
        {
            this.nodes = GraphString.getNodes(args[0]);
            this.arcs = GraphString.getArcs(args[1]);
            this.paths = GraphString.getPaths(args[2]);
            createIncidenceMatrix();
        }
        public void createIncidenceMatrix()
        {
            this.incidenceMatrix = UArray.sameValues<int>(-1, nodes.Length, nodes.Length);
            for (int a = 0; a < arcs.Length; a++) { this.incidenceMatrix[arcs[a].getFromIndex(), arcs[a].getToIndex()] = a; }
        }
        #endregion




        #region to delete
        public Graph(string strGraph)
        {
            StrFunc sf = GrStr.getStrFunc(strGraph);
            this.nodes = GrStr.getNodes(sf);
            this.arcs = GrStr.getArcs(sf);
            this.paths = GrStr.getPaths(sf);
            GraphValidity.check(this);
            GraphInitialization.initialize(this);
        }
        public void reConstruct(Node[] nodes, Arc[] arcs, Path[] paths)
        {
            this.nodes = nodes;
            this.arcs = arcs;
            this.paths = paths; if (paths == null) { this.paths = new Path[0]; }
            //GraphValidity.check(this);
            //GraphInitialization.initialize(this);
        } 
        #endregion
        



        // SETTERS
        public void insertPaths(List<List<int>> lstNodeIndices) { PathInsertion.add(this, lstNodeIndices); }
        public void clearPaths() { PathDeletion.clearAll(this); }
        public void setIncidenceMatrix(int[,] incidenceMatrix) { this.incidenceMatrix = incidenceMatrix; }
        public void setNodes(Node[] nodes) { this.nodes = nodes; }
        public void setArcs(Arc[] arcs) { this.arcs = arcs; }
        public void setPaths(Path[] paths) { this.paths = paths; }
        public void setPathLabels() { foreach (Path path in this.paths) { path.calcSetLabel(this); } }

        // GETTERS
        public int[,] getIncidenceMatrix() { return this.incidenceMatrix; }

        // Node
        public int getNbNodes() { return this.nodes.Length; }
        public Node[] getNodes() { return this.nodes; }
        public Node getNode(int index) { return this.nodes[index]; }
        public int getNodeIndexByLabel(string label) { for (int i = 0; i < this.nodes.Length; i++) { if (this.nodes[i].getLabel() == label) { return i; } } return -1; }

        // Arc
        public int getNbArcs() { return this.arcs.Length; }
        public Arc[] getArcs() { return this.arcs; }
        public Arc getArc(int index) { return this.arcs[index]; }
        public Arc getArc(int fromIndex, int toIndex) { return this.arcs[incidenceMatrix[fromIndex, toIndex]]; }
        public int getArcIndexByLabel(string label) { for (int i = 0; i < this.arcs.Length; i++) { if (this.arcs[i].getLabel() == label) { return i; } } return -1; }

        // Path
        public int getNbPaths() { return this.paths.Length; }
        public Path[] getPaths() { return this.paths; }
        public Path getPath(int index) { return this.paths[index]; }
        public int getPathIndexByLabel(string label) { for (int i = 0; i < this.paths.Length; i++) { if (this.paths[i].getLabel() == label) { return i; } } return -1; }


        //COMMON
        public override string ToString() { return GrStr.toString(this); }
        public virtual List<string> toArgs() { return GraphString.toArgs(this); }
        public virtual string toString() { return GraphString.toString(this); }
        


        // EXCEPTIONS
        public class InvalidGraphException : Exception { public InvalidGraphException(string message) : base("Invalid graph: " + message + ".") { } }
    }
}
