//-------------------------------------------------------------------------------------
// SID Monitor - Utility for Sudden Ionospheric Disturbances Monitoring Stations
// Copyright (C) 2006 - Lionel Loudet
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//-------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Threading;
using System.Globalization;
using System.Text.RegularExpressions;
using WeifenLuo.WinFormsUI;


namespace SID_monitor
{
    public partial class GenerateReportParametersDockablePanel : DockContent
    {

        // global variables
        public const string allChannelsString = "All";
        private readonly static String[] nullStrings = new string[16] { String.Empty, String.Empty, String.Empty, String.Empty, String.Empty, String.Empty, String.Empty, String.Empty, String.Empty, String.Empty, String.Empty, String.Empty, String.Empty, String.Empty, String.Empty, String.Empty };
        private String[] channelsDS = new string[16]; // contains the list of datasets
        private String[] channelComboBoxItems = new string[17]; // contains the channel combobox items
        private IntPtr outputTextBoxFormHandle = IntPtr.Zero;   // the outputTextBoxForm to use to display messages

        public GenerateReportParametersDockablePanel()
        {
            InitializeComponent();

            nullStrings.CopyTo(channelsDS, 0);
            
            this.numericUpDownMonthReport.Value = DateTime.UtcNow.Month;
            this.numericUpDownYearReport.Value = DateTime.UtcNow.Year;

            this.checkBoxShowSun.Enabled = false;
            this.checkBoxShowForecast.Enabled = false;
            this.checkBoxShowGOES10.Enabled = false;
            this.checkBoxShowGOES11.Enabled = false;
            this.checkBoxShowGOES12.Enabled = false;

            this.channelsDS.CopyTo(this.channelComboBoxItems, 1);
            this.channelComboBoxItems[0] = allChannelsString;
            this.comboBoxChannel.Items.Clear();
            this.comboBoxChannel.Items.AddRange(this.channelComboBoxItems);
            this.comboBoxChannel.Text = allChannelsString;
        }

        #region GenerateReportParametersDockablePane Method
        private void GenerateReportParametersDockablePanel_Load(object sender, EventArgs e)
        {
            // we start with a selected MonthReport radiobutton 
            this.radioButtonMonthReport.Select();
            this.dateTimePickerMonthReport_ValueChanged(sender, e); // to initialize all fields
        }
        #endregion

        #region Properties

        public string ReportFilename
        {
            get
            {
                return this.textBoxReportFilename.Text;
            }
            set
            {
                this.textBoxReportFilename.Text = value;
            }
        }

        public String[] ChannelsDS
        {
            get
            {
                return this.channelsDS;
            }
            set
            {
                this.channelsDS = value;
                this.channelsDS.CopyTo(this.channelComboBoxItems, 1);
                this.channelComboBoxItems[0] = allChannelsString;
                this.comboBoxChannel.Items.Clear();
                this.comboBoxChannel.Items.AddRange(this.channelComboBoxItems);
                this.comboBoxChannel.Text = allChannelsString;
            }
        }

        public bool ComboBoxChannelEnabled
        {
            get
            {
                return this.comboBoxChannel.Enabled;
            }
            set
            {
                this.comboBoxChannel.Enabled = value;
            }
        }

        public string ComboBoxChannelSelectedItem
        {
            get
            {
                if (String.IsNullOrEmpty(this.comboBoxChannel.Text))
                {
                    return allChannelsString; // empty text in the comboBox is understood as "All"
                }
                else
                {
                    return this.comboBoxChannel.Text;
                }
            }
            set
            {
                if (comboBoxChannel.Items.Contains(value))
                {
                    this.comboBoxChannel.Text = value;
                }
                else
                {
                    this.comboBoxChannel.Text = allChannelsString; // by default revert to "All"
                }
            }
        }

        public bool ShowSunChecked
        {
            get
            {
                return this.checkBoxShowSun.Checked;

            }
            set
            {
                this.checkBoxShowSun.Checked = value;
            }
        }

        public bool ShowSunEnabled
        {
            get
            {
                return this.checkBoxShowSun.Enabled;

            }
            set
            {
                this.checkBoxShowSun.Enabled = value;
            }
        }

