using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Factories.FactoriesService;
using System.Collections.ObjectModel;
using System.Windows.Browser;
using C1.Silverlight;
using System.Diagnostics;
using System.Collections;
using C1.Silverlight.Maps;
using System.Windows.Media.Imaging;
using System.ServiceModel;
using System.Reflection;


namespace Factories
{
    public partial class Page : UserControl
    {
        const double minStoreZoom = 10.5;
        
        DataBase localDataBase = InitializeDataBase();
        DataBase serverDataBase = InitializeDataBase();

        C1MapItemsLayer localFactories, serverFactories, localOffices, serverOffices;
        C1MapVirtualLayer localStores, serverStores;

        public Page()
        {
            InitializeComponent();
            InitializeMapLayers();            
            GetServerData();
            InitializeToolbar();
            InitializeCheckboxes();

            KeyDown += OnKeyDown;
        }

        void InitializeMapLayers()
        {
            localFactories = new C1MapItemsLayer
            {
                ItemsSource = localDataBase.Factories,
                ItemTemplate = factoryTemplate
            };

            serverFactories = new C1MapItemsLayer
            {
                ItemsSource = serverDataBase.Factories,
                ItemTemplate = factoryTemplate
            };

            localOffices = new C1MapItemsLayer
            {
                ItemsSource = localDataBase.Offices,
                ItemTemplate = officeTemplate
            };

            serverOffices = new C1MapItemsLayer
            {
                ItemsSource = serverDataBase.Offices,
                ItemTemplate = officeTemplate
            };

            int storeSlices = (int)Math.Pow(2, minStoreZoom);
            localStores = new C1MapVirtualLayer
            {
                Slices = {
                    new MapSlice(1, 1, 0),
                    new MapSlice(storeSlices, storeSlices, minStoreZoom)
                },
                MapItemsSource = new LocalStoreSource(localDataBase),
                ItemTemplate = storeTemplate
            };

            localDataBase.Stores.CollectionChanged += (s, e) => Dispatcher.BeginInvoke(() => localStores.ForceRefresh());
                        
            serverStores = new C1MapVirtualLayer
            {
                Slices = {
                    new MapSlice(1, 1, 0),
                    new MapSlice(storeSlices, storeSlices, minStoreZoom)
                },
                MapItemsSource = new ServerStoreSource(),
                ItemTemplate = storeTemplate
            };

            map.Layers.Add(localStores);
            map.Layers.Add(serverStores);

            map.Layers.Add(serverOffices);
            map.Layers.Add(localOffices);
            
            map.Layers.Add(serverFactories);
            map.Layers.Add(localFactories);
        }

        void InitializeCheckboxes()
        {
            labelsCheck.Checked += (s, e) => map.Source = new VirtualEarthHybridSource();
            labelsCheck.Unchecked += (s, e) => map.Source = new VirtualEarthAerialSource();
            labelsCheck.IsChecked = true;

            Action<CheckBox, UIElement, UIElement> init = (cb, ui1, ui2) =>
            {
                cb.Checked += (s, e) => ui1.Visibility = ui2.Visibility = Visibility.Visible;
                cb.Unchecked += (s, e) => ui1.Visibility = ui2.Visibility = Visibility.Collapsed;
                cb.IsChecked = true;
            };

            init(factoriesCheck, localFactories, serverFactories);
            init(officesCheck, localOffices, serverOffices);
            init(storesCheck, localStores, serverStores);
        }

        void InitializeToolbar()
        {
            ToolbarAdd("office", "Drag to add an office", OfficeGenerator, localDataBase.Offices);
            ToolbarAdd("factory", "Drag to add a factory", FactoryGenerator, localDataBase.Factories);
            var storeItem = ToolbarAdd("store", "Drag to add a store (zoom in to enable this)", StoreGenerator, localDataBase.Stores);

            storeItem.IsEnabled = map.Zoom >= minStoreZoom;

            map.ZoomChanged += (s, e) => storeItem.IsEnabled = map.Zoom >= minStoreZoom;
        }

        ToolbarItem ToolbarAdd(string name, string tooltip, Func<Point,Entity> buildEntity, IList entityCollection)
        {
            string asmName = new AssemblyName(Assembly.GetExecutingAssembly().FullName).Name;

            var item = new ToolbarItem
            {   
                Icon = new Uri(string.Format("/{0};component/Resources/{1}.png", asmName, name), UriKind.Relative),
                DisableIcon = new Uri(string.Format("/{0};component/Resources/{1}-disable.png", asmName, name), UriKind.Relative),
                Label = name.ToUpper(),
                Tooltip = tooltip,
                Target = map,
                DropDelegate = p =>
                {
                    var geo = map.ScreenToGeographic(p);
                    var e = buildEntity(geo);
                    entityCollection.Add(e);
                    e.Latitude = geo.Y;
                    e.Longitude = geo.X;
                }
            };
            toolbar.Children.Add(item);
            return item;
        }

