using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Windows;
using System.Windows.Media;
using Caliburn.Micro;

using FDOT.GIS.Client.Domain;
using FDOT.GIS.Client.Domain.Drawing;
using FDOT.GIS.Client.Domain.Graphics;
using FDOT.GIS.Client.Domain.Measurement;
using FDOT.GIS.Client.Domain.UnitConversion;
using FDOT.GIS.Client.Helpers;
using FDOT.GIS.Client.ViewModels;


namespace FDOT.GIS.Framework.Examples.ViewModels
{
    [Export]
    [PartCreationPolicy(CreationPolicy.Shared)]
    [DisplayName("My Component")]
    public class MyComponentViewModel : ComponentBase
    {
        private GeometrySelectionService _geometryService;
        private GenericDialogViewModel _dialogViewModel;
        private GraphicsManager _graphicsManager;
        private bool _isDrawing;

        private Unit _linearUnit = Unit.Mile;
        private SquareUnit _areaUnit = SquareUnit.SquareMile;

        public MyComponentViewModel() { }

        private double _perimter = 0d;
        [Label]
        [DisplayName("Perimeter: ")]
        public string Perimeter
        {
            get { return string.Format("{0} {1}", _perimter, UnitConverter.GetLabel(_linearUnit)); }
            set
            {
                double.TryParse(value, out _perimter);
                NotifyOfPropertyChange(() => Perimeter);
            }
        }


        private double _area = 0d;
        [Label]
        [DisplayName("Area: ")]
        public string Area
        {
            get { return string.Format("{0} {1}", _area, UnitConverter.GetLabel(_areaUnit)); }
            set
            {
                double.TryParse(value, out _area);
                NotifyOfPropertyChange(() => Area);
            }
        }


        [Action]
        [DisplayName("Start Drawing")]
        public void StartDrawing()
        {
            // if something is already being drawn, cancel that drawing and start a new one
            if (_isDrawing)
            { _geometryService.CancelSelection(); }
            _isDrawing = true;


            // SelectGeometry starts drawing an object of the specified type and returns to this event handler when
            // the drawing object raises its DrawComplete event.
            _geometryService.SelectGeometry(GeometryType.Box, DrawCompleteCallback, "Select geometry for measurement.",
                                        GeometryChangeDelegate, _linearUnit, _areaUnit);

            
            // The following is an alternative for geometry selection that doesn't include a change delegate.  This can be
            // used in situations where properties of the object aren't needed until it's been completed.
            //_geometryService.SelectGeometry(GeometryType.Polyline, g =>
            //{
            //    // The drawing is done, so set the flag and add the graphic to the map.
            //    // Note that the symbol, map tip, and attributes can also be set on the drawing object here.
            //    _isDrawing = false;
            //    _graphicsManager.AddGraphic(g, null, null, null);
            //});
        }


        [Action]
        [DisplayName("Clear Drawing")]
        public void Clear()
        {
            if (_isDrawing)
            { 
                _geometryService.CancelSelection();
                _isDrawing = false;
            }

            _graphicsManager.ClearGraphics();
            Area = null;
            Perimeter = null;
        }


        /// <summary>
        /// The drawing operation is complete, so add the geometry to the graphics manager.
        /// </summary>
        /// <param name="geometry">The completed drawing object.</param>
        private void DrawCompleteCallback(DataContracts.Geometry geometry)
        {
            // Objects are rendered to the map using a ESRI.ArcGIS.Client.Symbols.Symbol and can be point, line, or fill.
            // The GIS Framework has defaults for each symbol type, but the colors can be changed either for all defaults or
            // for an individual symbol.  Also, a custom symbol can be provided to the GraphicsManager.AddGraphic method.
            // The default fill symbol is made up of 3 colors for the outline and interior hatch pattern.  The fill can be
            // set with all 3 colors or by simply providing 1 color and letting the framework shift the hue of the others
            // to match it.

            // This changes the default palette for all symbols rendered for the duration of the application:
            //      SymbolManager.SetPalette("#FF0000");    // No transparency, equivalent to #FFFF0000.  For partial transparency, use an 8-character hex code, e.g. #55FF0000
            
            // This restores the default palette:
            //      SymbolManager.SetDefaultPalette();
            
            // This gets a fill symbol based on a single color without changing the default palette:
            //      SymbolManager.GetFillSymbol("#FF0000");

            // This gets a fill symbol with a hue shift without changing the default palette:
            //      SymbolManager.GetFillSymbol(0.1);
            // Note that the above is aimed at situations where multiple objects are being drawn and a different color is desired for each.
            // Placing that in a loop and incrementing the hue shift value allows the objects to be of different colors relative to the default
            // colors without having to specify different colors manually.

            // The drawing is done, so set the flag and add the graphic to the map.
            _isDrawing = false;
            _graphicsManager.AddGraphic(geometry, new SymbolDefinition("", "#FF0000"), null, null);

            
        }


        private void GeometryChangeDelegate(object sender, GeometryChangedEventArgs e)
        {
            // We can get a reference to the geometry being drawn from the GeometryChangedEventArgs
            DataContracts.Geometry envelope = e.CurrentGeometry;

            // And do something with the object as it's being drawn.  The following example uses the MeasureManager to
            // calculcate the perimiter and area of the envelope.
            DataContracts.Geometry polygon = GeometryTranslator.ConvertEnvelopeToPolygon(envelope);
            Area = Math.Round(MeasureManager.GetMeasureManager(_linearUnit, _areaUnit).GetPolygonArea(polygon), 2).ToString();
            Perimeter = Math.Round(MeasureManager.GetMeasureManager(_linearUnit, _areaUnit).GetPerimeter(polygon), 2).ToString();
        }


        #region IComponentBase implementation
        protected override string IconResourceUri
        {
            get { return "/FDOT.GIS.Framework.Examples;component/Assets/Images/MyComponent.png"; }
        }

        protected override void OnInitialize(IDictionary<string, string> customSettings)
        {
            // Initialize instances of the GraphicsManager and GeometrySelectionService
            _graphicsManager = GraphicManagerFactory.CreateGraphicsManager();
            _geometryService = new GeometrySelectionService(
                                    new GeometrySelectionServiceConfiguration 
                                        { 
                                            ShowLabels = true, 
                                            Color = Colors.Green 
                                        });
        }


        protected override void OnExecute()
        {
            // A logger is provided via ComponentBase
            Log.Info("Entered {0}.OnExecute().", this.GetType().ToString());

            _dialogViewModel = LayoutManager.Instance.ShowViewModel(this, 
                                          new DialogServiceArgs
                                          {
                                              Id="MyComponent",
                                              Title = this.ComponentConfiguration.Name,
                                              HelpId = "tools;" + GetType().FullName,
                                              Icon = Icon,
                                              ShowSettingsOnBack = true,
                                              SettingsViewModel = MyComponentSettingsViewModel.Instance
                                          });

            _dialogViewModel.Closed += OnClose;
        }


        private void OnClose(object sender, EventArgs e)
        {
            // do cleanup here
            if (_graphicsManager == null) return;
            _graphicsManager.ClearGraphics();       // clear the GraphicsManager - will remove all graphics
            _geometryService.CancelSelection();     // cancel any pending drawing operations
            _dialogViewModel = null;
        }

        protected override void OnError(ErrorEventArgs errorEventArgs)
        {
            Log.Error(errorEventArgs.Exception);
            MessageBox.Show(errorEventArgs.Exception.ToString());
            errorEventArgs.Handled = true;
        }

        #endregion
    }
}