        public bool ShowForecastChecked
        {
            get
            {
                return this.checkBoxShowForecast.Checked;

            }
            set
            {
                this.checkBoxShowForecast.Checked = value;
            }
        }

        public bool ShowForecastEnabled
        {
            get
            {
                return this.checkBoxShowForecast.Enabled;

            }
            set
            {
                this.checkBoxShowForecast.Enabled = value;
            }
        }

        public bool ShowGOES10Checked
        {
            get
            {
                return this.checkBoxShowGOES10.Checked;
            }
            set
            {
                this.checkBoxShowGOES10.Checked = value;
            }
        }

        public bool ShowGOES11Checked
        {
            get
            {
                return this.checkBoxShowGOES11.Checked;
            }
            set
            {
                this.checkBoxShowGOES11.Checked = value;
            }
        }

        public bool ShowGOES12Checked
        {
            get
            {
                return this.checkBoxShowGOES12.Checked;
            }
            set
            {
                this.checkBoxShowGOES12.Checked = value;
            }
        }

        public bool ShowGOES11Enabled
        {
            get
            {
                return this.checkBoxShowGOES11.Enabled;
            }
            set
            {
                this.checkBoxShowGOES11.Enabled = value;
            }
        }

        public bool ShowGOES10Enabled
        {
            get
            {
                return this.checkBoxShowGOES10.Enabled;
            }
            set
            {
                this.checkBoxShowGOES10.Enabled = value;
            }
        }

        public bool ShowGOES12Enabled
        {
            get
            {
                return this.checkBoxShowGOES12.Enabled;
            }
            set
            {
                this.checkBoxShowGOES12.Enabled = value;
            }
        }

        public string TextBoxChannelsDatabaseFilename
        {
            get
            {
                return this.textBoxDatabaseFilename.Text;
            }
            set
            {
                this.textBoxDatabaseFilename.Text = value;
            }
        }

        public string LabelGOESDatabaseFilename
        {
            get
            {
                return this.labelGOESdatabaseFilename.Text;
            }
            set
            {
                this.labelGOESdatabaseFilename.Text = value;
            }
        }

        public DateTime MonthReport
        {
            get
            {
                return new DateTime((int)this.numericUpDownYearReport.Value, (int)this.numericUpDownMonthReport.Value, 1);
            }
            set
            {
                this.numericUpDownMonthReport.Value = value.Month;
                this.numericUpDownYearReport.Value = value.Year;
            }
        }

        public decimal GraphDay
        {
            get
            {
                return this.numericUpDownGraphDate.Value;
            }
            set
            {
                this.numericUpDownGraphDate.Value = value;
            }
        }
        #endregion

        #region Radiobuttons methods
        private void radioButtonMonthReport_CheckedChanged(object sender, EventArgs e)
        {
            if (this.radioButtonMonthReport.Checked)
            {
                this.numericUpDownMonthReport.Enabled = true;
                this.numericUpDownYearReport.Enabled = true;

                this.textBoxReportFilename.Enabled = false;
                this.buttonSelectReport.Enabled = false;

                this.textBoxDatabaseFilename.Enabled = false;
                this.buttonSelectDatabase.Enabled = false;

                //           this.dateTimePickerMonthReport_ValueChanged(sender, e);
            }
        }

        private void radioButtonGenerateExistingReport_CheckedChanged(object sender, EventArgs e)
        {
            if (this.radioButtonSelectExistingReport.Checked)
            {
                this.numericUpDownMonthReport.Enabled = false;
                this.numericUpDownYearReport.Enabled = false;

                this.textBoxReportFilename.Enabled = true;
                this.buttonSelectReport.Enabled = true;

                this.textBoxDatabaseFilename.Enabled = false;
                this.buttonSelectDatabase.Enabled = false;

                //           this.textBoxReportFilename_TextChanged(sender, e);
            }
        }


