using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;
using Matching;
using MatrixLibrary;
using Graphs;
namespace Matching
{
    unsafe public abstract class Glpk_Solver 
    {

        #region Attributes
        /*******************************************************************************************/
        /****************************************Attributes******************************************/
        /*******************************************************************************************/


        protected double* lp;
        protected Graph g1;
        protected Graph g2;
        Dictionary<Node, Node> nodeMatching;

        public Dictionary<Node, Node> NodeMatching
        {
            get { return nodeMatching; }
            set { nodeMatching = value; }
        }

        protected const string glpkLibrary = "glpk_4_49_86";
        protected readonly int GLP_BV = 3; /*binary variable*/

        protected readonly int GLP_LO = 2;  /* lower bound */
        protected readonly int GLP_UP = 3; /* upper bound */
        protected readonly int GLP_DB = 4; /* double-bounded */
        protected readonly int GLP_FX = 5; /* fixed */

        protected readonly int GLP_UNDEF = 1;  /* solution is undefined */
        protected readonly int GLP_FEAS = 2;   /* solution is feasible */
        protected readonly int GLP_INFEAS = 3; /* solution is infeasible */
        protected readonly int GLP_NOFEAS = 4; /* no feasible solution exists */
        protected readonly int GLP_OPT = 5;     /* solution is optimal */
        protected readonly int GLP_UNBND = 6;   /* solution is unbounded */
        private double totalcost;

        public double Totalcost
        {
            get { return totalcost; }
            set { totalcost = value; }
        }

        protected struct ConstraintMatrix
        {
            public int[] ia;
            public int[] ja;
            public double[] ar;
        }
        #endregion

