using MarketData.CNNProcessing;
using MarketData.DataAccess;
using MarketData.Generator.MovingAverage;
using MarketData.MarketDataModel;
using MarketData.Utils;
using System;
using System.Collections.Generic;
using System.Linq;

namespace MarketData.Generator.CMMomentum
{
  public class CMMomentumGenerator
  {
    private CMMomentumGenerator()
    {
    }
    public static CMGeneratorResult GenerateCMCandidates(DateTime tradeDate,DateTime analysisDate,CMParams cmParams,List<String> symbolsHeld)
    {
      CMGeneratorResult cmGeneratorResult = new CMGeneratorResult();
      try
      {
        if(cmParams.UseCNN)
        {
          CNNClient cnnClient=new CNNClient(cmParams.UseCNNHost);
          if(!cnnClient.Ping())
          {
            cmGeneratorResult.Success=false;
            cmGeneratorResult.Messages.Add(String.Format("The CNN server at {0} is not responding",cmParams.UseCNNHost));
            return cmGeneratorResult;
          }
        }
        List<String> symbols = PricingDA.GetSymbols();
        bool checkBenchmarkSMAResult = CheckBenchmarkSMA(tradeDate,cmParams, cmGeneratorResult);
        if (!checkBenchmarkSMAResult && false == cmGeneratorResult.Success) return cmGeneratorResult;  // indicates an error calulating the moving average
        if (false == checkBenchmarkSMAResult)
        {
          MDTrace.WriteLine(String.Format("The benchmark simple moving average is less than the benchmark price.  Benchamrk:{0}, Days:{1}",cmParams.Benchmark,cmParams.BenchmarkMovingAverageDays));
          cmGeneratorResult.CMCandidates = GenerateFallbackCandidates(tradeDate,analysisDate,cmParams,symbolsHeld);
          cmGeneratorResult.InFallback = true;
          return cmGeneratorResult;
        }

        for(int index=0;index<symbols.Count;index++)
        {
          String symbol = symbols[index];
          if (0 == (index % 500)) MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Processing item {0} of {1}", index + 1, symbols.Count));
          CMCandidate cmCandidate = CMCandidateGenerator.GenerateCandidate(symbol,tradeDate,analysisDate,cmParams,symbolsHeld);
          if (null == cmCandidate) continue;
          if (cmCandidate.Violation)
					{
					  cmGeneratorResult.CMCandidatesWithViolation.Add(cmCandidate);
					}
          else 
					{
   					if(cmParams.UseCNN)PredictCandidate(cmCandidate,cmParams);
					  cmGeneratorResult.CMCandidates.Add(cmCandidate);
					}
        }

        if(null!=cmGeneratorResult.CMCandidatesWithViolation && 0!=cmGeneratorResult.CMCandidatesWithViolation.Count)
        {
          MDTrace.WriteLine(LogLevel.DEBUG,"**************** C A N D I D A T E   S U M M A R Y  ************************");
          IEnumerable<Tuple<string, int>> groups = cmGeneratorResult.CMCandidatesWithViolation.GroupBy(x => x.ReasonCategory).OrderByDescending(group => group.Count()).Select(group => Tuple.Create(group.Key, group.Count()));
          foreach(Tuple<string, int> group in groups)
          {
            MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Group: {0} Count:{1}",group.Item1, group.Item2));
          }
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Total Considered : {cmGeneratorResult.CMCandidates.Count+cmGeneratorResult.CMCandidatesWithViolation.Count}"));
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Total Disqualified : {cmGeneratorResult.CMCandidatesWithViolation.Count}"));
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Total Eligible : {cmGeneratorResult.CMCandidates.Count}"));
          MDTrace.WriteLine(LogLevel.DEBUG,"******************************************************************************************************");
        }

        if (0 == cmGeneratorResult.CMCandidates.Count)
        {
          cmGeneratorResult.CMCandidates = GenerateFallbackCandidates(tradeDate, analysisDate, cmParams,symbolsHeld);
          cmGeneratorResult.InFallback = true;
        }
        if (null == cmGeneratorResult.CMCandidates || 0 == cmGeneratorResult.CMCandidates.Count)
        {
          MDTrace.WriteLine(String.Format("Unable to produce any candidates for TradeDate {0}", tradeDate));
          return null;
        }
        cmGeneratorResult.CMCandidates = new CMCandidates((from CMCandidate cmCandidate in cmGeneratorResult.CMCandidates select cmCandidate).OrderByDescending(x => x.Score).ThenByDescending(x=>x.SharpeRatio).ToList().Take(cmParams.MaxPositions).ToList());
        cmGeneratorResult.Success = true;
        return cmGeneratorResult;
      }
      catch (Exception exception)
      {
        cmGeneratorResult.Success = false;
        cmGeneratorResult.Messages.Add(exception.ToString());
        MDTrace.WriteLine(LogLevel.DEBUG, exception.ToString());
        return cmGeneratorResult;
      }
    }
    public static CMCandidates GenerateFallbackCandidates(DateTime tradeDate,DateTime analysisDate,CMParams cmParams,List<String> symbolsHeld)
    {
      CMCandidates cmCandidates = new CMCandidates();
      if (null == cmParams || null == cmParams.FallbackCandidateBestOf) return null;
      List<String> fallbackCandidateSymbols = Utility.ToList(cmParams.FallbackCandidateBestOf);
      if (null != symbolsHeld && 0 != symbolsHeld.Count) fallbackCandidateSymbols = fallbackCandidateSymbols.Except(symbolsHeld).ToList();
      foreach (String symbol in fallbackCandidateSymbols)
      {
        CMCandidate cmCandidate = CMCandidateGenerator.GenerateCandidateForFallback(symbol, tradeDate,analysisDate,cmParams,null);
        if (null == cmCandidate || cmCandidate.Violation) continue;
        cmCandidates.Add(cmCandidate);
      }
      cmCandidates = new CMCandidates((from CMCandidate cmCandidate in cmCandidates select cmCandidate).OrderByDescending(x => x.Score).ToList());
      cmCandidates = new CMCandidates(cmCandidates.Take(1).ToList());
      if (null == cmCandidates || 0 == cmCandidates.Count)    // Guarantee that we get a candidate
      {
        CMCandidate cmCandidate = CMCandidateGenerator.GetFallbackCandidateOfLastResort(Utility.ToList(cmParams.FallbackCandidateBestOf), tradeDate, analysisDate, cmParams);
        cmCandidates.Add(cmCandidate);
      }
      return cmCandidates;
    }
    private static bool CheckBenchmarkSMA(DateTime tradeDate,CMParams cmParams, CMGeneratorResult cmGeneratorResult)
    {
      Prices pricesDMA = PricingDA.GetPrices(cmParams.Benchmark, tradeDate, cmParams.BenchmarkMovingAverageDays + 15);
      if (null == pricesDMA || (cmParams.BenchmarkMovingAverageDays + 15) != pricesDMA.Count)
      {
        cmGeneratorResult.Success = false;
        cmGeneratorResult.Messages.Add(String.Format("Insufficient pricing to generate {0} day moving average for {0}.  Required {2} days.", cmParams.Benchmark, cmParams.BenchmarkMovingAverageDays, cmParams.BenchmarkMovingAverageDays + 15));
        return false;
      }
      DMAPrices dmaPrices = MovingAverageGenerator.GenerateMovingAverage(pricesDMA, cmParams.BenchmarkMovingAverageDays);
      if (dmaPrices[0].CurrentPrice < dmaPrices[0].AVGPrice)
      {
        cmGeneratorResult.Success = true;
        cmGeneratorResult.Messages.Add(String.Format("Current price for {0} is less than moving average.  {1}<{2} on {3}", cmParams.Benchmark, dmaPrices[0].CurrentPrice, dmaPrices[0].AVGPrice, dmaPrices[0].Date.ToShortDateString()));
        return false;
      }
      return true;
    }
    // This method is made public in order that it can be tested
		public static bool PredictCandidate(CMCandidate cmCandidate,CMParams cmParams)
		{
		  try
			{
			  CNNClient cnnClient=new CNNClient(cmParams.UseCNNHost);
				DataProcessor dataProcessor=new DataProcessor();
				dataProcessor.Width=128;
				dataProcessor.Height=128;
				dataProcessor.PenWidth=1;
				TestCase testCase=new TestCase(cmCandidate.Symbol,cmCandidate.TradeDate,cmParams.UseCNNDayCount,TestCase.CaseType.Test,TestCase.GenerateType.BollingerBand,TestCase.OutputType.OutputStream);
        dataProcessor.ProcessData(testCase);
				String prediction = cnnClient.Predict(CNNClient.Model.resnet50_20241024_270,testCase.LastStream);
			  prediction=prediction.Substring(prediction.IndexOf("-->"));
				int result=int.Parse(Utility.BetweenString(prediction,"[[","]"));
				if(1==result)
				{
				  cmCandidate.Score*=(1.00+cmParams.UseCNNRewardPercentDecimal);   // increase the score by the percentage indicated in the params settings
					cmCandidate.CNNPrediction=true;
			  }
				return true;
			}
			catch(Exception exception)
			{
			  MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Error encountered calling convolutional model at {0}.  Exception was {1}",cmParams.UseCNNHost,exception.ToString()));
				return false;
			}
		}
  }
}
