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

namespace AutoSellerNS
{
	public class PriceNode
	{
		public int m_iTime;
		public int m_iPrice;
		public int m_iAskPrice;
		public int m_iBidPrice;
		public int m_iAccDealCnt;
		public int m_iDealCnt;
		public double m_dAvg;
		public double m_dSD;
		public double m_dSellLimit;
		public int m_iSellCnt;

		public override string ToString()
		{
			return $"{m_iPrice}({m_dAvg})";
		}
	}

	public class STOCK_CUR_ITEM
	{
		public AutoSeller m_Listener = null;
		public CybosHelper m_CybosHelper = null;
		public string m_strCode;
		public string m_strCodeName;
		public DSCBO1Lib.StockCur m_StockCur = null;
		public DSCBO1Lib.StockJpbid m_Jpbid = null;
		public int[] m_aiBidPrice = new int[10];
		public int[] m_aiBidCount = new int[10];
		public int[] m_aiAskPrice = new int[10];
		public int[] m_aiAskCount = new int[10];

		public int m_iOpenigPrice = 0;
		public int m_iCurPrice = 0;
		public int m_iMaxPrice = 0;
		public int m_iCheckCount = 0;

		public int m_iTrailingCount = 0;

		// real time, price time, price
		public List<PriceNode> m_PriceList = new List<PriceNode>();
		public double m_dCheckCountLimit = Config.GetBidCount();

		int m_iPrevTime = 0;

