using System;
using System.Windows;
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 a named collection of portfolio items.
    /// </summary>
    public class Portfolio : BaseObject
    {
        string _name;
        ListView<PortfolioItem> _items;

        /// <summary>
        /// Initializes a new instance of a <see cref="Portfolio"/>.
        /// </summary>
        public Portfolio()
        {
            _items = new ListView<PortfolioItem>();

            // raise PropertyChanged to update view when items change
            // for example, when an items purchase price changes, we have
            // to update the portfolio's total gain property.
            _items.CollectionChanged += (s, e) => { OnPropertyChanged(); };
            _items.ItemPropertyChanged += (s, e) => { OnPropertyChanged(); };
        }
        /// <summary>
        /// Gets or sets the portfolio name.
        /// </summary>
        [XmlAttribute]
        public string Name
        {
            get { return _name; }
            set
            {
                if (value != Name)
                {
                    _name = value;
                    OnPropertyChanged("Name");
                }
            }
        }
        /// <summary>
        /// Gets or sets the list of items in the portfolio.
        /// </summary>
        public ListView<PortfolioItem> Items
        {
            get { return _items; }
        }
        /// <summary>
        /// Gets the view of the items in the portfolio.
        /// </summary>
        public ICollectionView ItemsView
        {
            get { return _items.View; }
        }
        /// <summary>
        /// Gets the total cost of the portfolio.
        /// </summary>
        public double CostBasis
        {
            get
            {
                var value = 0.0;
                foreach (var item in Items)
                {
                    value += item.CostBasis;
                }
                return value;
            }
        }
        /// <summary>
        /// Gets the total market value of the portfolio.
        /// </summary>
        public double MarketValue
        {
            get
            {
                var value = 0.0;
                foreach (var item in Items)
                {
                    value += item.MarketValue;
                }
                return value;
            }
        }
        /// <summary>
        /// Gets the total gain of the portfolio.
        /// </summary>
        public double Gain
        {
            get
            {
                var gain = 0.0;
                foreach (var item in Items)
                {
                    gain += item.Gain;
                }
                return gain;
            }
        }
        /// <summary>
        /// Gets the percentage gain/loss for this portfolio.
        /// </summary>
        public double GainPercent
        {
            get { return CostBasis == 0 ? 0 : Gain / CostBasis; }
        }
    }

    /// <summary>
    /// Represents an asset type and optionally units purchased and price paid.
    /// </summary>
    public class PortfolioItem : BaseObject
    {
        Asset _asset;
        PortfolioFlags _flags;
        double _units, _purchasePrice;

        static Dictionary<string, IList<AssetPrice>> _priceCache = new Dictionary<string, IList<AssetPrice>>();

        /// <summary>
        /// Gets or sets the asset represented by this item.
        /// </summary>
        [XmlIgnore]
        public Asset Asset
        {
            get { return _asset; }
            set
            {
                if (value != Asset)
                {
                    _asset = value;
                    OnPropertyChanged();
                }
            }
        }
        /// <summary>
        /// Gets or sets the symbol of the asset represented by this item.
        /// </summary>
        [XmlAttribute]
        public string Symbol
        {
            get { return _asset.Symbol; }
            set
            {
                // create dummy asset with Symbol property 
                // while we get the real data from the server
                _asset = new ServiceReference1.Asset();
                _asset.Symbol = value;

                // go get asset from the server
                var svc = WebServiceClient.GetServiceClient();
                svc.GetAssetCompleted += (s, e) =>
                    {
                        if (e.Error == null && !e.Cancelled)
                        {
                            Asset = e.Result;
                            Debug.WriteLine("loaded Asset from server: " + value);
                        }
                        else
                        {
                            Debug.WriteLine("failed to get Asset from server: " + value);
                        }
                    };
                svc.GetAssetAsync(value);
            }
        }
        /// <summary>
        /// Gets the name of the asset represented by this item.
        /// </summary>
        public string Name
        {
            get { return _asset.Name; }
        }
        /// <summary>
        /// Gets the last price this asset was traded at.
        /// </summary>
        public double LastPrice
        {
            get { return _asset.LastPrice; }
        }
        /// <summary>
        /// Gets or sets the change in asset value today.
        /// </summary>
        public double Change
        {
            get { return _asset.Change; }
        }
        /// <summary>
        /// Gets or sets the percentage change in asset value today.
        /// </summary>
        public double ChangePercent
        {
            get { return _asset.LastPrice == 0 ? 0 : _asset.Change / _asset.LastPrice; }
        }
        /// <summary>
        /// Gets or sets general flags associated with this item.
        /// </summary>
        [XmlAttribute, DefaultValue(PortfolioFlags.None)]
        public PortfolioFlags Flags
        {
            get { return _flags; }
            set
            {
                if (value != Flags)
                {
                    _flags = value;
                    OnPropertyChanged("Flags");
                }
            }
        }
        /// <summary>
        /// Gets or sets whether the view should include this item in a chart.
        /// </summary>
        [XmlIgnore]
        public bool ShowChart
        {
            get { return (_flags & PortfolioFlags.ShowChart) != 0; }
            set
            {
                if (value != ShowChart)
                {
                    Flags = value
                        ? Flags | PortfolioFlags.ShowChart
                        : Flags & ~PortfolioFlags.ShowChart;
                    OnPropertyChanged("ShowChart");
                }
            }
        }
        /// <summary>
        /// Gets or sets how many units of this asset are represented by this item.
        /// </summary>
        [XmlAttribute, DefaultValue(0.0)]
        public double Units
        {
            get { return _units; }
            set
            {
                if (value != Units)
                {
                    _units = value;
                    OnPropertyChanged();
                }
            }
        }
        /// <summary>
        /// Gets or sets the price paid per asset unit owned.
        /// </summary>
        [XmlAttribute, DefaultValue(0.0)]
        public double PurchasePrice
        {
            get { return _purchasePrice; }
            set
            {
                if (value != PurchasePrice)
                {
                    _purchasePrice = value;
                    OnPropertyChanged();
                }
            }
        }
        /// <summary>
        /// Gets the total price paid for the asset.
        /// </summary>
        public double CostBasis
        {
            get { return Units * PurchasePrice; }
        }
        /// <summary>
        /// Gets the current market value of the asset.
        /// </summary>
        public double MarketValue
        {
            get { return Units * LastPrice; }
        }
        /// <summary>
        /// Gets the difference between the current value of the assets and the price paid.
        /// </summary>
        public double Gain
        {
            get { return MarketValue - CostBasis; }
        }
        /// <summary>
        /// Gets the percentage gain/loss since the assets were bought until today.
        /// </summary>
        public double GainPercent
        {
            get { return CostBasis == 0 ? 0 : Gain / CostBasis; }
        }
        /// <summary>
        /// Gets the gain/loss today.
        /// </summary>
        public double DaysGain
        {
            get { return Units * Change; }
        }
        /// <summary>
        /// Gets a list of historical prices for this asset.
        /// </summary>
        [XmlIgnore]
        public IList<AssetPrice> PriceHistory
        {
            get
            {
                // try loading prices from cache
                IList<AssetPrice> prices;
                if (_priceCache.TryGetValue(Symbol, out prices))
                {
                    return prices;
                }

                // not in cache, create new entry and add to cache
                prices = new List<AssetPrice>();
                _priceCache.Add(Symbol, prices);

                // get the data for the new entry, store in cache when it returns
                var svc = WebServiceClient.GetServiceClient();
                svc.GetAssetPricesCompleted += (s, e) =>
                {
                    _priceCache[Symbol] = e.Result;
                    OnPropertyChanged("PriceHistory");
                };
                svc.GetAssetPricesAsync(this.Symbol);

                // return the value we got
                return prices;
            }
        }
    }
    [Flags]
    public enum PortfolioFlags
    {
        None = 0,
        ShowChart = 1
    };
}