        private void radioButtonDatabase_CheckedChanged(object sender, EventArgs e)
        {
            if (this.radioButtonDatabase.Checked)
            {
                this.numericUpDownMonthReport.Enabled = false;
                this.numericUpDownYearReport.Enabled = false;

                this.textBoxReportFilename.Enabled = false;
                this.buttonSelectReport.Enabled = false;

                this.textBoxDatabaseFilename.Enabled = true;
                this.buttonSelectDatabase.Enabled = true;

                //           this.textBoxDatabaseFilename_TextChanged(sender, e);
            }
        }

        #endregion

        #region Date Change Method
        private void dateTimePickerMonthReport_ValueChanged(object sender, EventArgs e)
        {
            // sets the graph date maximum value according to the number of days in the selected month
            DateTime selectedMonthReport = new DateTime((int)this.numericUpDownYearReport.Value, (int)this.numericUpDownMonthReport.Value, 1);
            this.numericUpDownGraphDate.Maximum = DateTime.DaysInMonth(selectedMonthReport.Year, selectedMonthReport.Month);
            this.numericUpDownGraphDate.Value = 1;

            if (this.radioButtonMonthReport.Checked) // the date has been changed by the user, we update the databases filenames
            {
                // sets the filenames
                this.textBoxReportFilename.Text = "";
                this.textBoxDatabaseFilename.Text = CreateChannelsDatabaseName(selectedMonthReport);
                this.labelGOESdatabaseFilename.Text = CreateGOESDatabaseName(selectedMonthReport);
            }
        }
        #endregion

        #region Databases files change Method

        private void labelGOESdatabaseFilename_TextChanged(object sender, EventArgs e)
        {
            if (!File.Exists(this.labelGOESdatabaseFilename.Text))
            {
                errorProvider.SetError(this.labelGOESdatabaseFilename, "This file does not exists");

                this.checkBoxShowGOES10.Enabled = false;
                this.checkBoxShowGOES11.Enabled = false; 
                this.checkBoxShowGOES12.Enabled = false;
            }
            else
            {
                errorProvider.SetError(this.labelGOESdatabaseFilename, "");

                this.checkBoxShowGOES10.Enabled = true;
                this.checkBoxShowGOES11.Enabled = true; 
                this.checkBoxShowGOES12.Enabled = true;
            }
        }

        private void textBoxDatabaseFilename_TextChanged(object sender, EventArgs e)
        {
            if (this.radioButtonDatabase.Checked) // the filename has been changed by the user
            {
                OnClearEventsRequested(e);
                this.textBoxReportFilename.Text = "";
            }

            if (!File.Exists(this.textBoxDatabaseFilename.Text))
            {
                errorProvider.SetError(this.textBoxDatabaseFilename, "This file does not exists");

                this.checkBoxShowSun.Enabled = false;
                this.checkBoxShowForecast.Enabled = false;
                this.comboBoxChannel.Enabled = false;

                OnDatabaseFileError(e); // DatabaseFileError Event raised

            }
            else
            {
                
                OnAddOutputTextRequired(new AddOutputTextRequireEventArgs(" Reading database file \"" + this.textBoxDatabaseFilename.Text + "\"\n", OutputTextBox.OutputTextMessageCategory.Normal));

                // read the datasets present in the database
                channelsDS = GetDatasetsFromRRDToolDatabase(this.textBoxDatabaseFilename.Text, this.outputTextBoxFormHandle);


                if (channelsDS != null)
                {

                    SIDMonitorDateTime lastDatabaseUpdateTime;
                    try
                    {
                        lastDatabaseUpdateTime = RRDTool.getLastDatabaseUpdateTime(this.textBoxDatabaseFilename.Text, this.outputTextBoxFormHandle);

                        errorProvider.SetError(this.textBoxDatabaseFilename, "");

                        this.checkBoxShowSun.Enabled = true;
                        this.checkBoxShowForecast.Enabled = true;
                        this.comboBoxChannel.Enabled = true;

                        DateTime selectedMonthReport = lastDatabaseUpdateTime.WinTime.Date;

                        // sets the GOES database name
                        this.labelGOESdatabaseFilename.Text = CreateGOESDatabaseName(selectedMonthReport);

                        // we update the month report date
                        this.numericUpDownMonthReport.Value = selectedMonthReport.Month;
                        this.numericUpDownYearReport.Value = selectedMonthReport.Year;

                        // updates the channels comboBox
                        this.channelsDS.CopyTo(this.channelComboBoxItems, 1);
                        this.channelComboBoxItems[0] = allChannelsString;
                        this.comboBoxChannel.Items.Clear();
                        this.comboBoxChannel.Items.AddRange(this.channelComboBoxItems);
                        this.comboBoxChannel.Text = allChannelsString;

                        OnGraphUpdateRequested(e);
                    }
                    catch // the database date has not been found
                    {
                        errorProvider.SetError(this.textBoxDatabaseFilename, "Invalid database format");

                        OnAddOutputTextRequired(new AddOutputTextRequireEventArgs(" Wrong database file format\"" + this.textBoxDatabaseFilename.Text + "\"\n", OutputTextBox.OutputTextMessageCategory.Warning));

                        this.checkBoxShowSun.Enabled = false;
                        this.checkBoxShowForecast.Enabled = false;
                        this.comboBoxChannel.Enabled = false;

                        OnDatabaseFileError(e);
                    }
                }
                else // database does not contains datasets
                {
                    errorProvider.SetError(this.textBoxDatabaseFilename, "No station codes found in this database");

                    OnAddOutputTextRequired(new AddOutputTextRequireEventArgs(" Wrong database file format\"" + this.textBoxDatabaseFilename.Text + "\". No station codes found in the database.\n", OutputTextBox.OutputTextMessageCategory.Warning));

                    this.checkBoxShowSun.Enabled = false;
                    this.checkBoxShowForecast.Enabled = false;
                    this.comboBoxChannel.Enabled = false;

                    OnDatabaseFileError(e);

                }
            }
        }
        #endregion

