using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Windows.Forms;
using System.IO;
using LabelerCore.Data;
using LabelerCore.Loop;
using UserControls;

namespace Labeler
{

    public partial class LabelerForm : Form
    {
        public static readonly string FRQ_FOLDER_PATH = @"D:\\Galerie\\Loops"; //E:\Camea\Loops\data
        private TrainingData  _trainData;
        private ProgressBarForm _cpDataProgrBarForm;
        private ProgressBarForm _loadTrainsetProgrBarForm;
        private OptionsForm _optionsForm;
        private RecordTableColumns _recordTableColumns = new RecordTableColumns();
        private ClassificationInfo _clfInfo;
    
        private bool _clfInfoDetailsToolTipEnabled = false;
        private int _clfInfoDetailsToolTipTime = 10000;


        public TrainingData TrainData
        {
            get { return _trainData; }
            set
            {
                dataExplorer.TrainData = value;
                trainDataExplorer.TrainData = value;
                _trainData = value;
                if (_trainData.Count > 0)
                    _clfInfo.Fit(value.Samples);
                else
                    _clfInfo = new ClassificationInfo();
            }
        }


        public LabelerForm()
        {
            InitializeComponent();            
            TrainData = new TrainingData();

            trainDataExplorer.recordTable.ItemEnter += tDExplorerItemEnterHandler_ItemEnter;
            trainDataExplorer.recordTable.TrainDataCountChanged += tDExplorerTrainDataCountChangedHandler_TrainDataCount;
            
            dataExplorer.recordTable.ItemEnter += DataExplorerItemEnterHandler_ItemEnter;
            dataExplorer.recordTable.TrainDataCountChanged += DataExplorerTrainDataCountChangedHandler_TrainDataCount;
            dataExplorer.FolderChanged += DataExplorerFolderChangedHandler_FolderChanged;
            frqFolderLabel.Text = dataExplorer.RawDataFolder.FullName;


            trainDataExplorer.recordTable.DGV.CellMouseClick += DGV_CellMouseClickTrainExplorer;
            dataExplorer.recordTable.DGV.CellMouseClick += DGV_CellMouseClickRawExplorer;

            trainDataExplorer.recordTable.TableColumns_ = _recordTableColumns;
            dataExplorer.recordTable.TableColumns_ = _recordTableColumns;

            _optionsForm = new OptionsForm(_recordTableColumns,
                                           _clfInfoDetailsToolTipEnabled);




            //DEMO
            _loadTrainingData(FRQ_FOLDER_PATH, @"..\..\..\..\training_data\test_set.csv");
            classifierToolStripMenuItem_Click(new Object(), null);
            FormCollection fc = Application.OpenForms;
            for (int i = 0; i < fc.Count; i++) {
                ClassifierForm clfForm = (fc[i] as ClassifierForm);
                if (clfForm != null) {
                    clfForm.LoadClassifier(@"..\..\..\XMLData\loopclf_local_logit.xml");
                }
            }
            explorerTabControl.SelectTab(1);
            trainDataExplorer.classesDGV[3, 0].Value = "True";
            trainDataExplorer.updateTrainDataDGV();


           dataExplorer.RawDataFolder = new DirectoryInfo(Path.Combine(FRQ_FOLDER_PATH, @"FRQ\SN-JS\20160526\11"));
            dataExplorer.PopulateTreeView();
            frqFolderLabel.Text = dataExplorer.RawDataFolder.FullName;



        }

        private void _showToolTip(double[] x, int toolTipXPos, int toolTipYPos) {
            if (!_clfInfoDetailsToolTipEnabled) return;
            string message = _clfInfo.Details(x).ToString();
            toolTipClfInfo.Show(message, trainDataExplorer,
                  toolTipXPos,
                  toolTipYPos,
                  _clfInfoDetailsToolTipTime);
            dataExplorer.recordTable.DGV.ShowCellToolTips = false;
            trainDataExplorer.recordTable.DGV.ShowCellToolTips = false;
        }

