using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Net;
using System.Windows;
using System.Windows.Media;
using System.Windows.Input;
using System.Windows.Data;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Xml.Serialization;
using InvExplorer.ServiceReference1;

namespace InvExplorer
{
    /// <summary>
    /// Represents the ViewModel used to communicate with the View.
    /// The ViewModel contains a list of <see cref="Portfolio"/> objects.
    /// Each Portfolio contains a list of <see cref="PortfolioItem"/> objects.
    /// </summary>
    public class ViewModel : BaseObject
    {
        //------------------------------------------------------------------------------
        #region ** fields

        ListView<Portfolio> _portfolios;
        ListView<SymbolName> _symbolNames;
        ICommand _cmdAddAsset, _cmdRemoveAsset;
        ICommand _cmdAddPortfolio, _cmdCopyPortfolio, _cmdRemovePortfolio;
        ICommand _cmdSavePortfolio, _cmdLoadPortfolio, _cmdSaveData;
        int _currentPortfolioIndex = -1;

        static bool _serializing;
        static XmlSerializer _serializer = new XmlSerializer(typeof(ViewModel));
        
        #endregion

        //------------------------------------------------------------------------------
        #region ** ctor

        /// <summary>
        /// Initializes a new instance of a <see cref="ViewModel"/>.
        /// </summary>
        public ViewModel()
        {
            // create portfolio collection
            _portfolios = new ListView<Portfolio>();

            // initialize portfolio if not serializing
            if (!_serializing)
            {
                // load complete list of asset symbols and names 
                // (used in auto-search when adding items to portfolio)
                _symbolNames = new ListView<SymbolName>();
                LoadSymbolNames();

                // initialize add/remove asset/portfolio commands
                _cmdAddAsset = new AddAssetCommand(this);
                _cmdRemoveAsset = new RemoveAssetCommand(this);
                _cmdAddPortfolio = new AddPortfolioCommand(this);
                _cmdCopyPortfolio = new CopyPortfolioCommand(this);
                _cmdRemovePortfolio = new RemovePortfolioCommand(this);

                // initialize load/save portfolio/data commands
                _cmdSavePortfolio = new SavePortfolioCommand(this);
                _cmdLoadPortfolio = new LoadPortfolioCommand(this);
                _cmdSaveData = new SaveDataCommand(this);

                // don't start empty
                Initialize();

                // listen to changes (to save updates)
                PortfoliosView.CollectionChanged += PortfoliosView_CollectionChanged;
                PortfoliosView.CurrentChanged += PortfolioChanged;
                _portfolios.ItemPropertyChanged += PortfolioChanged;
            }
        }

        #endregion

        //------------------------------------------------------------------------------
        #region ** object model

