using Microscopic_Traffic_Simulator.ViewModels.Messages;
using Microscopic_Traffic_Simulator___Model.SimulationControl;
using System;
using System.IO;
using System.Windows.Input;

namespace Microscopic_Traffic_Simulator.ViewModels
{
    /// <summary>
    /// Main application viewmodel.
    /// </summary>
    class MainViewModel : ViewModelBase
    {
        /// <summary>
        /// Interaction implementation for questions.
        /// </summary>
        private IInteractions interactions;

        /// <summary>
        /// Application settings implementation.
        /// </summary>
        private ISettings settings;

        /// <summary>
        /// Action for switching language.
        /// </summary>
        private Action<string> switchLanguage;

        /// <summary>
        /// Event for initialization or opening of new topology.
        /// </summary>
        public event EventHandler AnotherTopologyInitializedOrOpened;

        /// <summary>
        /// Viewmodel of current top panel.
        /// </summary>
        private ViewModelBase currentTopPanelViewModel;
        /// <summary>
        /// Viewmodel of current top panel.
        /// </summary>
        public ViewModelBase CurrentTopPanelViewModel
        {
            get { return currentTopPanelViewModel; }
            set { currentTopPanelViewModel = value; OnPropertyChanged("CurrentTopPanelViewModel"); }
        }

        /// <summary>
        /// Viewmodel for canvas panel.
        /// </summary>
        private CanvasViewModel canvasViewModel;
        /// <summary>
        /// Viewmodel for canvas panel.
        /// </summary>        
        public CanvasViewModel CanvasViewModel { get { return canvasViewModel; } }

        /// <summary>
        /// Viewmodel for construction panel.
        /// </summary>
        private ConstructionViewModel constructionViewModel;
        /// <summary>
        /// Viewmodel for construction panel.
        /// </summary>        
        public ConstructionViewModel ConstructionViewModel
        {
            get { return constructionViewModel; }
        }

        /// <summary>
        /// Viewmodel for simulation control panel.
        /// </summary>
        private SimulationControlViewModel simulationControlViewModel;
        /// <summary>
        /// Viewmodel for simulation control panel.
        /// </summary>        
        public SimulationControlViewModel SimulationControlViewModel
        {
            get { return simulationControlViewModel; }
        }

        /// <summary>
        /// Name of geometric topology derived from file name where topology was saved or
        /// the name is assigned to "new".
        /// </summary>
        private string geometricTopologyName;
        /// <summary>
        /// Name of geometric topology derived from file name where topology was saved or
        /// the name is assigned to "new".
        /// </summary>
        public string GeometricTopologyName
        {
            get { return geometricTopologyName; }
            set { geometricTopologyName = value; OnPropertyChanged("GeometricTopologyName"); }
        }

        /// <summary>
        /// Flag indicating whether all changes of topology were saved.
        /// </summary>
        private bool hasChangesSaved;
        /// <summary>
        /// Flag indicating whether all changes of topology were saved.
        /// </summary>        
        public bool HasChangesSaved
        {
            get { return hasChangesSaved; }
            set { hasChangesSaved = value; OnPropertyChanged("HasChangesSaved"); }
        }

        /// <summary>
        /// Flag indicating whether it is possible to close application after user request without
        /// asking about whether to saves changes. If current geometric topology was not never saved
        /// and it is empty it is not needed to ask for save despite the false value of 
        /// HasChangesSaved.
        /// </summary>
        private bool HasChangesSavedOrIsNeverSavedAndEmpty
        {
            get
            {
                return hasChangesSaved ||
                    (string.IsNullOrEmpty(settings.LastGeometricTopologyPath) &&
                    (ConstructionViewModel.GeometricTopology == null ||
                    constructionViewModel.GeometricTopology.IsEmpty));
            }
        }

        /// <summary>
        /// Current culture (language) of application.
        /// </summary>
        private string uiCultureName;
        /// <summary>
        /// Current culture (language) of application.
        /// </summary>        
        public string UICultureName
        {
            get { return uiCultureName; }
            set { uiCultureName = value; OnPropertyChanged("UICultureName"); }
        }

        /// <summary>
        /// Command for creating new geometric topology.
        /// </summary>
        private ICommand newGeometricTopologyCommand;
        /// <summary>
        /// Command for creating new geometric topology.
        /// </summary>        
        public ICommand NewGeometricTopologyCommand
        {
            get
            {
                if (newGeometricTopologyCommand == null)
                {
                    newGeometricTopologyCommand =
                        new ObservableRelayCommand(i => NewGeometricTopology((string)i));
                }
                return newGeometricTopologyCommand;
            }
        }

        /// <summary>
        /// Command for opening geometric topology.
        /// </summary>
        private ICommand openGeometricTopologyCommand;
        /// <summary>
        /// Command for opening geometric topology.
        /// </summary>        
        public ICommand OpenGeometricTopologyCommand
        {
            get
            {
                if (openGeometricTopologyCommand == null)
                {
                    openGeometricTopologyCommand =
                        new ObservableRelayCommand(i => ChooseGeometricTopologyAndOpenIt());
                }
                return openGeometricTopologyCommand;
            }
        }