        private void _updateClfForms(double[] x) {
            FormCollection fc = Application.OpenForms;
            for (int i = 0; i < fc.Count; i++) {
                ClassifierForm clfForm = (fc[i] as ClassifierForm);
                if (clfForm != null) {
                    if (clfForm.IsFitted) {
                        clfForm.DispProbs(x);
                    }
                }
            }
        }


        private void _showNeighborForm(double[] x)
        {
            FormCollection fc = Application.OpenForms;
            for (int i = 0; i < fc.Count; i++) {
                ClassifierForm clfForm = (fc[i] as ClassifierForm);
                if (clfForm != null) {
                    if (!clfForm.IsFitted) continue;
                    CppNeighbor[] cppNeighbors = clfForm.GetNeighbors(x, 20);
                    Neighbor[] neighbors = new Neighbor[cppNeighbors.Length];
                    for (int j = 0; j < neighbors.Length; j++)
                        neighbors[j] = new Neighbor(new Sample(Path.Combine(FRQ_FOLDER_PATH, cppNeighbors[j].path),
                                                               cppNeighbors[j].label),
                                                    cppNeighbors[j].distance);


                    var neighborsForm = new NeighborsForm(neighbors.ToList());
                    //neighborsForm.TrainData = clfForm.TrainData;
                    neighborsForm.RecordTable.TableColumns_ = _recordTableColumns;
                    neighborsForm.Show();
                    neighborsForm.Text = clfForm.Text;
                }
            }
        }

        void DGV_CellMouseClickTrainExplorer(object sender, DataGridViewCellMouseEventArgs e)
        {
            if(e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                VehicleInfo info = (VehicleInfo)trainDataExplorer.recordTable.DGV.Rows[e.RowIndex].Tag;
                if (info != null) _showNeighborForm(info.FeatureArray);
            }
        }

        void DGV_CellMouseClickRawExplorer(object sender, DataGridViewCellMouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                VehicleInfo info = (VehicleInfo)dataExplorer.recordTable.DGV.Rows[e.RowIndex].Tag;
                if(info != null) _showNeighborForm(info.FeatureArray);
            }
        }


        private void DataExplorerItemEnterHandler_ItemEnter(VehicleInfo info,
                                                            double[] x,
                                                            int toolTipXPos,
                                                            int toolTipYPos)
        {
            try {
                recordView.updateRecordView(info, holdOnToolStripMenuItem.Checked);
                _showToolTip(x, toolTipXPos, toolTipYPos);
                _updateClfForms(x);
            }
            catch (Exception e) {
                MessageBox.Show("There was something unexpected: " + e.Message);
            }
        }

        private void tDExplorerItemEnterHandler_ItemEnter(VehicleInfo info,
                                                          double[] x,
                                                          int toolTipXPos,
                                                          int toolTipYPos)
        {
            try
            {
                recordView.updateRecordView(info, holdOnToolStripMenuItem.Checked);
                _showToolTip(x, toolTipXPos, toolTipYPos);
                _updateClfForms(x);
            }
            catch(Exception e)
            {
                MessageBox.Show("There was something unexpected: " + e.Message);
            }
        }

        private void DataExplorerTrainDataCountChangedHandler_TrainDataCount(int count)
        {
            trainDataCountLabel.Text = "training set size: " + count.ToString();
        }

        private void tDExplorerTrainDataCountChangedHandler_TrainDataCount(int count)
        {
            trainDataCountLabel.Text = "training set size: " + count.ToString();
        }

        private void DataExplorerFolderChangedHandler_FolderChanged(DirectoryInfo rawDataFolder)
        {
            frqFolderLabel.Text = rawDataFolder.FullName;
        }