        void GetServerData()
        {
            var client = CreateFactoriesService();
            client.GetFactoriesAndOfficesCompleted += (s, e) =>
            {
                if (e.Result == null)
                    return;

                Copy(e.Result.Factories, serverDataBase.Factories);
                Copy(e.Result.Offices, serverDataBase.Offices);
                Copy(e.Result.Products, serverDataBase.Products);                
            };
            client.GetFactoriesAndOfficesAsync();
        }

        void Copy<T>(ObservableCollection<T> source, ObservableCollection<T> target)
        {
            target.Clear();
            foreach (var p in source)
            {
                target.Add(p);
            }
        }

        void OnKeyDown(object s, KeyEventArgs e)
        {
            if ((Keyboard.Modifiers & (ModifierKeys.Control | ModifierKeys.Shift)) != 0
                     && e.Key == Key.S
                     && Application.Current.Host.Source.Host == "localhost")
            {
                var client = CreateFactoriesService();
                client.MergeDataBaseCompleted += (s2,e2) => HtmlPage.Window.Navigate(new Uri("SaveDataBase.ashx", UriKind.Relative));
                client.MergeDataBaseAsync(localDataBase);
            }
        }

        Entity FactoryGenerator(Point latLong)
        {
            var f = new Factory();
            f.Capacity = new Random().Next(100);
            NameEntity(f, latLong, "Factory");
            return f;
        }

        Entity OfficeGenerator(Point latLong)
        {
            var o = new Office();
            NameEntity(o, latLong, "Office");
            return o;
        }

        Entity StoreGenerator(Point latLong)
        {
            var s = new Store();
            NameEntity(s, latLong, "Store");
            
            var random = new Random();
            var sales = new ObservableCollection<ProductSale>();
            foreach (var p in serverDataBase.Products)
                sales.Add(new ProductSale { Product = p, Sales = random.Next(100) });
            s.Sales = sales;
            return s;
        }

        void NameEntity(Entity entity, Point latLong, string type)
        {
            var client = CreateFactoriesService();
            client.GetLocationNameCompleted += (s, e) =>
                entity.Name = !string.IsNullOrEmpty(e.Result) ? e.Result + "'s " + type : type;
            client.GetLocationNameAsync(latLong.Y, latLong.X);
        }

        static FactoriesServiceSoapClient CreateFactoriesService()
        {            
            return new FactoriesServiceSoapClient(
                new BasicHttpBinding(), 
                new EndpointAddress(Extensions.GetAbsoluteUri("FactoriesService.asmx")));
        }

        static DataBase InitializeDataBase() 
        {
            return new DataBase 
            {
                Factories = new ObservableCollection<Factory>(),
                Offices = new ObservableCollection<Office>(),
                Stores = new ObservableCollection<Store>(),
                Products = new ObservableCollection<Product>(),
            };
        }

        public class LocalStoreSource : IMapVirtualSource
        {
            DataBase _dataBase;

            public LocalStoreSource(DataBase localDataBase)
            {
                _dataBase = localDataBase;
            }

            public void Request(double minZoom, double maxZoom, Point lowerLeft, Point upperRight, Action<ICollection> callback)
            {
                if (minZoom < minStoreZoom)
                    return;

                var stores = new List<Store>();

                foreach (var store in _dataBase.Stores)
                {
                    if (store.Latitude > lowerLeft.Y
                      && store.Longitude > lowerLeft.X
                      && store.Latitude <= upperRight.Y
                      && store.Longitude <= upperRight.X)
                    {
                        stores.Add(store);
                    }
                }
                
                callback(stores);
            }
        }

        public class ServerStoreSource : IMapVirtualSource
        {
            public void Request(double minZoom, double maxZoom, Point lowerLeft, Point upperRight, Action<ICollection> callback)
            {
                if (minZoom < minStoreZoom)
                    return;

                var client = CreateFactoriesService();
                client.GetStoresCompleted += (s, e) =>
                {
                    if(e.Error == null)
                        callback(e.Result);
                };
                client.GetStoresAsync(lowerLeft.Y, lowerLeft.X, upperRight.Y, upperRight.X);
            }
        }
    }
}
