using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Graphs;
using Matching;
using System.Windows.Forms;

namespace Matching
{
    class Glpk_Controller
    {
        #region Attributes
        /*******************************************************************************************/
        /****************************************Attributes******************************************/
        /*******************************************************************************************/

        Graph graph1;  //graph original
        Graph graph2;  //graph original
        Glpk_Solver glpk_solver = null;
        
        //if the graph original is undirected, then we transform it to the directedGraph
        Graph directedGraph1 = null; 
        Graph directedGraph2 = null;
        Graph isoGraph = null;
        double executeTime=-1;  //execute time of solving a certain isomorphism problem (millisecond)
        double optimalValue; //the optimal value of the PLNE
        #endregion


        #region Properties
        /*******************************************************************************************/
        /***************************************Properties******************************************/
        /*******************************************************************************************/
        public Graph Graph1
        {
            get { return graph1; }
            set { graph1 = value; }
        }
        public Graph Graph2
        {
            get { return graph2; }
            set { graph2 = value; }
        }
        public Graph DirectedGraph1
        {
            get { return directedGraph1; }
            set { directedGraph1 = value; }
        }
        public Graph DirectedGraph2
        {
            get { return directedGraph2; }
            set { directedGraph2 = value; }
        }
        public Graph IsoGraph
        {
            get { return isoGraph; }
            set { isoGraph = value; }
        }
        public double ExecuteTime
        {
            get { return executeTime;  }
            set { executeTime = value; }
        }
        public double OptimalValue
        {
            get { return optimalValue; }
            set { optimalValue = value; }
        }
        

        #endregion

        /// <summary>
        ///  constructor
        /// </summary>
        public Glpk_Controller()
        {
 
        }

        /// <summary>
        ///  transform a undirected graph to a directed graph
        /// </summary>
        /// <param name="g">the graph to be transformed</param>
        public void transToDirectedGraph(Graph g)
        {
            List<Edge> oldListEdges = new List<Edge>();
            oldListEdges.AddRange(g.ListEdges);
            if (g.IsDirected == false)
            {
                g.IsDirected = true;
                foreach (Node n in g.ListNodes)
                {
                    n.ListEdgesOut.Clear();
                    n.ListEdgesIn.Clear();
                }

                foreach (Edge e in oldListEdges)
                {
                    e.NodeSource.ListEdgesOut.Add(e);
                    e.NodeTarget.ListEdgesIn.Add(e);

                    Edge edge = new Edge("copy_" + e.Id, e.NodeTarget, e.NodeSource, null);
                    edge.AttributesGXL.AddRange(e.AttributesGXL);
                    Type typeEdge = e.Label.GetType();
                    object obj = Activator.CreateInstance(typeEdge);
                    edge.Label = (Graphs.Label)obj;
                    edge.Label.Id = edge.Id;
                    if (edge.AttributesGXL.Count != 0) edge.Label.fromAttributes(edge.AttributesGXL);
                    edge.Label.Id = edge.Id;
                    g.addEdge(edge);

                }
            }

        }
       
        /// <summary>
        ///  Call the solver GLPK
        /// </summary>
        /// <param name="exact">indicating the method to use is exact solve or not</param>
        /// <param name="status"></param>
        /// <param name="ResultText"></param>
        /// <param name="lib_controller">the controller of the lib Graph</param>
        public void Call_GlPK(bool exact, ToolStripStatusLabel status, string ResultText, Lib_Controller lib_controller)
        {
            status.Text = " ";
            if (graph1 == null) MessageBox.Show("please load graph1");
            else if (graph2 == null) MessageBox.Show("please load graph2");
            else if (graph1.IsDirected ^ graph2.IsDirected == true) MessageBox.Show("Can't Solve!");
            else
            {
                try
                {
                    //exact solve
                    if (exact == true)
                    {
                        if (directedGraph1.ListNodes.Count() <= directedGraph2.ListNodes.Count() && directedGraph1.ListEdges.Count() <= directedGraph2.ListEdges.Count())
                        {
                            List<string[]> Iso_Nodes = exact_solve(directedGraph1, directedGraph2,ResultText);
                            if (Iso_Nodes.Count() != 0)
                            isoGraph = lib_controller.CreateIsoGraph(Graph1, Graph2, Iso_Nodes);
                        }
                            

                        else if (directedGraph1.ListNodes.Count() >= directedGraph2.ListNodes.Count() && directedGraph1.ListEdges.Count() >= directedGraph2.ListEdges.Count())
                        {
                            List<string[]> Iso_Nodes = exact_solve(directedGraph2, directedGraph1, ResultText);
                            if (Iso_Nodes.Count() != 0)
                            isoGraph = lib_controller.CreateIsoGraph(Graph2, Graph1, Iso_Nodes);
                        }
                        else
                        {
                            ResultText="";
                            ResultText="Impossible to Solve!";
                        }

                    }
                    //inexact solve
                    else
                    {
                        List<string[]> Iso_Nodes = inexact_solve(directedGraph1, directedGraph2, ResultText);
                        if (Iso_Nodes.Count() != 0)
                        isoGraph = lib_controller.CreateIsoGraph(Graph1, Graph2, Iso_Nodes);
                    }
                }
                catch (Exception exc)
                {
                    ResultText+="exc.Message";
                }
            }
        }