        /// <summary>
        /// Command for saving geometric topology as custom file.
        /// </summary>
        private ICommand saveGeometricTopologyAsCommand;
        /// <summary>
        /// Command for saving geometric topology as custom file.
        /// </summary>        
        public ICommand SaveGeometricTopologyAsCommand
        {
            get
            {
                if (saveGeometricTopologyAsCommand == null)
                {
                    saveGeometricTopologyAsCommand = new RelayCommand(
                        i => ChooseGeometricTopologyAndSaveItAs());
                }
                return saveGeometricTopologyAsCommand;
            }
        }

        /// <summary>
        /// Command for saving geometric topology to file of current topology.
        /// </summary>
        private ICommand saveGeometricTopologyCommand;
        /// <summary>
        /// Command for saving geometric topology to file of current topology.
        /// </summary>        
        public ICommand SaveGeometricTopologyCommand
        {
            get
            {
                if (saveGeometricTopologyCommand == null)
                {
                    saveGeometricTopologyCommand = new RelayCommand(
                        i => ChooseGeometricTopologyAndSaveIt(), i => !hasChangesSaved);
                }
                return saveGeometricTopologyCommand;
            }
        }

        /// <summary>
        /// Command for switching language of application.
        /// </summary>
        private ICommand switchLanguageCommand = null;
        /// <summary>
        /// Command for switching language of application.
        /// </summary>        
        public ICommand SwitchLanguageCommand
        {
            get
            {
                if (switchLanguageCommand == null)
                {
                    switchLanguageCommand = new RelayCommand(
                        uiCultureName => SwitchLanguage((string)uiCultureName));
                }
                return switchLanguageCommand;
            }
        }

        /// <summary>
        /// Initialization of main viewmodel.
        /// </summary>
        /// <param name="interactions">Implementation of interactions to questions.</param>
        /// <param name="settings">Implementation of application settings.</param>
        /// <param name="switchLanguage">Action to apply after switching language.</param>        
        public MainViewModel(IInteractions interactions, ISettings settings, Action<string> switchLanguage)
        {
            messenger = new Messenger();
            canvasViewModel = new CanvasViewModel(messenger);
            constructionViewModel = new ConstructionViewModel(messenger);
            simulationControlViewModel = new SimulationControlViewModel(messenger, interactions, settings);
            currentTopPanelViewModel = constructionViewModel;

            messenger.GetEvent<ConstructionViewModel.BuildCellularTopologyMessage>().Subscribe(
                (geometricTopology, parameters) => CurrentTopPanelViewModel = simulationControlViewModel);
            messenger.GetEvent<SimulationControlViewModel.SwitchToConstructionModeMessage>().Subscribe(
                () => CurrentTopPanelViewModel = constructionViewModel);
            messenger.GetEvent<ConstructionViewModel.GeometricTopologyModifiedMessage>().Subscribe(
                () => HasChangesSaved = false);

            this.interactions = interactions;
            this.settings = settings;
            this.switchLanguage = switchLanguage;            
        }

        /// <summary>
        /// Creates new or open last opened geometric topology.
        /// </summary>
        /// <param name="newTopologyName">String defining name of topology name in case that
        /// no previous opened topology was found.</param>
        /// <param name="languageOnStartup">Language to be set after loading application.</param>
        public void Initialize(string newTopologyName, string languageOnStartup)
        {
            UICultureName = languageOnStartup;
            if (string.IsNullOrEmpty(settings.LastGeometricTopologyPath) ||
                !File.Exists(settings.LastGeometricTopologyPath))
            {
                NewGeometricTopology(newTopologyName);
            }
            else
            {
                try
                {
                    OpenGeometricTopology(settings.LastGeometricTopologyPath);
                }
                catch
                {
                    settings.LastGeometricTopologyPath = string.Empty;
                    NewGeometricTopology(newTopologyName);
                }
            }
        }

        /// <summary>
        /// Creates new geometric topology.
        /// </summary>
        /// <param name="newTopologyName">Name of the new geometric topology.</param>
        internal void NewGeometricTopology(string newTopologyName)
        {
            //check whether not to be saved the potential currently opened topology.
            if (!TestIfCurrentTopologyChangesAreSavedAndAskForAction())
                return;

            GeometricTopologyName = newTopologyName;
            settings.LastGeometricTopologyPath = null;
            settings.Save();
            constructionViewModel.CreateNewGeometricTopology();
            HasChangesSaved = false;
            OnAnotherTopologyCreatedOrOpened();
            CurrentTopPanelViewModel = constructionViewModel;
        }