		public async void OnRecievedPrice()
		{
			bool bOpeningHour = (m_StockCur.GetHeaderValue(19) == '2');
			m_iCurPrice = m_StockCur.GetHeaderValue(13);
			if (m_iOpenigPrice == 0)
				m_iOpenigPrice = m_iCurPrice;
			int iTime = m_StockCur.GetHeaderValue(18);
			int iConclusionCnt = m_StockCur.GetHeaderValue(17);
			int iAccDealCnt = m_StockCur.GetHeaderValue(9);
			int iDealCnt = m_StockCur.GetHeaderValue(17);

			int iTimeDiff = 0;
			if (m_iPrevTime > 0)
				iTimeDiff = ((iTime / 10000) * 60 * 60 + ((iTime % 10000) / 100) * 60 + (iTime % 100)) - ((m_iPrevTime / 10000) * 60 * 60 + ((m_iPrevTime % 10000) / 100) * 60 + (m_iPrevTime % 100));

			if (bOpeningHour == true)
			{
				DateTime RealTime = DateTime.Now;
				int iRealTime = RealTime.Hour * 10000 + RealTime.Minute * 100 + RealTime.Second;

				List<int> lastNPrice = null;
				lock (m_PriceList)
				{
					if (Config.GetListSize() == 0)
						lastNPrice = m_PriceList.Select(s => s.m_iPrice).ToList();
					else
						lastNPrice = m_PriceList.Skip(Math.Max(0, m_PriceList.Count - (Config.GetListSize() - 1))).Select(s => s.m_iPrice).ToList();

					lastNPrice.Add(m_iCurPrice);
				}

				double dAverage = lastNPrice.Average();
				double sumOfSquaresOfDifferences = lastNPrice.Select(val => Math.Pow((val - dAverage) / m_CybosHelper.GetUnitValue(val), 2)).Sum();
				double dStdDev = Math.Sqrt(sumOfSquaresOfDifferences / lastNPrice.Count);
				if (dStdDev >= Config.GetFastSD())
				{
					if (m_iCurPrice >= dAverage)
						m_dCheckCountLimit += Config.GetFastUp();
					else
						m_dCheckCountLimit -= Config.GetFastDown();
				}
				else if (dStdDev <= Config.GetSlowSD())
				{
					if (m_iCurPrice >= dAverage)
						m_dCheckCountLimit += Config.GetSlowUp();
					else
						m_dCheckCountLimit -= Config.GetSlowDown();
				}

				m_dCheckCountLimit -= iTimeDiff * Config.GetTimeDown();


				if (m_iCurPrice > m_iMaxPrice)
					m_iTrailingCount = 0;

				m_iMaxPrice = Math.Max(m_iCurPrice, m_iMaxPrice);

				int iAskPrice = m_StockCur.GetHeaderValue(7);
				int iBidPrice = m_StockCur.GetHeaderValue(8);

				if (m_iCurPrice * iConclusionCnt > Config.GetIgnorePrice())
				{
					if (m_iCurPrice <= iBidPrice)
						m_iCheckCount++;
					else
						m_iCheckCount = 0;
				}

				if (m_iCheckCount >= m_dCheckCountLimit && m_Listener.IsSelling() == true)
				{
					await m_CybosHelper.SellItem(m_strCode, m_Listener.GetSellableCount(m_strCode), m_aiAskPrice[0]);
					Util.Log(Util.LOG_TYPE.SELL, string.Format("[조건 완료 매도] {0} ({1}회) (현재가 {2:n0}원, 최고가 {3:n0}원)",
						m_strCodeName,
						Config.GetBidCount(),
						m_iCurPrice,
						m_iMaxPrice));
				}

				if (m_iCurPrice < (m_iMaxPrice * (100.0f - Config.GetTrailingRate()) / 100.0f))
					m_iTrailingCount++;
				if (m_iTrailingCount >= Config.GetTrailingCnt() && m_Listener.IsSelling() == true)
				{
					await m_CybosHelper.SellItem(m_strCode, m_Listener.GetSellableCount(m_strCode), m_aiAskPrice[0]);
					Util.Log(Util.LOG_TYPE.SELL, string.Format("[트레일링 매도] {0} ({1}% {2}회) (현재가 {3:n0}원, 최고가 {4:n0}원)",
						m_strCodeName,
						Config.GetTrailingRate(),
						Config.GetTrailingCnt(),
						m_iCurPrice,
						m_iMaxPrice));
				}

				if (iTimeDiff >= Config.GetTimeLimit() && m_Listener.IsSelling() == true)
				{
					await m_CybosHelper.SellItem(m_strCode, m_Listener.GetSellableCount(m_strCode), m_aiAskPrice[0]);
					Util.Log(Util.LOG_TYPE.SELL, string.Format("[시간제한 매도] {0} ({1}초) (현재가 {2:n0}원, 최고가 {3:n0}원)",
						m_strCodeName,
						iTimeDiff,
						m_iCurPrice,
						m_iMaxPrice));
				}

				lock (m_PriceList)
				{
					m_PriceList.Add(new PriceNode
					{
						m_iTime = iTime,
						m_iPrice = m_iCurPrice,
						m_iAskPrice = iAskPrice,
						m_iBidPrice = iBidPrice,
						m_iAccDealCnt = iAccDealCnt,
						m_iDealCnt = iDealCnt,
						m_dAvg = dAverage,
						m_dSD = dStdDev,
						m_dSellLimit = m_dCheckCountLimit,
						m_iSellCnt = m_iCheckCount,
					});
				}
			}

			m_Listener.OnReceivedCurPrice(m_strCode, m_iCurPrice, m_iMaxPrice, m_dCheckCountLimit, m_iCheckCount, bOpeningHour);
			m_iPrevTime = iTime;
		}

		public void OnReceivedCall()
		{
			for (int i = 0; i < 5; i++)
			{
				m_aiBidPrice[i] = m_Jpbid.GetHeaderValue(4 + 4 * i);
				m_aiBidCount[i] = m_Jpbid.GetHeaderValue(6 + 4 * i);
				m_aiAskPrice[i] = m_Jpbid.GetHeaderValue(3 + 4 * i);
				m_aiAskCount[i] = m_Jpbid.GetHeaderValue(5 + 4 * i);
			}

			for (int i = 0; i < 5; i++)
			{
				m_aiBidPrice[i + 5] = m_Jpbid.GetHeaderValue(28 + 4 * i);
				m_aiBidCount[i + 5] = m_Jpbid.GetHeaderValue(30 + 4 * i);
				m_aiAskPrice[i + 5] = m_Jpbid.GetHeaderValue(27 + 4 * i);
				m_aiAskCount[i + 5] = m_Jpbid.GetHeaderValue(29 + 4 * i);
			}

			m_Listener.OnReceivedCall(m_strCode, 0, m_aiBidPrice, m_aiBidCount, m_aiAskPrice, m_aiAskCount);
		}

		public override string ToString()
		{
			return $"[{m_strCodeName}:{m_strCode}] {m_iCurPrice}";
		}
	}
}
