using Microscopic_Traffic_Simulator.Renderers;
using Microscopic_Traffic_Simulator.ViewModels;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;

namespace Microscopic_Traffic_Simulator.Views.MainWindowView
{
    /// <summary>
    /// Interaction logic for SimulationControlUserControl.xaml
    /// </summary>
    public partial class SimulationControlUserControl : UserControl
    {
        /// <summary>
        /// Timer for rendering simulation state.
        /// </summary>
        private DispatcherTimer timer;

        /// <summary>
        /// Instance of viewmodel associated with this user control.
        /// </summary>
        private SimulationControlViewModel viewModel;

        /// <summary>
        /// Initialize control.
        /// </summary>
        public SimulationControlUserControl()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Tick of timer when simulation is rendered.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">Unused event args.</param>
        private void timer_Tick(object sender, EventArgs e)
        {
            (Window.GetWindow(this) as MainWindow).canvasUserControl
                .SimulationTrafficRenderer.RenderSimulationTraffic(Rendering.Continuous);
        }

        /// <summary>
        /// Connect event receivers to commands' CommandExecuted event handlers to perform
        /// post command UI operations. Initialize timer.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">Unused event args.</param>
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            viewModel = DataContext as SimulationControlViewModel;
            (viewModel.ForwardCommand as ObservableRelayCommand)
                .CommandExecuted += SimulationControlUserControl_StartTimer;
            (viewModel.StopCommand as ObservableRelayCommand)
                .CommandExecuted += SimulationControlUserControl_StopCommandExecuted;
            (viewModel.PauseCommand as ObservableRelayCommand)
                .CommandExecuted += SimulationControlUserControl_PauseCommandExecuted;
            (viewModel.SwitchToConstructionModeCommand as ObservableRelayCommand)
                .CommandExecuted += SimulationControlUserControl_StopCommandExecuted;
            (viewModel.RestartCommand as ObservableRelayCommand).CommandExecuted += 
                SimulationControlUserControl_RestartCommandExecuted;
            viewModel.CellularTopology.Simulation.SimulationPaused += Simulation_SimulationPaused;
            timer = new DispatcherTimer(DispatcherPriority.Render);
            timer.Tick += timer_Tick;
            timer.Interval = TimeSpan.FromMilliseconds(1000.0 / sliderGraphicsTimer.Value);
        }

        /// <summary>
        /// Disconnect event receivers to commands' CommandExecuted event handlers to perform
        /// post command UI operations. Disconnect tick from timer.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">Unused event args.</param>
        private void UserControl_Unloaded(object sender, RoutedEventArgs e)
        {
            (viewModel.ForwardCommand as ObservableRelayCommand)
                .CommandExecuted -= SimulationControlUserControl_StartTimer;
            (viewModel.StopCommand as ObservableRelayCommand)
                .CommandExecuted -= SimulationControlUserControl_StopCommandExecuted;
            (viewModel.PauseCommand as ObservableRelayCommand)
                .CommandExecuted -= SimulationControlUserControl_PauseCommandExecuted;
            (viewModel.SwitchToConstructionModeCommand as ObservableRelayCommand)
                .CommandExecuted -= SimulationControlUserControl_StopCommandExecuted;
            (viewModel.RestartCommand as ObservableRelayCommand).CommandExecuted -= 
                SimulationControlUserControl_RestartCommandExecuted;
            viewModel.CellularTopology.Simulation.SimulationPaused -= Simulation_SimulationPaused;
            timer.Tick -= timer_Tick;
        }

        /// <summary>
        /// When simulation is paused the buttons IsEnabled states have to be refreshed.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">Not used event args.</param>
        private void Simulation_SimulationPaused(object sender, EventArgs e)
        {
            timer.Stop();
            Application.Current.Dispatcher.Invoke(() => CommandManager.InvalidateRequerySuggested(),
                DispatcherPriority.Render);
        }

        /// <summary>
        /// Event receiver for starting the timer after the forward button is pressed and command
        /// of running simulation is completed.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">Unused event args.</param>
        private void SimulationControlUserControl_StartTimer(object sender, EventArgs e)
        {
            timer.Start();
        }

        /// <summary>
        /// Event receiver for stopping the timer and rendering the simulation traffic for one extra
        /// time after the stop or pause button are pressed and command of stopping or pausing
        /// simulation is completed.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">Unused event args.</param>
        private void SimulationControlUserControl_PauseCommandExecuted(object sender, EventArgs e)
        {
            timer.Stop();
            (Window.GetWindow(this) as MainWindow).canvasUserControl.SimulationTrafficRenderer.Pause();
        }

        /// <summary>
        /// Event receiver for stopping the timer and cleaning the simulation canvas.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">Unused event args.</param>
        private void SimulationControlUserControl_StopCommandExecuted(object sender, EventArgs e)
        {
            timer.Stop();
            (Window.GetWindow(this) as MainWindow).canvasUserControl.SimulationTrafficRenderer.Stop();
        }

        /// <summary>
        /// Event when slider value for higher simulation traffic rendering fps is changed.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">Unused event args.</param>
        private void sliderGraphicsTimer_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (timer != null)
            {
                timer.Stop();
                timer.Interval = TimeSpan.FromMilliseconds(1000.0 / sliderGraphicsTimer.Value);
                timer.Start();
            }
        }

        /// <summary>
        /// When button for applying alarm is clicked.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">Not used event args.</param>
        private void ApplyAlarmButton_Click(object sender, RoutedEventArgs e)
        {
            btnAlarm.IsChecked = false;
        }

        /// <summary>
        /// Occurs when button for return to construction mode is clicked. The event handlers of cellular topology.
        /// are detached.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">Not used event args.</param>
        private void SwitchToConstructionModeButton_Click(object sender, RoutedEventArgs e)
        {
            (Window.GetWindow(this) as MainWindow).canvasUserControl.SimulationTrafficRenderer
                .DetachEventHandlerFromCellularTopology();
        }

        /// <summary>
        /// Occurs when Forward button is clicked. Necessary renderer actions are performed.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">Unused event args.</param>
        private void ForwardButton_Click(object sender, RoutedEventArgs e)
        {
            (Window.GetWindow(this) as MainWindow).canvasUserControl.SimulationTrafficRenderer.Run();
        }

        /// <summary>
        /// Occurs when Forward step button is clicked. Necessary renderer actions are performed.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">Unused event args.</param>
        private void ForwardStepButton_Click(object sender, RoutedEventArgs e)
        {
            (Window.GetWindow(this) as MainWindow).canvasUserControl.SimulationTrafficRenderer.StepForward();
        }

        /// <summary>
        /// Occurs when Restart button is clicked. Necesarry renderer actions are performed.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">Unused event args.</param>
        private void SimulationControlUserControl_RestartCommandExecuted(object sender, EventArgs e)
        {
            SimulationTrafficRenderer renderer = 
                (Window.GetWindow(this) as MainWindow).canvasUserControl.SimulationTrafficRenderer;
            renderer.Stop();
            renderer.Run();
            if (!timer.IsEnabled)
            {
                timer.Start();
            }
        }
    }
}
