using Microscopic_Traffic_Simulator.ViewModels.Messages;
using Microscopic_Traffic_Simulator___Model.CellularTopologyObjects.GeneralParameters;
using System;
using System.ComponentModel;
using System.IO;
using System.Windows.Input;

namespace Microscopic_Traffic_Simulator.ViewModels
{
    /// <summary>
    /// Class representing view model of parameters view.
    /// </summary>
    class ParametersViewModel : ViewModelBase, INotifyPropertyChanged
    {
        /// <summary>
        /// Message sending the parameters to use in the simulation.
        /// </summary>
        internal class ParametersMessage : ParameterizedMessage<Parameters> { }

        /// <summary>
        /// Reference to simulator parameters.
        /// </summary>
        private Parameters parameters;

        /// <summary>
        /// Reference to interactions class.
        /// </summary>
        private IInteractions interactions;

        /// <summary>
        /// Reference to user application settings.
        /// </summary>
        private ISettings settings;

        /// <summary>
        /// Name of the last used parameters file.
        /// </summary>
        public string LastUsedParametersFileName
        {
            get { return Path.GetFileNameWithoutExtension(PathToParameters); }
        }

        /// <summary>
        /// Path to the last used parameters file.
        /// </summary>
        private string PathToParameters
        {
            get { return settings.PathToParameters; }
            set
            {
                settings.PathToParameters = value;
                settings.Save();
                OnPropertyChanged("LastUsedParametersFileName");
            }
        }

        /// <summary>
        /// Cell length parameter.
        /// </summary>
        public double P1_CellLength
        {
            get { return parameters.CellularTopologyParameters.P1_CellLength; }
            set { parameters.CellularTopologyParameters.P1_CellLength = value; OnPropertyChanged("P1_CellLength"); }
        }

        /// <summary>
        /// Cell length parameter.
        /// </summary>
        public TimeSpan P2_SimulationStepInterval
        {
            get { return parameters.CellularTopologyParameters.P2_SimulationStepInterval; }
            set
            {
                parameters.CellularTopologyParameters.P2_SimulationStepInterval = value;
                OnPropertyChanged("P2_SimulationStepInterval");
            }
        }

        /// <summary>
        /// Low speed deceleration probability.
        /// </summary>
        public double P5_LowSpeedDecProbability
        {
            get { return parameters.TransitionFunctionParameters.P5_LowSpeedDecProbability; }
            set
            {
                parameters.TransitionFunctionParameters.P5_LowSpeedDecProbability = value;
                OnPropertyChanged("P5_LowSpeedDecProbability");
            }
        }

        /// <summary>
        /// Threshold speed for the p5 and p8.
        /// </summary>
        public int P6_DecProbabilitySpeedBound
        {
            get { return parameters.TransitionFunctionParameters.P6_DecProbabilitySpeedBound; }
            set
            {
                parameters.TransitionFunctionParameters.P6_DecProbabilitySpeedBound = value;
                OnPropertyChanged("P6_DecProbabilitySpeedBound");
            }
        }

        /// <summary>
        /// Acceleration probability.
        /// </summary>
        public double P7_AccelerationProbability
        {
            get { return parameters.TransitionFunctionParameters.P7_AccelerationProbability; }
            set
            {
                parameters.TransitionFunctionParameters.P7_AccelerationProbability = value;
                OnPropertyChanged("P7_AccelerationProbability");
            }
        }

        /// <summary>
        /// High speed deceleration probability.
        /// </summary>
        public double P8_HighSpeedDecProbability
        {
            get { return parameters.TransitionFunctionParameters.P8_HighSpeedDecProbability; }
            set
            {
                parameters.TransitionFunctionParameters.P8_HighSpeedDecProbability = value;
                OnPropertyChanged("P8_HighSpeedDecProbability");
            }
        }

        /// <summary>
        /// Deceleration rate when leading vehicle accelerates.
        /// </summary>
        public double P9_DecRateWhenLeadingAcc
        {
            get { return parameters.TransitionFunctionParameters.P9_DecRateWhenLeadingAcc; }
            set
            {
                parameters.TransitionFunctionParameters.P9_DecRateWhenLeadingAcc = value;
                OnPropertyChanged("P9_DecRateWhenLeadingAcc");
            }
        }

        /// <summary>
        /// Deceleration rate when leading vehicle decelerates.
        /// </summary>
        public double P10_DecRateWhenLeadingDec
        {
            get { return parameters.TransitionFunctionParameters.P10_DecRateWhenLeadingDec; }
            set
            {
                parameters.TransitionFunctionParameters.P10_DecRateWhenLeadingDec = value;
                OnPropertyChanged("P10_DecRateWhenLeadingDec");
            }
        }