        /// <summary>
        ///  solve the problem using exact method
        /// </summary>
        /// <param name="directedG1">the graph 1</param>
        /// <param name="directedG2">the graph 2</param>
        /// <param name="ResultText"></param>
        public List<string[]> exact_solve(Graph directedG1, Graph directedG2, string ResultText)
        {
            List<string[]> matching_nodes = new List<string[]>();
            TimeSpan start = new TimeSpan(DateTime.Now.Ticks);           
            glpk_solver = new Glpk_Exact_Solve(directedG1, directedG2, ResultText);
            glpk_solver.constractProblem();
            glpk_solver.solve_problem();
            TimeSpan end = new TimeSpan(DateTime.Now.Ticks);
            TimeSpan usingtime = end.Subtract(start).Duration();
            //executeTime = usingtime.ToString();
            executeTime = usingtime.TotalMilliseconds;
            optimalValue = glpk_solver.getObjResVal();
            glpk_solver.print_sol(matching_nodes, ResultText);
            ResultText+="time used: " + executeTime + " ms\n";
            return matching_nodes;
            
        }

        /// <summary>
        ///  solve the problem using inexact method
        /// </summary>
        /// <param name="directedG1">the graph 1</param>
        /// <param name="directedG2">the graph 2</param>
        /// <param name="ResultText"></param>
        public List<string[]> inexact_solve(Graph directedG1, Graph directedG2, string ResultText)
        {
            List<string[]> matching_nodes = new List<string[]>();
            TimeSpan start = new TimeSpan(DateTime.Now.Ticks);
            glpk_solver = new Glpk_Inexact_Solve(directedG1, directedG2, ResultText);
            glpk_solver.constractProblem();
            glpk_solver.solve_problem();
            TimeSpan end = new TimeSpan(DateTime.Now.Ticks);
            TimeSpan usingtime = end.Subtract(start).Duration();
            //executeTime = usingtime.ToString();
            executeTime = usingtime.TotalMilliseconds;
            optimalValue = glpk_solver.getObjResVal();
            glpk_solver.print_sol(matching_nodes, ResultText);
            ResultText+="time used: " + executeTime + " ms\n";
            return matching_nodes;
        }

        /// <summary>
        ///  Save the problem in CLPLEX format to text file
        /// </summary>
        public void saveProblem(ToolStripStatusLabel status)
        {
            if (glpk_solver == null)
                MessageBox.Show("call the solver first!");
            else
            {
                SaveFileDialog mySave = new SaveFileDialog();
                string path = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
                mySave.InitialDirectory = path + "\\save_to_files";
                mySave.RestoreDirectory = true;
                mySave.Filter = "Text Files (*.txt)|*.txt";
                mySave.ShowDialog();
                string nomFichier = mySave.FileName;
                if (nomFichier != "")
                {
                    glpk_solver.problemSave(nomFichier);
                    status.Text = "Saved with success";
                }
                else status.Text = "failed to save the problem";
            }
        }

        /// <summary>
        ///  Save the result in GLPK format to text file
        /// </summary>
        public void saveResult(ToolStripStatusLabel status)
        {
            if (glpk_solver == null)
                MessageBox.Show("call the solver first!");
            else
            {
                string strPath = System.IO.Directory.GetCurrentDirectory();
                System.IO.Directory.SetCurrentDirectory(strPath);
                SaveFileDialog mySave = new SaveFileDialog();
                string path = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
                mySave.InitialDirectory = path + "\\save_to_files";
                mySave.RestoreDirectory = true;
                mySave.Filter = "Text Files (*.txt)|*.txt";
                mySave.ShowDialog();
                string nomFichier = mySave.FileName;
                if (nomFichier != "")
                {
                    glpk_solver.solutionSave(nomFichier);
                    status.Text = "Saved with success.";
                }
                else status.Text = "failed to save the result";
            }
        }

        /// <summary>
        ///  let all the graph pointors of the controller point to null
        /// </summary>
        public void clearGraphs()
        {
            this.Graph1 = null;
            this.Graph2 = null;
            this.DirectedGraph1 = null;
            this.DirectedGraph2 = null;
            this.IsoGraph = null;
        }

    }
}