        /// <summary>
        /// Gets or sets the collection of portfolios owned by the user.
        /// </summary>
        public ListView<Portfolio> Portfolios
        {
            get { return _portfolios; }
        }
        /// <summary>
        /// Gets or sets the view of portfolios owned by the user.
        /// </summary>
        public ICollectionView PortfoliosView
        {
            get { return _portfolios.View; }
        }
        /// <summary>
        /// Gets or sets the view of asset symbol/name pairs (useful for auto-complete).
        /// </summary>
        public ICollectionView SymbolNamesView
        {
            get { return _symbolNames.View; }
        }
        /// <summary>
        /// Command used to add an asset to the current portfolio.
        /// </summary>
        public ICommand AddAssetCommand
        {
            get { return _cmdAddAsset; }
        }
        /// <summary>
        /// Command used to remove selected assets from the current portfolio.
        /// </summary>
        public ICommand RemoveAssetCommand
        {
            get { return _cmdRemoveAsset; }
        }
        /// <summary>
        /// Command used to add a Portfolio to the current portfolio collection.
        /// </summary>
        public ICommand AddPortfolioCommand
        {
            get { return _cmdAddPortfolio; }
        }
        /// <summary>
        /// Command used to create a copy of the current Portfolio and add it to the portfolio collection.
        /// </summary>
        public ICommand CopyPortfolioCommand
        {
            get { return _cmdCopyPortfolio; }
        }
        /// <summary>
        /// Command used to remove selected Portfolios from the portfolio collection.
        /// </summary>
        public ICommand RemovePortfolioCommand
        {
            get { return _cmdRemovePortfolio; }
        }
        /// <summary>
        /// Gets or sets the index of the currently selected portfolio.
        /// </summary>
        [XmlAttribute]
        public int CurrentPortfolioIndex
        {
            get { return _currentPortfolioIndex; }
            set
            {
                _currentPortfolioIndex = value;
                try
                {
                    PortfoliosView.MoveCurrentToPosition(value);
                }
                catch { }
            }
        }
        /// <summary>
        /// Gets or sets a string that represents the current portfolio definition.
        /// </summary>
        [XmlIgnore]
        public string PortfolioDefinition
        {
            get { return SaveToString(); }
            set
            {
                var def = SaveToString();
                if (value != def)
                {
                    LoadFromString(value);
                    OnPropertyChanged("PortfolioDefinition");
                }
            }
        }
        /// <summary>
        /// Saves the portfolios to a stream.
        /// </summary>
        public void Save(Stream stream)
        {
            CurrentPortfolioIndex = PortfoliosView.CurrentPosition;
            _serializer.Serialize(stream, this);
        }
        /// <summary>
        /// Loads the portfolios from a stream.
        /// </summary>
        public void Load(Stream stream)
        {
            // load temp model from stream
            _serializing = true;
            var vm = _serializer.Deserialize(stream) as ViewModel;
            _serializing = false;

            // copy data to this model
            Portfolios.Clear();
            foreach (var pf in vm.Portfolios)
            {
                Portfolios.Add(pf);
                pf.ItemsView.MoveCurrentToFirst();
            }

            // select the current portfolio
            if (vm.CurrentPortfolioIndex > -1 && vm.CurrentPortfolioIndex < _portfolios.Count)
            {
                PortfoliosView.MoveCurrentToPosition(vm.CurrentPortfolioIndex);
            }
            else
            {
                PortfoliosView.MoveCurrentToFirst();
            }
        }
        /// <summary>
        /// Saves the portfolios to a string.
        /// </summary>
        public string SaveToString()
        {
            using (var ms = new MemoryStream())
            {
                Save(ms);
                ms.Position = 0;
                using (var sr = new StreamReader(ms))
                {
                    return sr.ReadToEnd();
                }
            }
        }
        /// <summary>
        /// Loads the portfolios from a string.
        /// </summary>
        public void LoadFromString(string serializationString)
        {
            using (var ms = new MemoryStream())
            using (var sw = new StreamWriter(ms))
            {
                sw.Write(serializationString);
                sw.Flush();
                ms.Position = 0;
                Load(ms);
            }
        }
        /// <summary>
        /// Gets a palette used to color assets on charts, grids, etc according to their index in a portfolio.
        /// </summary>
        public static Color[] Palette
        {
            get { return _palette; }
        }
        static Color[] _palette = new Color[] 
        {
            Color.FromArgb(255, 255, 0, 0), Color.FromArgb(255, 255, 190, 0), Color.FromArgb(255, 200, 200, 0), Color.FromArgb(255, 148, 215, 82), Color.FromArgb(255, 0, 182, 82), Color.FromArgb(255, 0, 182, 239), Color.FromArgb(255, 0, 117, 198), Color.FromArgb(255, 0, 34, 99), Color.FromArgb(255, 115, 53, 156), // Standard
            Color.FromArgb(255, 181, 61, 156), Color.FromArgb(255, 247, 231, 239), Color.FromArgb(255, 189, 61, 107), Color.FromArgb(255, 173, 101, 189), Color.FromArgb(255, 222, 109, 51), Color.FromArgb(255, 255, 182, 56), Color.FromArgb(255, 206, 109, 165), Color.FromArgb(255, 255, 142, 56), // Opulent
            Color.FromArgb(255, 82, 93, 107), Color.FromArgb(255, 255, 243, 156), Color.FromArgb(255, 255, 134, 51), Color.FromArgb(255, 115, 154, 222), Color.FromArgb(255, 181, 43, 21), Color.FromArgb(255, 247, 207, 43), Color.FromArgb(255, 173, 186, 214), Color.FromArgb(255, 115, 125, 132), // Oriel
            Color.FromArgb(255, 66, 68, 82), Color.FromArgb(255, 222, 235, 239), Color.FromArgb(255, 115, 125, 165), Color.FromArgb(255, 156, 186, 206), Color.FromArgb(255, 214, 219, 123), Color.FromArgb(255, 255, 219, 123), Color.FromArgb(255, 189, 134, 115), Color.FromArgb(255, 140, 114, 107), // Origin
            Color.FromArgb(255, 66, 76, 34), Color.FromArgb(255, 255, 251, 206), Color.FromArgb(255, 165, 182, 148), Color.FromArgb(255, 247, 166, 66), Color.FromArgb(255, 231, 190, 43), Color.FromArgb(255, 214, 146, 165), Color.FromArgb(255, 156, 134, 198), Color.FromArgb(255, 132, 158, 198), // Paper
            Color.FromArgb(255, 74, 34, 21), Color.FromArgb(255, 231, 223, 206), Color.FromArgb(255, 56, 146, 165), Color.FromArgb(255, 255, 186, 0), Color.FromArgb(255, 198, 43, 43), Color.FromArgb(255, 132, 170, 51), Color.FromArgb(255, 148, 66, 0), Color.FromArgb(255, 66, 89, 140), // Solstice
            Color.FromArgb(255, 56, 56, 56), Color.FromArgb(255, 214, 211, 214), Color.FromArgb(255, 107, 162, 181), Color.FromArgb(255, 206, 174, 0), Color.FromArgb(255, 140, 138, 165), Color.FromArgb(255, 115, 134, 99), Color.FromArgb(255, 156, 146, 115), Color.FromArgb(255, 123, 134, 140), // Technic
            Color.FromArgb(255, 21, 72, 123), Color.FromArgb(255, 239, 239, 231), Color.FromArgb(255, 74, 130, 189), Color.FromArgb(255, 198, 80, 74), Color.FromArgb(255, 156, 186, 90), Color.FromArgb(255, 132, 101, 165), Color.FromArgb(255, 74, 174, 198), Color.FromArgb(255, 247, 150, 66), // Office
            Color.FromArgb(255, 107, 101, 107), Color.FromArgb(255, 206, 195, 214), Color.FromArgb(255, 206, 186, 99), Color.FromArgb(255, 156, 178, 132), Color.FromArgb(255, 107, 178, 206), Color.FromArgb(255, 99, 134, 206), Color.FromArgb(255, 123, 105, 206), Color.FromArgb(255, 165, 120, 189), // Apex
            Color.FromArgb(255, 51, 46, 51), Color.FromArgb(255, 231, 223, 214), Color.FromArgb(255, 247, 125, 0), Color.FromArgb(255, 56, 39, 51), Color.FromArgb(255, 21, 89, 123), Color.FromArgb(255, 74, 134, 66), Color.FromArgb(255, 99, 72, 123), Color.FromArgb(255, 198, 154, 90), // Aspect
            Color.FromArgb(255, 99, 105, 132), Color.FromArgb(255, 198, 211, 214), Color.FromArgb(255, 214, 96, 74), Color.FromArgb(255, 206, 182, 0), Color.FromArgb(255, 40, 174, 173), Color.FromArgb(255, 140, 120, 115), Color.FromArgb(255, 140, 178, 140), Color.FromArgb(255, 14, 146, 74) // Civic
        };
        /// <summary>
        /// Command that saves the current portfolio definitions to a file.
        /// </summary>
        public ICommand SavePortfolioCommand
        {
            get { return _cmdSavePortfolio; }
        }
        /// <summary>
        /// Command that loads the portfolio definitions from a file.
        /// </summary>
        public ICommand LoadPortfolioCommand
        {
            get { return _cmdLoadPortfolio; }
        }
        /// <summary>
        /// Command that saves the current stock data to a file.
        /// </summary>
        public ICommand SaveDataCommand
        {
            get { return _cmdSaveData; }
        }
        /// <summary>
        /// Initializes model by clearing the current portfolio collection, then adding a
        /// new portfolio containing two dummy items.
        /// </summary>
        public void Initialize()
        {
            // clear portfolio collection
            Portfolios.Clear();

            // add a portfolio with two items
            var pf = new Portfolio();
            pf.Name = "My Portfolio";
            foreach (var symbol in "AAPL,MSFT".Split(','))
            {
                var item = new PortfolioItem();
                item.Symbol = symbol;
                item.ShowChart = true;
                pf.Items.Add(item);
            }
            Portfolios.Add(pf);

            // select first item everywhere
            PortfoliosView.MoveCurrentToFirst();
            pf.ItemsView.MoveCurrentToFirst();
        }

