using SeSue.Classes;
using SeSue.DGVs;
using SeSue.FormClasses.Dgv;
using SeSue.Forms.Common;
using SeSue.Forms.PathGeneration;
using SeSue.Forms.Wizards;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using U.IO;
using U.StringOperations;
using U.SUE;
using U.SUE.Algorithms;

namespace SeSue.Forms
{
    public partial class SueForm : Form
    {
        // COLORS
        public static ColorConverter cc = new ColorConverter();
        public static Color editableColor = Color.White;
        public static Color readOnlyColor = (Color)cc.ConvertFromString("#C0C7C7");// Color.GhostWhite;
        public static Color labelColor = (Color)cc.ConvertFromString("#0F3650");
        public static Color errorColor = Color.IndianRed;
        public static Color regularFontColor = SystemColors.ControlText;
        public static Color gridColor = (Color)cc.ConvertFromString("#CACAD4");
        public static int headerVertPadding = 7;
        public static int rowHeight = 25;


        // Properties
        private string filepath;
        public static Sue sue;
        public static List<List<int>> odPathDtIndices;
        private List<string> exportLines;

        public static StatDgv statDgv;
        public static NodesDgv nodesDgv;
        public static LinkCostsDgv linkCostsDgv;
        public static LinkSueDgv linkSueDgv;
        public static OdDgv odDgv;
        public static PathDgv pathDgv;
        public static MsaDgv msaDgv;

        public static DataTable statDt;
        public static DataTable nodesDt;
        public static DataTable linkCostsDt;
        public static DataTable linkSueDt;
        public static DataTable odDt;
        public static DataTable pathDt;
        public static DataTable msaDt;

        // Parameters
        public static double theta = 0.1;
        public static double eta = 1.0;//proportionality
        public static double nu = 0.3;//coef of var
        public static double beta0 = 0.1;//commonality factor
        public static double gamma = 1.0;//commonality factor
        public static double beta = 3.7;//weibull shape
        public static double xi = 0.0; //weibull location
        public static double varEpsilon = 0.0001;//mdm precision


        private string message;
        private List<string> sueLines;


        #region CONSTURCTOR
        public SueForm() { construct(); }
        public SueForm(string filepath)
        {
            construct();
            string strSue = SueFile.getAllTextFromFile(filepath);
            this.bgwRead.RunWorkerAsync(strSue); Progress.setText("Reading SUE file."); Progress.show(this);
            if (this.message != null) { Msg.show("Error", this.message, this); this.message = null; return; }
            this.message = null;
            this.filepath = filepath;
            newSueAfter();
        }
        private void construct()
        {
            InitializeComponent();
            this.KeyPreview = true;
            this.Text = "seSue";
            this.Icon = Properties.Resources.logo;
            sue = new Sue(null, new MSA(new StepSizeStandard(1.0, 0.0), 100, 0.01));
            btnMsaProps.Text = sue.msa.getLabel();
            statDgv = new StatDgv(splitGraph1.Panel1);
            nodesDgv = new NodesDgv(tabNodes);
            linkCostsDgv = new LinkCostsDgv(tabLinkCost);
            linkSueDgv = new LinkSueDgv(tabLinkSue);
            odDgv = new OdDgv(tabOd);
            pathDgv = new PathDgv(splitPaths.Panel2);
            msaDgv = new MsaDgv(splitMsa.Panel2);
            newSue();
            
        } 
        #endregion

