using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MarketData.Utils;

namespace MarketData.MarketDataModel
{
  public class Position
  {
    public String Symbol { get; set; }
    public double Shares{get;set;}
    public double Exposure { get; set; }
    public double MarketValue { get; set; }
  }
  public class PortfolioTrades : List<PortfolioTrade>
  {
    public PortfolioTrades()
    {
    }
    public PortfolioTrades(List<PortfolioTrade> trades)
    {
      if(null==trades)return;
      for (int index = 0; index < trades.Count; index++) Add(trades[index]);
    }
    public List<String> Symbols
    {
      get
      {
        Dictionary<String, String> uniqueSymbols = new Dictionary<String, String>();
        List<String> symbols = new List<String>();
        foreach (PortfolioTrade portfolioTrade in this)
        {
          if (!uniqueSymbols.ContainsKey(portfolioTrade.Symbol)) uniqueSymbols.Add(portfolioTrade.Symbol, portfolioTrade.Symbol);
        }
        symbols = new List<String>(uniqueSymbols.Values);
        symbols.Sort();
        return symbols;
      }
    }
    public List<Position> GetPositions(DateTime asOf)
    {
      List<Position> positions = new List<Position>();
      List<String> symbols = Symbols;
      foreach (String symbol in symbols) positions.Add(GetPosition(symbol, asOf));
      return positions;
    }
    public Position GetPosition(String symbol,DateTime asof)
    {
      List<PortfolioTrade> portfolioTrades = new List<PortfolioTrade>();
      for (int index = 0; index < Count; index++)
      {
        PortfolioTrade portfolioTrade = this[index];
        if (!portfolioTrade.Symbol.Equals(symbol)) continue;
        if (portfolioTrade.IsOpen && portfolioTrade.TradeDate<=asof) { portfolioTrades.Add(portfolioTrade); continue; }
        if (portfolioTrade.IsClosed && portfolioTrade.SellDate > asof) { portfolioTrades.Add(portfolioTrade); continue; }
      }
      if (0 == portfolioTrades.Count) return null;
      Position position = new Position();
      position.Symbol = symbol;
      position.Shares = portfolioTrades.Sum(x => x.Shares);
      position.Exposure = portfolioTrades.Sum(x=>x.Shares*x.Price);
      return position;
    }
    public double Exposure(){return this.Sum(x=>x.Exposure());}
    public List<String> Accounts
    {
      get
      {
        Dictionary<String, String> uniqueAccounts = new Dictionary<String, String>();
        List<String> accounts = new List<String>();
        foreach (PortfolioTrade portfolioTrade in this)
        {
          if (!uniqueAccounts.ContainsKey(portfolioTrade.Account)) uniqueAccounts.Add(portfolioTrade.Account, portfolioTrade.Account);
        }
        accounts = new List<String>(uniqueAccounts.Values);
        accounts.Sort();
        return accounts;
      }
    }
    public DateTime GetMinTradeDate()
    {
      DateTime minDate = Utility.Epoch;
      minDate = (from portfolioTrade in this select portfolioTrade.TradeDate).Min();
      return minDate;
    }
		public DateTime GetMinTradeDate(String symbol)
		{
			DateTime minDate=Utility.Epoch;
			symbol=symbol.ToUpper();
      minDate = (from portfolioTrade in this where portfolioTrade.Symbol.Equals(symbol) select portfolioTrade.TradeDate).Min();
      return minDate;
		}

// We just want the trades (open or closed) on or before the specified date.   This will be used to run a cumulative gain/loss and return
    public PortfolioTrades GetTradesOnOrBefore(DateTime date)
    {
      PortfolioTrades tradesOnOrBefore = new PortfolioTrades();
      foreach (PortfolioTrade portfolioTrade in this)
      {
        if (date >= portfolioTrade.TradeDate) tradesOnOrBefore.Add(portfolioTrade);
      }
      return tradesOnOrBefore;
    }
// This will remove trades that are status='CLOSED' and SELL_DATE=closingDate
// The user of this fucntion would be making the inference that a trade that closed on closingDate should not be seen as part of the holdings.
// For example.  The assumption would be that trades that closed on closingDate would not be a part of the portfolio on closingDate
    public PortfolioTrades RemoveClosedTradesWhereClosedOn(DateTime closingDate)
    {
      PortfolioTrades portfolioTrades;
      List<PortfolioTrade> removeTrades = (from PortfolioTrade portfolioTrade in this where portfolioTrade.Status.Equals("CLOSED") && portfolioTrade.SellDate.Date.Equals(closingDate.Date) select portfolioTrade).ToList();
      if (null != removeTrades && 0 != removeTrades.Count) portfolioTrades = new PortfolioTrades(this.Except(removeTrades).ToList());
      else portfolioTrades = this;
      return portfolioTrades;
    }
    public PortfolioTrades GetOpenTradesOn(DateTime date)
    {
      PortfolioTrades openTrades = new PortfolioTrades();
      foreach (PortfolioTrade portfolioTrade in this)
      {
        if (Utility.Epoch.Equals(portfolioTrade.SellDate))  // No sell date so trade is open
        {
          if (date >= portfolioTrade.TradeDate) openTrades.Add(portfolioTrade);
        }
        else  // sell date is not epoch so see if date is in between tradedate and selldate
        {    
          if (date >= portfolioTrade.TradeDate && date < portfolioTrade.SellDate) openTrades.Add(portfolioTrade);  // assume that if the sell date is equal to date then the position is closed
        }
      }
      return openTrades;
    }
    public bool HasOpenPositions(String symbol)
    {
      int openTrades=(from PortfolioTrade portfolioTrade in this where portfolioTrade.Symbol.Equals(symbol) && portfolioTrade.IsOpen select portfolioTrade).Count();
      return openTrades>0?true:false;
    }
    public bool HasOpenPositionsOn(String symbol,DateTime dateTime)
    {
      PortfolioTrades portfolioTrades=GetOpenTradesOn(dateTime);
      int numTrades=(from PortfolioTrade portfolioTrade in portfolioTrades where portfolioTrade.Symbol.Equals(symbol) select portfolioTrade).Count();
      return numTrades>0?true:false;
    }
// This method relies on the fact that BreakoutTrades method in PortfolioDA.GetTradesSymbol() creates pairs of BUY and SELL legs with paired legs sharing the same TradeId.
// The open trades will show up with count==1 when we group them by TradeId
    public PortfolioTrades GetOpenTrades()
    {
      if(0==Count)return new PortfolioTrades();
      PortfolioTrades openTrades=new PortfolioTrades((from PortfolioTrade trade in this select trade).GroupBy(x=>x.TradeId).Where(grouping=>grouping.Count()==1).Select(grouping=>grouping.FirstOrDefault()).ToList());
      return openTrades;
    }
    public PortfolioTrades FilterAccount(String account)
    {
      PortfolioTrades portfolioTrades = new PortfolioTrades();
      foreach (PortfolioTrade portfolioTrade in this)
      {
        if (portfolioTrade.Account.Equals(account)) portfolioTrades.Add(portfolioTrade);
      }
      return portfolioTrades;
    }
    public PortfolioTrades FilterAccount(List<String> accounts)
    {
      PortfolioTrades portfolioTrades = new PortfolioTrades();
      foreach (PortfolioTrade portfolioTrade in this)
      {
				if(accounts.Any(x=>x.Equals(portfolioTrade.Account)))portfolioTrades.Add(portfolioTrade);
      }
      return portfolioTrades;
    }
    public PortfolioTrades FilterSymbol(String symbol)
    {
      PortfolioTrades portfolioTrades = new PortfolioTrades();
      foreach (PortfolioTrade portfolioTrade in this)
      {
        if (portfolioTrade.Symbol.Equals(symbol)) portfolioTrades.Add(portfolioTrade);
      }
      return portfolioTrades;
    }
  }
}