        #endregion

        //------------------------------------------------------------------------------
        #region ** implementation

        // keep track of changes in the portfolio definition
        void PortfoliosView_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.OldItems != null)
            {
                foreach (Portfolio pf in e.OldItems)
                {
                    pf.Items.ItemPropertyChanged -= PortfolioChanged;
                    pf.ItemsView.CollectionChanged -= PortfolioChanged;
                }
            }
            if (e.NewItems != null)
            {
                foreach (Portfolio pf in e.NewItems)
                {
                    pf.Items.ItemPropertyChanged += PortfolioChanged;
                    pf.ItemsView.CollectionChanged += PortfolioChanged;
                }
            }
        }
        void PortfolioChanged(object sender, EventArgs e)
        {
            OnPropertyChanged("PortfolioDefinition");
        }

        // load asset symbols and names asynchronously
        void LoadSymbolNames()
        {
            const int batchSize = 200;
            var svc = WebServiceClient.GetServiceClient();
            if (svc != null)
            {
                svc.GetAssetSymbolNamesCompleted += (s, e) =>
                    {
                        if (e.Error == null && !e.Cancelled)
                        {
                            // add to list
                            foreach (var sn in e.Result)
                            {
                                _symbolNames.Add(sn);
                            }
                            Debug.WriteLine("got {0} symbols so far...", _symbolNames.Count);

                            // if there are more, keep loading
                            if (e.Result.Count >= batchSize)
                            {
                                svc.GetAssetSymbolNamesAsync(_symbolNames.Count, batchSize);
                            }
                        }
                    };
                svc.GetAssetSymbolNamesAsync(_symbolNames.Count, batchSize);
            }
        }

        #endregion
    }
}
