using MarketData.MarketDataModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MarketData.DividendRiskParity
{
  //  Takes a collection of positions and calculates the price at which the security needs to be at in order to have a zero (parity) overall position
  public class EquilibriumGenerator
  {
    private EquilibriumGenerator()
    {
    }
    public static Price GenerateEquilibriumPrice(DividendRiskParityPositionCollection dividendRiskParityCollection, Price givenPrice)
    {
      double? lowerClamp = null;
      double? upperClamp = null;
      double? gainLoss = null;
      double? prevGuess = null;
      int direction = 0;
      int iterations = 0;

      if (null == dividendRiskParityCollection || 0 == dividendRiskParityCollection.Count) return null;
      Price price = givenPrice.Clone();
      gainLoss = GetGainLoss(price, dividendRiskParityCollection);
      if (gainLoss < 0) direction = -1;
      else direction = 1;
      while (true)
      {
        if (IsZero(gainLoss)) break;
        if (null == lowerClamp && null == upperClamp && gainLoss < 0 && direction.Equals(-1))
        {
          prevGuess = price.Close;
          price.Close = price.Close + (price.Close / 2.00);
          iterations++;
        }
        else if (null == lowerClamp && null == upperClamp && gainLoss >= 0 && direction.Equals(1))
        {
          prevGuess = price.Close;
          price.Close = price.Close - (price.Close / 2.00);
          iterations++;
        }
        else
        {
          if (gainLoss < 0)
          {
            lowerClamp = price.Close;
            if (null == upperClamp) upperClamp = prevGuess;
            price.Close = (lowerClamp.Value + upperClamp.Value) / 2.00;
            prevGuess = price.Close;
            iterations++;
          }
          else
          {
            upperClamp = price.Close;
            if (null == lowerClamp) lowerClamp = prevGuess;
            price.Close = (upperClamp.Value + lowerClamp.Value) / 2.00;
            prevGuess = price.Close;
            iterations++;
          }
        }
        gainLoss = GetGainLoss(price, dividendRiskParityCollection);
      }
      price.Open = 0.00;
      price.High = 0.00;
      price.Low = 0.00;
      price.AdjClose = 0.00;
      price.Volume = 0;
      return price;
    }
    private static double? GetGainLoss(Price price, DividendRiskParityPositionCollection dividendRiskParityCollection)
    {
      double? gainLoss = null;
      List<String> symbols = (from DividendRiskParityPosition dividendRiskParityPosition in dividendRiskParityCollection select dividendRiskParityPosition.Symbol).Distinct().ToList();
      if (1 != symbols.Count) return null;
      foreach (DividendRiskParityPosition dividendRiskParityPosition in dividendRiskParityCollection)
      {
        if (null == gainLoss)
        {
          gainLoss = GetMarketValue(price, dividendRiskParityPosition) - GetExposure(price, dividendRiskParityPosition);
        }
        else
        {
          gainLoss += GetMarketValue(price, dividendRiskParityPosition) - GetExposure(price, dividendRiskParityPosition);
        }
      }
      return gainLoss;
    }
    private static double? GetMarketValue(Price price, DividendRiskParityPosition dividendRiskParityPosition)
    {
      if (null == price) return null;
      if (price.Date < dividendRiskParityPosition.TradeDate) return null;
      return dividendRiskParityPosition.Shares * price.Close;
    }
    private static double? GetExposure(Price price, DividendRiskParityPosition dividendRiskParityPosition)
    {
      if (null == price) return null;
      if (price.Date < dividendRiskParityPosition.TradeDate) return null;
      return dividendRiskParityPosition.Shares * dividendRiskParityPosition.PurchasePrice;
    }
    private static bool IsZero(double? value)
    {
      if (null == value)
      {
        return true;
      }
      int intValue = (int)(value * 10000.00);
      if (0 == intValue) return true;
      return false;
    }
  } 
}