        private void newSue()
        {
            statDgv.newSue();
            nodesDgv.newSue();
            linkCostsDgv.newSue();
            linkSueDgv.newSue();
            odDgv.newSue();
            string selOd = String.Empty;
            if (sue.graph != null) { if (sue.graph.getNbOdPairs() > 0) { selOd = sue.graph.getOdPair(0).getLabel(); } }
            pathDgv.newSue(selOd, String.Empty, String.Empty);
            msaDgv.afterRun();
        }
        private void newSueAfter()
        {
            this.Text = "seSue"; if (filepath != null) { this.Text = "seSue  -  " + System.IO.Path.GetFileName(filepath); }
            statDgv.dgv.setDataTable(statDt);
            nodesDgv.dgv.setDataTable(nodesDt);
            linkCostsDgv.dgv.setDataTable(linkCostsDt);
            linkSueDgv.dgv.setDataTable(linkSueDt);
            odDgv.dgv.setDataTable(odDt);
            pathDgv.dgv.setDataTable(pathDt);
            msaDgv.dgv.setDataTable(msaDt);
            string selOd = String.Empty;
            if (sue.graph != null) { if (sue.graph.getNbOdPairs() > 0) { selOd = sue.graph.getOdPair(0).getLabel(); } }
            cbPaths_OdPairs.Text = selOd;
            tbPaths_Nodes.Text = String.Empty;
            tbPaths_Links.Text = String.Empty;
            cbPaths_OdPairs.Items.Clear();
            foreach (OdPair od in sue.graph.getOdPairs()) { cbPaths_OdPairs.Items.Add(od.getLabel()); }
            btnMsaProps.Text = sue.msa.getLabel();
        }



