using Microscopic_Traffic_Simulator.Renderers;
using Microscopic_Traffic_Simulator.ViewModels;
using System.Collections.Generic;
using System.Windows;

namespace Microscopic_Traffic_Simulator.Views.MainWindowView
{
    /// <summary>
    /// Previewer of building roads on canvas.
    /// </summary>
    class CanvasRoadPreviewer
    {
        /// <summary>
        /// Current renderer for previewing roads according to selected lane type.
        /// </summary>
        private LaneBuildingRenderer currentLaneBuildingRenderer;

        /// <summary>
        /// Mapping from lane type to lane building renderer.
        /// </summary>
        private Dictionary<LaneType, LaneBuildingRenderer> laneTypeToRenderer;

        /// <summary>
        /// Topology renderer reference.
        /// </summary>
        private TopologyRenderer topologyRenderer;

        /// <summary>
        /// Canvas view model reference.
        /// </summary>
        private CanvasViewModel canvasViewModel;

        /// <summary>
        /// Previously set lane type.
        /// </summary>
        private LaneType previousLaneType;

        /// <summary>
        /// Initialization of canvas road previewer.
        /// </summary>
        /// <param name="laneTypeToRenderer">Mapping from lane type to lane building renderer.</param>
        /// <param name="topologyRenderer">Topology renderer reference.</param>
        internal CanvasRoadPreviewer(Dictionary<LaneType, LaneBuildingRenderer> laneTypeToRenderer,
            TopologyRenderer topologyRenderer)
        {
            this.laneTypeToRenderer = laneTypeToRenderer;
            this.topologyRenderer = topologyRenderer;
            currentLaneBuildingRenderer = laneTypeToRenderer[previousLaneType];
        }

        /// <summary>
        /// Performs necessary actions after changing lane type including transfering compatible points between
        /// different lane renderers.
        /// </summary>
        internal void LaneTypeWasChanged()
        {
            if (previousLaneType != canvasViewModel.CurrentLaneType)
            {
                foreach (KeyValuePair<LaneType, LaneBuildingRenderer> laneTypeRendererTuple in laneTypeToRenderer)
                {
                    if (laneTypeRendererTuple.Key == canvasViewModel.CurrentLaneType)
                    {
                        laneTypeToRenderer[canvasViewModel.CurrentLaneType].TransferPoints(currentLaneBuildingRenderer);
                    }
                }
                currentLaneBuildingRenderer = laneTypeToRenderer[canvasViewModel.CurrentLaneType];
                foreach (KeyValuePair<LaneType, LaneBuildingRenderer> laneTypeRendererTuple in laneTypeToRenderer)
                {
                    if (laneTypeRendererTuple.Key != canvasViewModel.CurrentLaneType)
                    {
                        laneTypeRendererTuple.Value.ResetRendererAndClearAnyPreview();
                    }
                }
                currentLaneBuildingRenderer.RenderBuildingLane();
                previousLaneType = canvasViewModel.CurrentLaneType;
            }
        }

        /// <summary>
        /// Clears preview of roads which are currently being built.
        /// </summary>
        internal void ClearPreviewOfBuildingOfAnyRoads()
        {
            currentLaneBuildingRenderer.ResetRendererAndClearAnyPreview();
        }

        /// <summary>
        /// Actions performed on releasing left mouse button. Depending on the state of the building either the 
        /// dragging of the lane is reset or new lane is built and added to geometric topology or new point
        /// is set for preview.
        /// </summary>
        /// <param name="cursorCanvasLocation">Cursor canvas location.</param>
        internal void MouseLeftButtonUp(Point cursorCanvasLocation)
        {
            if (currentLaneBuildingRenderer.IsSomePointBeingDragged)
            {
                // resets dragginf of point
                currentLaneBuildingRenderer.ResetDraggingOfPoint();
            }
            else if (currentLaneBuildingRenderer.IsLaneDefined)
            {
                // builds new road and re-render current lane renderer and topology renderer
                if (canvasViewModel.CurrentLaneType == LaneType.Straight)
                {
                    canvasViewModel.BuildNewStraightLane(currentLaneBuildingRenderer.StartWorldPoint.Value,
                        currentLaneBuildingRenderer.EndWorldPoint.Value);
                }
                else if (canvasViewModel.CurrentLaneType == LaneType.Bezier)
                {
                    BezierLaneBuildingRenderer bezierLaneBuildingRenderer =
                        currentLaneBuildingRenderer as BezierLaneBuildingRenderer;
                    canvasViewModel.BuildNewBezierLaneLane(bezierLaneBuildingRenderer.StartWorldPoint.Value,
                        bezierLaneBuildingRenderer.FirstWorldControlPoint.Value,
                        bezierLaneBuildingRenderer.SecondWorldControlPoint.Value,
                        bezierLaneBuildingRenderer.EndWorldPoint.Value);
                }
                currentLaneBuildingRenderer.ResetRendererAndClearAnyPreview();
                topologyRenderer.RenderTopology();
            }
            else
            {
                // sets new point of a lane which is previewed
                currentLaneBuildingRenderer.SetPoint(cursorCanvasLocation);
            }
        }

        /// <summary>
        /// Drags point if previewing lane is fully defined.
        /// </summary>
        /// <param name="cursorCanvasLocation">Cursor canvas location.</param>
        internal void MouseLeftButtonDown(Point cursorCanvasLocation)
        {
            if (currentLaneBuildingRenderer.IsLaneDefined)
            {
                currentLaneBuildingRenderer.InitializeDraggingModeOfAPointIfAnyIsNear(cursorCanvasLocation);
            }
        }

        /// <summary>
        /// Re-render currently active lane preview renderer.
        /// </summary>
        /// <param name="cursorCanvasLocation">Cursor canvas location.</param>
        internal void RenderBuildingLane(Point cursorCanvasLocation)
        {
            currentLaneBuildingRenderer.RenderBuildingLane(cursorCanvasLocation);
        }

        /// <summary>
        /// Move point which is in dragging mode when the previewing lane is fully defined.
        /// </summary>
        /// <param name="cursorCanvasLocation">Cursor canvas location.</param>
        internal void MovePointOfLaneIfCursorIsNear(Point cursorCanvasLocation)
        {
            if (currentLaneBuildingRenderer.IsLaneDefined)
            {
                currentLaneBuildingRenderer.MovePointOfLane(cursorCanvasLocation);
            }
        }

        /// <summary>
        /// Sets canvas view model reference.
        /// </summary>
        /// <param name="canvasViewModel">Reference of canvas view model to be set in canvas road previewer.</param>
        internal void SetCanvasViewModel(CanvasViewModel canvasViewModel)
        {
            this.canvasViewModel = canvasViewModel;
        }
    }
}