        #region Channel Change Method
        private void comboBoxChannel_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (this.comboBoxChannel.Focused) // the selection change comes from user input
            {
                OnGraphUpdateRequested(e);
            }
        }
        #endregion

        #region Checkboxes methods
        /// <summary>
        /// Ask to update the graph when "Show GOES10" selection changes
        /// </summary>
        private void checkBoxShowGOES10_CheckedChanged(object sender, EventArgs e)
        {
            OnGraphUpdateRequested(e);
        }

        /// <summary>
        /// Ask to update the graph when "Show GOES11" selection changes
        /// </summary>
        private void checkBoxShowGOES11_CheckedChanged(object sender, EventArgs e)
        {
            OnGraphUpdateRequested(e);
        }

        /// <summary>
        /// Ask to update the graph when "Show GOES12" selection changes
        /// </summary>
        private void checkBoxShowGOES12_CheckedChanged(object sender, EventArgs e)
        {
            OnGraphUpdateRequested(e);
        }

        /// <summary>
        /// Ask to update the graph when "Show Sun" selection changes
        /// </summary>
        private void checkBoxShowSun_CheckedChanged(object sender, EventArgs e)
        {
            OnGraphUpdateRequested(e);
        }


        /// <summary>
        /// Ask to update the graph when "Show Forest" selection changes
        /// </summary>
        private void checkBoxShowForecast_CheckedChanged(object sender, EventArgs e)
        {
            OnGraphUpdateRequested(e);
        }
        #endregion

        #region Graph Date Change Method
        /// <summary>
        /// Updates the graph when a new date is selected
        /// </summary>
        private void numericUpDownGraphDate_ValueChanged(object sender, EventArgs e)
        {
            OnClearEventsSelectionRequested(e);
            OnGraphUpdateRequested(e);
        }
        #endregion

        #region Report Change Methods
        private void textBoxReportFilename_TextChanged(object sender, EventArgs e)
        {
            if (this.radioButtonSelectExistingReport.Checked) // only if the user has changed  the filename
            {
                if (!File.Exists(this.textBoxReportFilename.Text) && this.textBoxReportFilename.Enabled)
                {
                    errorProvider.SetError(this.textBoxReportFilename, "This file does not exists");
                    OnReportFileError(e);
                }
                else
                {
                    errorProvider.SetError(this.textBoxReportFilename, "");
                    OnLoadReportRequested(e);
                }
            }
        }
        #endregion