        #region WIZARDS
        private void logitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (sue.graph == null) { return; }
            LogitWizard frm = new LogitWizard(this.Width, this.Height);
            frm.ShowDialog(this);
        }
        private void weibitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (sue.graph == null) { return; }
            WeibitWizard frm = new WeibitWizard(this.Width, this.Height);
            frm.ShowDialog(this);
        }
        private void mDMToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (sue.graph == null) { return; }
            MdmExpWizard frm = new MdmExpWizard(this.Width, this.Height);
            frm.ShowDialog(this);
        }
        private void mDMWithNormalMDsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (sue.graph == null) { return; }
            MdmNorWizard frm = new MdmNorWizard(this.Width, this.Height);
            frm.ShowDialog(this);
        } 
        #endregion

        #region HOT KEYS
        private void SueForm_KeyDown(object sender, KeyEventArgs e)
        {
            // SAVE AS
            if (e.Control && e.Alt && e.KeyCode == Keys.S) { menuSaveAs_Click(sender, e); e.SuppressKeyPress = true; }
            // SAVE
            else if (e.Control && e.KeyCode == Keys.S) { menuSave_Click(sender, e); e.SuppressKeyPress = true; }
            // OPEN
            else if (e.Control && e.KeyCode == Keys.O) { menuOpen_Click(sender, e); e.SuppressKeyPress = true; }

        } 
        #endregion

        #region PATH GENERATION
        private int[] closeAllPgaForms()
        {
            int[] whtl = new int[4]; whtl[0] = this.Width; whtl[1] = this.Height; whtl[2] = this.Top; whtl[3] = this.Left;
            List<Form> pgaForms = new List<Form>();
            foreach (Form frm in Application.OpenForms) { if (frm.GetType() == typeof(PGA)) { pgaForms.Add(frm); } }
            if (pgaForms.Count != 0) { whtl[0] = pgaForms[0].Width; whtl[1] = pgaForms[0].Height; whtl[2] = pgaForms[0].Top; whtl[3] = pgaForms[0].Left; }
            foreach (Form frm in pgaForms) { frm.Close(); }
            pgaForms.Clear();
            return whtl;
        }
        private void yensKShortestPathAlgorithmToolStripMenuItem_Click(object sender, EventArgs e) { if (sue.graph == null) { return; } int[] whtl = closeAllPgaForms(); PGA pga = PgaInitializer.getYensPga(sue.graph, whtl[0], whtl[1], whtl[2], whtl[3]); pga.ShowDialog(this); }
        private void dialsAlgorithmToolStripMenuItem_Click(object sender, EventArgs e) { if (sue.graph == null) { return; } int[] whtl = closeAllPgaForms(); PGA pga = PgaInitializer.getDialsPga(sue.graph, whtl[0], whtl[1], whtl[2], whtl[3]); pga.ShowDialog(this); }
        private void linkPenaltyToolStripMenuItem_Click(object sender, EventArgs e) { if (sue.graph == null) { return; } int[] whtl = closeAllPgaForms(); PGA pga = PgaInitializer.getLinkPenaltyPga(sue.graph, whtl[0], whtl[1], whtl[2], whtl[3]); pga.ShowDialog(this); }
        private void linkEliminationToolStripMenuItem_Click(object sender, EventArgs e) { if (sue.graph == null) { return; } int[] whtl = closeAllPgaForms(); PGA pga = PgaInitializer.getLinkEliminationPga(sue.graph, whtl[0], whtl[1], whtl[2], whtl[3]); pga.ShowDialog(this); }
        private void linkPenaltyLinkEliminationToolStripMenuItem_Click(object sender, EventArgs e) { if (sue.graph == null) { return; } int[] whtl = closeAllPgaForms(); PGA pga = PgaInitializer.getLinkPenElimPga(sue.graph, whtl[0], whtl[1], whtl[2], whtl[3]); pga.ShowDialog(this); }
        private void linkEliminationLinkPenaltyToolStripMenuItem_Click(object sender, EventArgs e) { if (sue.graph == null) { return; } int[] whtl = closeAllPgaForms(); PGA pga = PgaInitializer.getLinkElimPenPga(sue.graph, whtl[0], whtl[1], whtl[2], whtl[3]); pga.ShowDialog(this); } 
        #endregion

        #region READ
        private void menuOpen_Click(object sender, EventArgs e)
        {
            string[] arr = SueFile.getFilenameAndAllText(); if (arr[0] == null) { return; }
            //Algorithms.Timer timer = new Algorithms.Timer(); timer.start();
            this.bgwRead.RunWorkerAsync(arr[1]); Progress.setText("Reading SUE file."); Progress.show(this);
            if (this.message != null) { Msg.show("Error", this.message, this); this.message = null; return; }
            this.message = null;
            filepath = arr[0];
            newSueAfter();
            //timer.stop(); MessageBox.Show(timer.getMiliseconds().ToString());
        }
        private void bgwRead_DoWork(object sender, DoWorkEventArgs e)
        {
            string strSue = (string)e.Argument;
            this.message = null;
            try
            {
                sue = new Sue(strSue);
                newSue();
            }
            catch (Exception) { this.message = "Invalid graph file."; }
            System.Threading.Thread.Sleep(100);
        }
        private void bgwRead_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { Progress.hide(); } 
        #endregion

        #region SAVE
        private void menuSave_Click(object sender, EventArgs e)
        {
            if (sue.graph == null) { return; }
            this.sueLines = null;
            bgwSave.RunWorkerAsync(); Progress.setText("Creating SUE file."); Progress.show(this);
            OFile.create(filepath, this.sueLines);
            Msg.show("Saved.", filepath + " saved.", this);
            this.sueLines = null;
        }
        private void menuSaveAs_Click(object sender, EventArgs e)
        {
            if (sue.graph == null) { return; }
            this.sueLines = null;
            bgwSave.RunWorkerAsync(); Progress.setText("Creating SUE file."); Progress.show(this);
            string path = SueFile.createAndGetFilename(this.sueLines);
            if (path != String.Empty)
            {
                this.filepath = path;
                this.Text = "seSue - " + System.IO.Path.GetFileName(path);
                Msg.show("Saved.", filepath + " saved.", this);
            }
            this.sueLines = null;
        }
        private void bgwSave_DoWork(object sender, DoWorkEventArgs e) { this.sueLines = SueWriter.getLines(sue); }
        private void bgwSave_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { Progress.hide(); } 
        #endregion

        #region MSA
        private void btnMsaProps_Click(object sender, EventArgs e) { if (sue == null) { return; } if (sue.graph == null) { return; } MsaForm frm = new MsaForm(sue.msa, this.Width, this.Height); frm.ShowDialog(this); if (MsaForm.newMsaCreated) { sue.msa = MsaForm.newMsa; btnMsaProps.Text = sue.msa.getLabel(); } }
        private void btnMsaRun_Click(object sender, EventArgs e)
        {
            if (sue.graph == null) { return; }
            if (!sue.msa.areAllChoiceModelsSet(sue.graph)) { Msg.show("Error.", "Choice model of at least one OD pair is not defined.", this); return; }
            bgwMsa.RunWorkerAsync(); Progress.setText("MSA algorithm is running."); Progress.show(this);
            msaDgv.dgv.setDataTable(msaDt);
            linkSueDgv.dgv.setDataTable(linkSueDt);
            pathDgv.newSue(cbPaths_OdPairs.Text, tbPaths_Nodes.Text, tbPaths_Links.Text);
            pathDgv.dgv.setDataTable(pathDt);
            Msg.show("MSA Algorithm.", this.message, this);
        }
        private void bgwMsa_DoWork(object sender, DoWorkEventArgs e)
        {
            sue.msa.run(sue.graph, true);
            msaDgv.afterRun();
            this.message = "Running time is " + sue.msa.getSolutionMiliseconds().ToString("###,###,###,##0") + " miliseconds.";
            this.message = this.message + Environment.NewLine + sue.msa.getNbIterations().ToString("#,###,##0") + " iterations have been executed until convergence.";
            this.message = this.message + Environment.NewLine + sue.msa.getMdmLambda().ToString();
        }
        private void bgwMsa_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { Progress.hide(); }


        private void btnPathsFilter_Click(object sender, EventArgs e)
        {
            pathDgv.newSue(cbPaths_OdPairs.Text, tbPaths_Nodes.Text, tbPaths_Links.Text);
            pathDgv.dgv.setDataTable(pathDt);
        } 
        #endregion

        #region EXPORT
        private void graphStatisticsToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            if (sue == null) { return; } if (sue.graph == null) { return; }
            this.exportLines = null;
            bgwExport.RunWorkerAsync(statDgv.dgv); Progress.setText("Creating file."); Progress.show(this);
            string path = OTextFile.createAndGetFilename(this.exportLines);
            if (path != String.Empty) { Msg.show("Export.", path + " created.", this); this.exportLines = null; }
        }
        private void nodesToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            if (sue == null) { return; } if (sue.graph == null) { return; }
            this.exportLines = null;
            bgwExport.RunWorkerAsync(nodesDgv.dgv); Progress.setText("Creating file."); Progress.show(this);
            string path = OTextFile.createAndGetFilename(this.exportLines);
            if (path != String.Empty) { Msg.show("Export.", path + " created.", this); this.exportLines = null; }
        }
        private void linkCostsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (sue == null) { return; } if (sue.graph == null) { return; }
            this.exportLines = null;
            bgwExport.RunWorkerAsync(linkCostsDgv.dgv); Progress.setText("Creating file."); Progress.show(this);
            string path = OTextFile.createAndGetFilename(this.exportLines);
            if (path != String.Empty) { Msg.show("Export.", path + " created.", this); this.exportLines = null; }
        }
        private void linkSUEToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            if (sue == null) { return; } if (sue.graph == null) { return; }
            this.exportLines = null;
            bgwExport.RunWorkerAsync(linkSueDgv.dgv); Progress.setText("Creating file."); Progress.show(this);
            string path = OTextFile.createAndGetFilename(this.exportLines);
            if (path != String.Empty) { Msg.show("Export.", path + " created.", this); this.exportLines = null; }
        }
        private void oDPairsToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            if (sue == null) { return; } if (sue.graph == null) { return; }
            this.exportLines = null;
            bgwExport.RunWorkerAsync(odDgv.dgv); Progress.setText("Creating file."); Progress.show(this);
            string path = OTextFile.createAndGetFilename(this.exportLines);
            if (path != String.Empty) { Msg.show("Export.", path + " created.", this); this.exportLines = null; }
        }
        private void pathsToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            if (sue == null) { return; } if (sue.graph == null) { return; }
            this.exportLines = null;
            bgwExportPath.RunWorkerAsync(); Progress.setText("Creating file."); Progress.show(this);
            string path = OTextFile.createAndGetFilename(this.exportLines);
            if (path != String.Empty) { Msg.show("Export.", path + " created.", this); this.exportLines = null; }
        }
        private void mSASolutionToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (sue == null) { return; } if (sue.graph == null) { return; }
            this.exportLines = null;
            bgwExport.RunWorkerAsync(msaDgv.dgv); Progress.setText("Creating file."); Progress.show(this);
            string path = OTextFile.createAndGetFilename(this.exportLines);
            if (path != String.Empty) { Msg.show("Export.", path + " created.", this); this.exportLines = null; }
        }
        private void bgwExport_DoWork(object sender, DoWorkEventArgs e) { Dgv dgv = (Dgv)e.Argument; this.exportLines = dgv.getDtAsLines(); }
        private void bgwExport_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { Progress.hide(); }
        private void bgwExportPath_DoWork(object sender, DoWorkEventArgs e)
        {
            this.exportLines = new List<string>();
            string line = "w" + '\t' + "k" + '\t' + "Label" + '\t' + "# Arcs" + '\t' + "Marginal Dist." + '\t' + "SUE Cost" + '\t' + "SUE Ch. Prob." + '\t' + "SUE Flow";
            this.exportLines.Add(line);
            for (int w = 0; w < sue.graph.getNbOdPairs(); w++)
            {
                OdPair od = sue.graph.getOdPair(w);
                for (int k = 0; k < od.getPathIndices().Length; k++)
                {
                    Path path = sue.graph.getPath(od.getPathIndices()[k]);
                    line = w.ToString() + '\t' + k.ToString() + '\t' + path.getLabel() + '\t' + path.getArcIndices().Length.ToString() + '\t';
                    string dist = String.Empty;
                    if (od.getChoiceModel() != null)
                    {
                        if (od.getChoiceModel().getType() == DiscreteChoiceModel.ChoiceModel.Type.MDM)
                        {
                            DiscreteChoiceModel.MDM mdm = (DiscreteChoiceModel.MDM)od.getChoiceModel();
                            if (mdm.getMarginalDistributions() != null)
                            {
                                if (mdm.getMarginalDistributions()[k] != null)
                                {
                                    dist = mdm.getMarginalDistributions()[k].ToString();
                                }
                            }
                        }
                    }
                    line = line + dist + '\t' + path.getCost().ToString() + '\t' + path.getProb().ToString() + '\t' + path.getFlow().ToString();
                    this.exportLines.Add(line);
                }
            }
        }
        private void bgwExportPath_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { Progress.hide(); }
        #endregion

        #region NETWORK VISUALIZATION
        private void networkVisualizationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (sue == null) { return; } if (sue.graph == null) { return; }
            Visualization.Visualization frm = new Visualization.Visualization(sue.graph, this.Width, this.Height, this.Left, this.Top, this.WindowState);
            frm.ShowDialog(this);
        } 
        #endregion

        #region SENSITIVITY
        private void perturbODDemandToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (sue == null) { return; } if (sue.graph == null) { return; }
            if (!sue.msa.areAllChoiceModelsSet(sue.graph)) { Msg.show("Model is not ready for MSA.", "Choice models of at least one OD pair is not set.", this); return; }
            Sensitivity.SensitivityAnalysis frm = new Sensitivity.SensitivityAnalysis("demand", this.Width, this.Height, this.WindowState); frm.ShowDialog(this);
            linkSueDgv.newSue(); linkSueDgv.dgv.setDataTable(linkSueDt);
            pathDgv.newSue(cbPaths_OdPairs.Text, tbPaths_Nodes.Text, tbPaths_Links.Text); pathDgv.dgv.setDataTable(pathDt);
        }
        private void perturbLinkFreeFlowTravelTimeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (sue == null) { return; } if (sue.graph == null) { return; }
            if (!sue.msa.areAllChoiceModelsSet(sue.graph)) { Msg.show("Model is not ready for MSA.", "Choice models of at least one OD pair is not set.", this); return; }
            Sensitivity.SensitivityAnalysis frm = new Sensitivity.SensitivityAnalysis("fftt", this.Width, this.Height, this.WindowState); frm.ShowDialog(this);
            linkSueDgv.newSue(); linkSueDgv.dgv.setDataTable(linkSueDt);
            pathDgv.newSue(cbPaths_OdPairs.Text, tbPaths_Nodes.Text, tbPaths_Links.Text); pathDgv.dgv.setDataTable(pathDt);
        }
        #endregion



    }
}