        #region DLLImport
        /*******************************************************************************************/
        /****************************************DLLImport******************************************/
        /*******************************************************************************************/

        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern double* glp_create_prob();
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern void glp_set_prob_name(double *P, string name);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern void glp_add_rows(double* lp, int rows);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern void glp_add_cols(double* lp, int cols);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern void glp_set_col_bnds(double* lp, int col, int bound_type, double lower_bound, double upper_bound);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern void glp_set_col_kind(double* lp, int col, int kind);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern void glp_set_obj_dir(double* lp, int dir);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern void glp_load_matrix(double* lp, int elements, int[] ia, int[] ja, double[] ar);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern void glp_set_col_name(double* P, int j, string name);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern void glp_set_row_name(double* P, int i, string name);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern void glp_set_row_bnds(double* P, int i, int type, double lb, double ub);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern void glp_set_obj_coef(double* P, int j, double coef);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern void glp_create_index(double* P);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern int glp_find_col(double* P, string name);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        static extern int glp_simplex(double* P, void* parm);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        static extern double glp_get_obj_val(double* P);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        static extern double glp_get_obj_coef(double* P, int j);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern double glp_get_col_prim(double* P, int j);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true, CharSet = CharSet.Ansi)]
        //[return: MarshalAs(UnmanagedType.LPStr)]
        protected static extern IntPtr glp_get_col_name(double* P, int j);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern int glp_get_num_cols(double* P);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern void glp_delete_prob(double* lp);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern int glp_write_prob(double* P, int flags, string fname);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern int glp_print_mip(double* P, string fname);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern int glp_get_col_kind(double* P, int j);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern int glp_get_status(double* P);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern int glp_get_num_int(double* P);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern int glp_get_num_bin(double* P);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern int glp_intopt(double* P, void* parm);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern double glp_mip_obj_val(double* P);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern double glp_mip_row_val(double* P, int i);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern double glp_mip_col_val(double* P, int j);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern int glp_mip_status(double* P);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern int glp_write_lp(double* P, void* parm, string fname);
        [DllImport(glpkLibrary, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        protected static extern void glp_init_iocp(void* parm);

        #endregion

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

        /// <summary>
        ///  constructor
        ///  Construct the problem and then solve it
        /// </summary>
        public Glpk_Solver(Graph G1, Graph G2, string ResultText)
        {

            NodeMatching = new Dictionary<Node, Node>();
            this.g1 = G1;
            this.g2 = G2;
           
            ResultText="";
            ResultText+="Solver GLPK : 4.49\n";
            lp = glp_create_prob();
        }

        public bool init(Graph G1, Graph G2, string ResultText)
        {

            NodeMatching = new Dictionary<Node, Node>();
            this.g1 = G1;
            this.g2 = G2;

            ResultText = "";
            ResultText += "Solver GLPK : 4.49\n";
            lp = glp_create_prob();
            return true;
        }


        /// <summary>
        ///  destructor
        /// </summary>
        ~Glpk_Solver()
        {
            if (lp != null)
                glp_delete_prob(lp);
        }


        //should be extended
        //constract the lp problem (objectif function and the matrix of constraints)
        public abstract void constractProblem();

        /// <summary>
        ///  call the solve method to solve the problem transformed as a parameter
        /// </summary>
        public int solve_problem()
        {
            int num_int = glp_get_num_int(lp);
            int num_bin = glp_get_num_bin(lp);
            Console.WriteLine("num_int : " + num_int.ToString());
            Console.WriteLine("num_bin : " + num_bin.ToString());

            glp_simplex(lp, null);
            return glp_intopt(lp, null);

        }
        /// <summary>
        ///  get the result value of the function objective       
        /// </summary>
        /// <returns> the objective value
        /// if the problem has no bojective value, return -1
        /// </returns>
        public double getObjResVal()
        {

            if (lp != null)
            {
                int status = glp_mip_status(lp);
                if (status == GLP_OPT || status == GLP_FEAS)
                {
                    double minimum = glp_mip_obj_val(lp);
                    if (minimum < 0.000001 && minimum > -0.000001) minimum = 0;
                    return minimum; 
                }              
 
            }
            return -1;
        }


        /// <summary>
        ///  print the result to the richText of the mainWindow
        /// </summary>
        /// <param name="MatchingNodes"></param>
        /// <param name="ResultText"></param>
        public void print_sol(List<string[]> MatchingNodes, string ResultText)
        {
            int status = glp_mip_status(lp);

            if (status == GLP_NOFEAS)
            {
                ResultText+="no feasible solution exists\n";
            }
            else if (status == GLP_UNDEF)
               ResultText+="solution is undefined \n";
            else if (status == GLP_INFEAS)
                ResultText+="solution is infeasible\n ";
            else if (status == GLP_UNBND)
                ResultText+="solution is unbounded\n ";
            else if (status == GLP_OPT || status == GLP_FEAS)
            {                
                //if (this.g1.ListNodes.Count() == this.g2.ListNodes.Count() && this.g1.ListEdges.Count() == this.g2.ListEdges.Count())
                   // ResultText.AppendText(this.g1.Name + " is the isomorphisme of " + this.g2.Name + "\n");
                //else
                  //  ResultText.AppendText(this.g1.Name + " is the sous-isomorphisme of " + this.g2.Name + "\n");
                double ress;
                double minimum = glp_mip_obj_val(lp);
               
                if (minimum < 0.000001 && minimum > -0.000001) minimum = 0;
                this.totalcost = minimum;
                ResultText+="the minimum value of the objective function: " + minimum.ToString() + "\n";
                ResultText+="///////////\n";

                string valName;
                double distance = -1.0;
                for (int i = 1; i <= (glp_get_num_cols(lp)); i++)
                {
                    ress = glp_mip_col_val(lp, i);
                    valName = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(glp_get_col_name(lp, i));

                    if (ress == 1)
                    {
                        if (valName.StartsWith("x_"))
                        {
                            string[] res = seperateValName(valName);
                            MatchingNodes.Add(res);
                            ResultText+="Nodes: " + this.g1.Name + "#" + res[0] + " ----------> " + this.g2.Name + "#" + res[1] + "\n";
                            distance = glp_get_obj_coef(lp, i);
                            ResultText+="distance: " + distance.ToString() + "\n\n";
                            //distance = -1.0;
                            Node source = g1.findNode(res[0]);
                            Node target = g1.findNode(res[1]);
                            nodeMatching.Add(source, target);
                        }
                        else if (valName.StartsWith("y_"))
                        {
                            string[] res = seperateValName(valName);
                            if (res[0].StartsWith("copy_") == false)
                            {
                                if (res[1].StartsWith("copy_"))
                                    res[1] = res[1].Substring(5);
                                ResultText+="Edges: " + g1.Name + "#" + res[0] + " ----------> " + g2.Name + "#" + res[1] + "\n";
                                distance = glp_get_obj_coef(lp, i);
                                ResultText+="distance: " + distance.ToString() + "\n\n";
                                //distance = -1.0;
                               // Node source = g1.findNode(res[0]);
                               // Node target = g1.findNode(res[1]);
                               // nodeMatching.Add(source, target);
                            }
                        }
                        else if (valName.StartsWith("u_"))
                        {
                            string[] res = seperateValName(valName);
                            ResultText+="Nodes: " + this.g1.Name + "#" + res[0] + " is lonely..\n";
                            Node source = g1.findNode(res[0]);
                            Node target = new Node(ConstantsAC.EPS_ID);
                            nodeMatching.Add(source, target);
                        }
                        else if (valName.StartsWith("e_"))
                        {
                            string[] res = seperateValName(valName);
                            if (res[0].StartsWith("copy_") == false)
                            {
                                ResultText+="Edges: " + g1.Name + "#" + res[0] + " is lonely\n";

                            }
                        }
                        
                        
                    }

                }


            }
        }

        /// <summary>
        ///  small tool function. Analysing the name of a variable
        ///  for exemple: valName="x_node1,node3", the function return a table of 2 elements: "node1", "node3"
        /// </summary>
        /// <param name="valName"></param>
        private string[] seperateValName(string valName)
        {
            string[] result;
            valName = valName.Substring(2);
            result = valName.Split(',');
            return result;
        }

        /// <summary>
        ///  save the problem in CPLEX format into a text file.
        /// </summary>
        /// <param name="fname"></param>
        public int problemSave(string fname)
        {
            return glp_write_lp(this.lp, null, fname);
        }

        /// <summary>
        ///  save the solve result into a text file.
        /// </summary>
        /// <param name="fname"></param>
        public int solutionSave(string fname)
        {
            return glp_print_mip(this.lp, fname);
        }

       

       

    }
}