        #region Buttons Methods
        public void buttonSelectReport_Click(object sender, EventArgs e)
        {
            this.openFileDialog.DefaultExt = "dat";
            this.openFileDialog.Filter = "Report Files|*.dat|All files|*.*";
            this.openFileDialog.Title = "Select Report File";
            this.openFileDialog.ReadOnlyChecked = false;
            if (File.Exists(this.textBoxReportFilename.Text))
            {
                this.openFileDialog.FileName = this.textBoxReportFilename.Text;
            }

            if (this.openFileDialog.ShowDialog() == DialogResult.OK)
            {
                this.textBoxReportFilename.Text = this.openFileDialog.FileName;
            }
        }


        public void buttonOpenDatabase_Click(object sender, EventArgs e)
        {
            this.openFileDialog.DefaultExt = "rrd";
            this.openFileDialog.Filter = "RRDTool Database|*.rrd|All files|*.*";
            this.openFileDialog.Title = "Select Database to process";
            this.openFileDialog.ReadOnlyChecked = true;
            if (File.Exists(this.textBoxDatabaseFilename.Text))
            {
                this.openFileDialog.FileName = this.textBoxDatabaseFilename.Text;
            }

            if (this.openFileDialog.ShowDialog() == DialogResult.OK)
            {
                this.textBoxDatabaseFilename.Text = this.openFileDialog.FileName;
                this.radioButtonDatabase.Select();
            }

        }
        #endregion

        #region Database manipulation Methods
        /// <summary>
        /// Creates the channels database name that corresponds to a given date
        /// </summary>
        private static String CreateChannelsDatabaseName(DateTime DatabaseDate)
        {
            DateTime today = DateTime.UtcNow;
            if ((DatabaseDate.Month == today.Month) && (DatabaseDate.Year == today.Year))
            {
                return SID_monitor.Properties.Settings.Default.RRDToolDatabase;
            }
            else
            {
                return Path.Combine(Path.GetFullPath(SID_monitor.Properties.Settings.Default.HistoryDatabaseFolder) + "\\" + DatabaseDate.ToString("MMyy"),
                                    SID_monitor.Properties.Settings.Default.RRDToolChannelsSaveFilename + "." + DatabaseDate.ToString("MMyy") + ".rrd");
            }
        }

        /// <summary>
        /// Creates the GOES database name that corresponds to a given date
        /// </summary>
        private static String CreateGOESDatabaseName(DateTime DatabaseDate)
        {
            DateTime today = DateTime.UtcNow;
            if ((DatabaseDate.Month == today.Month) && (DatabaseDate.Year == today.Year))
            {
                return SID_monitor.Properties.Settings.Default.RRDToolGOES;
            }
            else
            {
                return Path.Combine(Path.GetFullPath(SID_monitor.Properties.Settings.Default.HistoryDatabaseFolder) + "\\" + DatabaseDate.ToString("MMyy"),
                                    SID_monitor.Properties.Settings.Default.RRDToolGOESSaveFilename + "." + DatabaseDate.ToString("MMyy") + ".rrd");
            }
        }

        /// <summary>
        /// Reads the datasets present in a given RRDTool database
        /// </summary>
        private static String[] GetDatasetsFromRRDToolDatabase(string filename, IntPtr outputTextBoxHandle)
        {
            String[] DSList = new string[16];

            nullStrings.CopyTo(DSList, 0);

            if (!String.IsNullOrEmpty(filename) && File.Exists(Path.GetFullPath(filename)))
            {
                String infoString = RRDToolConnection.ExecuteRRDTool("info \"" + Path.GetFullPath(filename) + "\" ", RRDToolProcess.ShowOptions.ErrorsOnly, outputTextBoxHandle);

                Regex datasetRegEx = new Regex("^ds\\[(?<DS>.+)\\][.]type.*$",
                                              RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Multiline);
                Match m;

                int nbMatch = 0;

                for (m = datasetRegEx.Match(infoString); m.Success; m = m.NextMatch())
                {
                    if (!String.Equals(m.Groups["DS"].Value, "sun") && (Array.IndexOf(DSList, m.Groups["DS"].Value) < 0) && (nbMatch < DSList.Length))
                    {
                        DSList[nbMatch] = m.Groups["DS"].Value;
                        nbMatch++;
                    }

                }
                for (int i=nbMatch; i<DSList.Length; i++)  // clean-up the rest of the array
                {
                    DSList[i] = String.Empty;
                }

                return DSList;
            }
            else
            {
                return null;
            }
        }
        #endregion