        /// <summary>
        /// Command for default settings.
        /// </summary>
        private ICommand defaultCommand;
        /// <summary>
        /// Command for default settings.
        /// </summary>        
        public ICommand DefaultCommand
        {
            get
            {
                if (defaultCommand == null)
                {
                    defaultCommand = new RelayCommand(i => SetDefaults());
                }
                return defaultCommand;
            }
        }

        /// <summary>
        /// Command for import the parameters.
        /// </summary>
        private ICommand importCommand;
        /// <summary>
        /// Command for import the parameters.
        /// </summary>        
        public ICommand ImportCommand
        {
            get
            {
                if (importCommand == null)
                {
                    importCommand = new RelayCommand(i => Import());
                }
                return importCommand;
            }
        }

        /// <summary>
        /// Command for export of the parameters.
        /// </summary>
        private ICommand exportCommand;
        /// <summary>
        /// Command for export of the parameters.
        /// </summary>        
        public ICommand ExportCommand
        {
            get
            {
                if (exportCommand == null)
                {
                    exportCommand = new RelayCommand(i => Export());
                }
                return exportCommand;
            }
        }

        /// <summary>
        /// Command for confirming parameters.
        /// </summary>
        private ICommand okCommand;
        /// <summary>
        /// Command for confirming parameters.
        /// </summary>        
        public ICommand OkCommand
        {
            get
            {
                if (okCommand == null)
                {
                    okCommand = new RelayCommand(i => UseParameters());
                }
                return okCommand;
            }
        }

        /// <summary>
        /// Constructor to parameters view model.
        /// </summary>
        /// <param name="messenger">Reference to messenger.</param>
        /// <param name="settings">Reference to settings</param>
        /// <param name="interactions">Reference to interactions.</param>
        public ParametersViewModel(Messenger messenger, ISettings settings, IInteractions interactions)
        {
            this.messenger = messenger;
            this.settings = settings;
            this.interactions = interactions;
        }

        /// <summary>
        /// Initialize parameters view model by getting last used or default parameters.
        /// </summary>
        public void Initialize()
        {
            if (PathToParameters == string.Empty)
            {
                SetDefaults();
            }
            else
            {
                try
                {
                    Import(PathToParameters);
                }
                catch
                {
                    PathToParameters = string.Empty;
                    SetDefaults();
                }
            }
        }

        /// <summary>
        /// Sets defaults for all parameters.
        /// </summary>        
        private void SetDefaults()
        {
            parameters = new Parameters();
            P1_CellLength = 1.0;
            P2_SimulationStepInterval = TimeSpan.FromSeconds(1.0);
            P5_LowSpeedDecProbability = 0.2;
            P6_DecProbabilitySpeedBound = 15;
            P7_AccelerationProbability = 0.8;
            P8_HighSpeedDecProbability = 0.1;
            P9_DecRateWhenLeadingAcc = 2.0;
            P10_DecRateWhenLeadingDec = 3.0;
            PathToParameters = string.Empty;
            OnPropertyChanged(string.Empty);
        }

        /// <summary>
        /// Imports parameters from file.
        /// </summary>        
        private void Import()
        {
            string pathToOpen = interactions.GetPathToOpenParametersFile();
            if (!string.IsNullOrEmpty(pathToOpen))
            {
                Import(pathToOpen);
            }
        }

        /// <summary>
        /// Imports parameters from file on specified path.
        /// </summary>
        /// <param name="path">The path with file storing the parameters.</param>
        private void Import(string path)
        {
            try
            {
                parameters = Parameters.Deserialize(path);
                PathToParameters = path;
                OnPropertyChanged(string.Empty);
            }
            catch (Exception ex)
            {
                interactions.ScreamErrorMessage(ex.Message);
            }
        }


        /// <summary>
        /// Exports parameters from file.
        /// </summary>
        private void Export()
        {
            string pathToSave = interactions.GetPathToSaveParametersFile();
            if (!string.IsNullOrEmpty(pathToSave))
            {
                try
                {
                    parameters.Serialize(pathToSave);
                    PathToParameters = pathToSave;
                }
                catch (Exception ex)
                {
                    interactions.ScreamErrorMessage(ex.Message);
                }
            }
        }

        /// <summary>
        /// Use currently set parameters.
        /// </summary>        
        private void UseParameters()
        {
            messenger.GetEvent<ParametersMessage>().Publish(parameters);
        }
    }
}