        private void newTrainSetToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DialogResult result = MessageBox.Show("Save changes?",
                                                   "Do you want to save current training set?",
                                                    MessageBoxButtons.YesNoCancel);
            if (result == DialogResult.Yes)
            {
                saveTrainSetAsToolStripMenuItem.PerformClick();
            }
            else if (result == DialogResult.Cancel)
            {
                return;
            }
           TrainData = new TrainingData ();
           trainDataFileLabel.Text = " ";
           trainDataCountLabel.Text = "training set size: 0";
           trainDataExplorer.updateClassesDGV();

        }

        private void _loadTrainingData(string frqFolderPath, string filename)
        {
            try
            {
                _loadTrainsetProgrBarForm = new ProgressBarForm();
                _loadTrainsetProgrBarForm.Text = "Reading ...";
                _loadTrainsetProgrBarForm.StartPosition = FormStartPosition.CenterScreen;
                loadingTrainsetBackgroundWorker.RunWorkerAsync(
                        new Tuple<string, string>(frqFolderPath, filename));
                _loadTrainsetProgrBarForm.ShowDialog();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Cant read training data: " + ex.Message);
                TrainData = new TrainingData();
            }

        }

        private void openTrainSetToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DialogResult result = MessageBox.Show("Save changes?",
                                                   "Do you want to save current training set?",
                                                    MessageBoxButtons.YesNoCancel);
            if (result == DialogResult.Yes)
                saveTrainSetAsToolStripMenuItem.PerformClick();
            else if(result == DialogResult.Cancel)
                return;
           
            var fileDialog = new OpenFileDialog();
            fileDialog.Filter = "Training Set|*.csv|Training Set|*.xml";
            DialogResult res = fileDialog.ShowDialog();
            if(res == DialogResult.OK)
                _loadTrainingData(FRQ_FOLDER_PATH, fileDialog.FileName);
        }

        private void openTrainingSetWithLocalDataToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DialogResult result = MessageBox.Show("Save changes?",
                                                   "Do you want to save current training set?",
                                                    MessageBoxButtons.YesNoCancel);
            if (result == DialogResult.Yes)
                saveTrainSetAsToolStripMenuItem.PerformClick();
            else if(result == DialogResult.Cancel)
                return;

            var fileDialog = new OpenFileDialog();
            fileDialog.Filter = "Training Set|*.csv|Training Set|*.xml";
            _loadTrainingData(Path.GetDirectoryName(fileDialog.FileName),  Path.GetFileNameWithoutExtension(fileDialog.FileName));
        }

        private void openRawDataFolderToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try {
                var folderBrowser = new FolderBrowserDialog();
                folderBrowser.SelectedPath = dataExplorer.RawDataFolder.FullName;
                if (folderBrowser.ShowDialog() == DialogResult.OK) {
                    dataExplorer.RawDataFolder = new DirectoryInfo(folderBrowser.SelectedPath);
                    dataExplorer.PopulateTreeView();
                    frqFolderLabel.Text = dataExplorer.RawDataFolder.FullName;
                }
            }
            catch (Exception ex) {
                MessageBox.Show(ex.Message);
            }

        }

        private void saveTrainSetToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if(!TrainData.Save())
                saveTrainSetAsToolStripMenuItem.PerformClick();
        }

        private void saveTrainSetAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var fileDialog = new SaveFileDialog();
            fileDialog.Filter = "Training Set|*.csv|Training Set|*.xml";
            if (fileDialog.ShowDialog() == DialogResult.OK)
            {
                if (Path.GetExtension(fileDialog.FileName) == ".csv")
                    TrainData.SaveToCSV(fileDialog.FileName);
                else if (Path.GetExtension(fileDialog.FileName) == ".xml")
                    TrainData.SaveToXml(fileDialog.FileName);
                trainDataFileLabel.Text = TrainData.fileName;
            }
        }

        private void explorerTabControl_SelectedIndexChanged(object sender, EventArgs e)
        {
            if(explorerTabControl.SelectedIndex == 1)
            {
                trainDataExplorer.updateClassesDGV();
                trainDataExplorer.updateTrainDataDGV();
            }
            else if(explorerTabControl.SelectedIndex == 0)
            {
                //fake node click?
            }
        }

        private void LabelerForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            DialogResult result = MessageBox.Show("Save changes?",
                                                   "Do you want to save current training set?",
                                                    MessageBoxButtons.YesNoCancel);
            if(result == DialogResult.Yes)
                saveTrainSetAsToolStripMenuItem.PerformClick();
            if(result == DialogResult.Cancel)
                e.Cancel = true;
        }

        private void saveTrainsetWithDataToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var fileDialog = new SaveFileDialog();
            fileDialog.Filter = "Training Set|*.csv|Training Set|*.xml";
            if (fileDialog.ShowDialog() == DialogResult.OK)
            {
                if (Path.GetExtension(fileDialog.FileName) == ".csv")
                    TrainData.SaveToCSV(fileDialog.FileName);
                else if (Path.GetExtension(fileDialog.FileName) == ".xml")
                    TrainData.SaveToXml(fileDialog.FileName);

                trainDataFileLabel.Text = TrainData.fileName;

                string dataPath = Path.Combine(Path.GetDirectoryName(fileDialog.FileName),
                                               Path.GetFileNameWithoutExtension(fileDialog.FileName));
                trainDataFileLabel.Text = TrainData.fileName;
                _cpDataProgrBarForm = new ProgressBarForm();
                _cpDataProgrBarForm.Text = "Coping data files...";
                _cpDataProgrBarForm.Show();
                copyingDataBackgroundWorker.RunWorkerAsync(dataPath);
            }
        }

        private void saveTrainsetWithAttributesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var fileDialog = new SaveFileDialog();
            fileDialog.Filter = "Training Set|*.csv";
            if (fileDialog.ShowDialog() == DialogResult.OK)
                TrainData.SaveToCSV_WithAttributes(fileDialog.FileName);
        }

        private void copingDataBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            string dataPath = (string)e.Argument;
            int i = 0;
            foreach (Sample s in TrainData)
            {
                s.CopyDataFilesTo(dataPath);
                copyingDataBackgroundWorker.ReportProgress( i++ * 100 / TrainData.Count);
            }
        }

        private void copingDataBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if(_cpDataProgrBarForm != null)
                _cpDataProgrBarForm.progressBar1.Value = e.ProgressPercentage;
        }

        private void copingDataBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if(_cpDataProgrBarForm != null)
            {
                _cpDataProgrBarForm.Close();
                _cpDataProgrBarForm.Dispose();
                _cpDataProgrBarForm = null;
            }

        }

        private void loadingTrainsetBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {

            TrainingData trainData = new TrainingData();
            trainData.SampleRead += TrainingDataSampleReadHandler_SampleRead;
            trainData.FRQFolderPath = ((Tuple<string, string>)e.Argument).Item1;
            trainData.deepLoad = _optionsForm.deepLoad;
            try
            {
                trainData.Load(((Tuple<string, string>)e.Argument).Item2);
                TrainData = trainData;
            }
            catch(Exception ex)
            {
                MessageBox.Show("Failed to load, exception: \n" + ex.Message);
                TrainData = new TrainingData();
            }


        }

        private void loadingTrainsetBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if(_loadTrainsetProgrBarForm != null)
                _loadTrainsetProgrBarForm.progressBar1.Value = e.ProgressPercentage;
        }

        private void loadingTrainsetBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (_loadTrainsetProgrBarForm != null)
            {
                _loadTrainsetProgrBarForm.Close();
                _loadTrainsetProgrBarForm.Dispose();
                _loadTrainsetProgrBarForm = null;
            }
            trainDataExplorer.recordTable.TrainDataCountChanged += tDExplorerTrainDataCountChangedHandler_TrainDataCount;
            dataExplorer.recordTable.TrainDataCountChanged += DataExplorerTrainDataCountChangedHandler_TrainDataCount;
            trainDataFileLabel.Text = TrainData.fileName;
            trainDataCountLabel.Text = "training set size: " + TrainData.Count.ToString();
            trainDataExplorer.updateClassesDGV();
        }

        private void TrainingDataSampleReadHandler_SampleRead(int curCount, int totalCount)
        {
            loadingTrainsetBackgroundWorker.ReportProgress(curCount * 100 / totalCount);
        }

        private void clusteringToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //if (trainDataExplorer.DisplayedClasses.Length < 1)
            //{
            //    MessageBox.Show("Select at least one vehicle class");
            //    return;
            //}
            double[][] X;
            VehicleInfo[] samples;
            if (explorerTabControl.SelectedIndex == 0) {
                X = dataExplorer.recordTable.DisplayedVehicles.Select(x => x.FeatureArray).ToArray();
                samples = dataExplorer.recordTable.DisplayedVehicles;
            }
            else {
                X = trainDataExplorer.recordTable.DisplayedVehicles.Select(x => x.FeatureArray).ToArray();
                samples = trainDataExplorer.recordTable.DisplayedVehicles;                  
            }
            if (samples.Length < 1) {
                MessageBox.Show("no vehicles to cluster");
                return;
            }
            //Sample[] samples = TrainData.Samples
            //                        .Where(x => trainDataExplorer.DisplayedClasses.Contains(x.vehicleClass))
            //                        .ToArray();
            //double[][] X = samples.Select(x => x.FeatureArray).ToArray();

            X = (new LabelerCore.Model.Transformers.MinMaxScaler()).FitTransform(X);

            var dialogResult = new KMeansSetupForm(X, samples.ToArray()).ShowDialog();

            if (dialogResult == DialogResult.OK) {
                checkBoxCluster.Checked = true;
                if (explorerTabControl.SelectedIndex == 0) {
                    dataExplorer.recordTable.ShowItems(dataExplorer.recordTable.DisplayedVehicles);
                }
                else {
                    trainDataExplorer.updateTrainDataDGV();
                }
            }
                
        }

        private void optionsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DialogResult res = _optionsForm.ShowDialog();
            if(res == DialogResult.OK)
            {
                _clfInfoDetailsToolTipEnabled = _optionsForm.clfInfoDetailsToolTipEnabled;
                Sample.Features = _optionsForm.Features;
                //if(TrainData.Count > 1)
                //    _clfInfo.Fit(TrainData.Samples);
            }
        }

        private void showPredictedToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (explorerTabControl.SelectedIndex == 1) {
                trainDataExplorer.updateTrainDataDGV();
            }
            else if (explorerTabControl.SelectedIndex == 0) {
                ;
            }

            VehicleInfo[] infos;
            if (explorerTabControl.SelectedIndex == 1)
                infos = trainDataExplorer.recordTable.GetCurrentItems();
            else
                infos = dataExplorer.recordTable.GetCurrentItems();

            if (infos == null) return;
            if (infos.Length < 1) return;

            FormCollection fc = Application.OpenForms;
            try {
                foreach (Form f in fc) {
                    ClassifierForm clfForm = (f as ClassifierForm);
                    if (clfForm != null) {
                        double[][] X = infos.Select(x => x.FeatureArray).ToArray();

                        int[] predicted = clfForm.Predict(X);
                        if (predicted == null) return;
                        var res = new Tuple<VehicleInfo, object>[predicted.Length];
                        for (int i = 0; i < res.Length; i++)
                            res[i] = new Tuple<VehicleInfo, object>(infos[i], (object)predicted[i]);
                        if (explorerTabControl.SelectedIndex == 1)
                            trainDataExplorer.recordTable.AddColumn(res, clfForm.Text);
                        else
                            dataExplorer.recordTable.AddColumn(res, clfForm.Text);
                    }
                }
            }
            catch (Exception ex) {
                MessageBox.Show("There was something unexpected: " + ex.Message);
            }

        }

        private void classifierToolStripMenuItem_Click(object sender, EventArgs e) {
            new ClassifierForm().Show();
        }

        private void converterToolStripMenuItem_Click(object sender, EventArgs e) {
            new DatasetConverterForm().ShowDialog();
        }

        private void checkBoxMahalonobis_CheckedChanged(object sender, EventArgs e) {
            _recordTableColumns.GetColumn("Mahalonobis").enabled = checkBoxMahalonobis.Checked;
            trainDataExplorer.updateTrainDataDGV();
        }

        private void checkBoxCluster_CheckedChanged(object sender, EventArgs e) {
            _recordTableColumns.GetColumn("Cluster").enabled = checkBoxCluster.Checked;
            trainDataExplorer.updateTrainDataDGV();
            dataExplorer.UpdateDGV();
        }


    }
}