        /// <summary>
        /// Opens geometric topology.
        /// </summary>
        /// <param name="path">Path to file with geometric topology.</param>
        private void OpenGeometricTopology(string path)
        {
            constructionViewModel.OpenGeometricTopology(path);
            GeometricTopologyName = Path.GetFileNameWithoutExtension(path);
            HasChangesSaved = true;
            settings.LastGeometricTopologyPath = path;
            settings.Save();
            OnAnotherTopologyCreatedOrOpened();
            CurrentTopPanelViewModel = constructionViewModel;
        }

        /// <summary>
        /// Saves geometric topology.
        /// </summary>
        /// <param name="path">Path to file the geometric topology to be saved.</param>
        private void SaveGeometricTopology(string path)
        {
            constructionViewModel.SaveGeometricTopology(path);
            GeometricTopologyName = Path.GetFileNameWithoutExtension(path);
            HasChangesSaved = true;
            settings.LastGeometricTopologyPath = path;
            settings.Save();
        }

        /// <summary>
        /// Let interactions choose to select file with geometric topology to open.
        /// </summary>
        private void ChooseGeometricTopologyAndOpenIt()
        {
            if (!TestIfCurrentTopologyChangesAreSavedAndAskForAction())
                return;

            string pathToOpeningTopologyFile = interactions.GetPathToOpenTopologyFile();
            if (pathToOpeningTopologyFile == null)
                return;
            try
            {
                OpenGeometricTopology(pathToOpeningTopologyFile);
            }
            catch (Exception ex)
            {
                interactions.ScreamErrorMessage(ex.Message);
            }
        }

        /// <summary>
        /// Saves geometric topology to its file and in case that it has not already had the file
        /// the action is treatened like save-as operation.
        /// </summary>
        private void ChooseGeometricTopologyAndSaveIt()
        {
            if (settings.LastGeometricTopologyPath == null)
                ChooseGeometricTopologyAndSaveItAs();
            else
            {
                try
                {
                    SaveGeometricTopology(settings.LastGeometricTopologyPath);
                }
                catch (Exception ex)
                {
                    interactions.ScreamErrorMessage(ex.Message);
                }
            }
        }

        /// <summary>
        /// Let interatctions choose to select custom file 
        /// the geometric topology to save topology into.
        /// </summary>
        private void ChooseGeometricTopologyAndSaveItAs()
        {
            string pathToSaveTopologyFile = interactions.GetPathToSaveTopologyFile();
            if (pathToSaveTopologyFile == null)
                return;
            try
            {
                SaveGeometricTopology(pathToSaveTopologyFile);
            }
            catch (Exception ex)
            {
                interactions.ScreamErrorMessage(ex.Message);
            }
        }

        /// <summary>
        /// Test whether is needed to ask user whether to save changes in topology.
        /// </summary>
        /// <returns>True/False whether to continue caller action or to interrupt it.</returns>
        private bool TestIfCurrentTopologyChangesAreSavedAndAskForAction()
        {
            if (!HasChangesSavedOrIsNeverSavedAndEmpty)
            {
                bool? result = interactions.SaveChangesYesNoCancel();

                if (result == null)
                    return false;
                else if (result.Value)
                {
                    string savePath;
                    if (settings.LastGeometricTopologyPath == null)
                    {
                        savePath = interactions.GetPathToSaveTopologyFile();
                        if (savePath == null)
                            return false;
                    }
                    else
                        savePath = settings.LastGeometricTopologyPath;
                    try
                    {
                        SaveGeometricTopology(savePath);
                    }
                    catch (Exception ex)
                    {
                        interactions.ScreamErrorMessage(ex.Message);
                        return false;
                    }
                }
            }
            return true;
        }

        /// <summary>
        /// Method for switching the language.
        /// </summary>
        /// <param name="uiCultureName">Culture code of language.</param>
        private void SwitchLanguage(string uiCultureName)
        {
            switchLanguage(uiCultureName);
            UICultureName = uiCultureName;
        }

        /// <summary>
        /// Method to determine whether the UI associated with this viewmodel can be closed.
        /// </summary>
        /// <returns>True/False for Yes/No</returns>
        internal bool CanBeClosed()
        {
            return TestIfCurrentTopologyChangesAreSavedAndAskForAction();
        }

        /// <summary>
        /// Publishing information about initialization or opening of another topology.
        /// </summary>
        private void OnAnotherTopologyCreatedOrOpened()
        {
            if (CurrentTopPanelViewModel == SimulationControlViewModel && 
                SimulationControlViewModel.CellularTopology.Simulation.SimulationState != SimulationState.NotRunning)
            {
                SimulationControlViewModel.CellularTopology.GeneratorsManager.DetachFromGeneratorEvents();
            }
            if (AnotherTopologyInitializedOrOpened != null)
                AnotherTopologyInitializedOrOpened(this, EventArgs.Empty);
        }

        /// <summary>
        /// Returns parameters view model.
        /// </summary>
        internal ParametersViewModel GetParametersViewModel()
        {
            ParametersViewModel parametersViewModel = new ParametersViewModel(messenger, settings, interactions);
            parametersViewModel.Initialize();
            return parametersViewModel;
        }
    }
}