        #region Events
        /// <summary>
        /// GraphUpdateRequested Event
        /// </summary>

        public event EventHandler GraphUpdateRequested;  // GraphUpdate is requested

        protected virtual void OnGraphUpdateRequested(EventArgs e)
        {
            EventHandler handler = GraphUpdateRequested;
            if (handler != null)
            {
                // Invokes the delegates. 
                handler(this, e);
            }
        }

        /// <summary>
        /// LoadReportRequested Event
        /// </summary>

        public event EventHandler LoadReportRequested;  // Load Report is requested

        protected virtual void OnLoadReportRequested(EventArgs e)
        {
            EventHandler handler = LoadReportRequested;
            if (handler != null)
            {
                // Invokes the delegates. 
                handler(this, e);
            }
        }

        /// <summary>
        /// ReportFileError Event
        /// </summary>

        public event EventHandler ReportFileError;  // Report File is in error

        protected virtual void OnReportFileError(EventArgs e)
        {
            EventHandler handler = ReportFileError;
            if (handler != null)
            {
                // Invokes the delegates. 
                handler(this, e);
            }
        }

        /// <summary>
        /// DatabaseFileError Event
        /// </summary>

        public event EventHandler DatabaseFileError;  // Report File is in error

        protected virtual void OnDatabaseFileError(EventArgs e)
        {
            EventHandler handler = DatabaseFileError;
            if (handler != null)
            {
                // Invokes the delegates. 
                handler(this, e);
            }
        }

        /// <summary>
        /// ClearEventsSelectionRequested Event
        /// </summary>

        public event EventHandler ClearEventsSelectionRequested;  // Needs to clear Events Selection

        protected virtual void OnClearEventsSelectionRequested(EventArgs e)
        {
            EventHandler handler = ClearEventsSelectionRequested;
            if (handler != null)
            {
                // Invokes the delegates. 
                handler(this, e);
            }
        }


        /// <summary>
        /// ClearEventsRequested Event
        /// </summary>

        public event EventHandler ClearEventsRequested;  // Needs to clear all Events

        protected virtual void OnClearEventsRequested(EventArgs e)
        {
            EventHandler handler = ClearEventsRequested;
            if (handler != null)
            {
                // Invokes the delegates. 
                handler(this, e);
            }
        }

        /// <summary>
        /// AddOutputTextRequired Event
        /// </summary>

        public class AddOutputTextRequireEventArgs : EventArgs
        {
            private string msg = String.Empty;
            private OutputTextBox.OutputTextMessageCategory category = OutputTextBox.OutputTextMessageCategory.Normal;

            public AddOutputTextRequireEventArgs(string msg, OutputTextBox.OutputTextMessageCategory category)
            {
                this.msg = msg;
                this.category = category;
            }

            public string Msg
            {
                get
                {
                    return msg;
                }
            }

            public OutputTextBox.OutputTextMessageCategory Category
            {
                get
                {
                    return category;
                }
            }
        }

        public delegate void AddOutputTextRequireEventHandler(object sender, AddOutputTextRequireEventArgs e);

        public event AddOutputTextRequireEventHandler AddOutputTextRequired; // A new message is to be displayed

        protected virtual void OnAddOutputTextRequired(AddOutputTextRequireEventArgs e)
        {
            AddOutputTextRequireEventHandler handler = AddOutputTextRequired;
            if (handler != null)
            {
                // Invokes the delegates. 
                handler(this, e);
            }
        }
        #endregion




    }
}
