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

namespace AutoSellerNS
{
	class CybosHelper
	{
		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_iCurPrice = 0;
			public int m_iMaxPrice = 0;
			public int m_iCheckCount = 0;

			public int m_iTrailingCount = 0;

			public void OnRecievedPrice()
			{
				bool bReal = (m_StockCur.GetHeaderValue(19) == '2');
				m_iCurPrice = m_StockCur.GetHeaderValue(13);
				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(bReal == true)
				{
					if(m_iCurPrice <= iBidPrice)
						m_iCheckCount++;
					else
						m_iCheckCount = 0;
				}

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

				m_Listener.OnReceivedCurPrice(m_strCode, m_iCurPrice, m_iMaxPrice, m_iCheckCount, bReal);
			}

			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);
			}
		}

		AutoSeller m_Listener = null;

		CPUTILLib.CpCybos m_CPCybos = new CPUTILLib.CpCybos();
		CPFORETRADELib.CpForeTdUtil m_CPUtil = new CPFORETRADELib.CpForeTdUtil();
		DSCBO1Lib.CpConclusion m_CpConclusion = new DSCBO1Lib.CpConclusion();

        CPTRADELib.CpTd6033 m_6033 = new CPTRADELib.CpTd6033();
		Dictionary<string, STOCK_CUR_ITEM> m_aStockCur = new Dictionary<string, STOCK_CUR_ITEM>();
		
		public CybosHelper(AutoSeller Listener)
		{
			m_Listener = Listener;
		}

        public void InitCybos()
        {
            short iResult = m_CPUtil.TradeInit();
            switch (iResult)
            {
                case -1:
                    Util.Log(Util.LOG_TYPE.ERROR, "[TradeInit] 오류");
                    break;

                case 0:
                    Util.Log(Util.LOG_TYPE.VERVOSE, "[TradeInit] 로그인 되었습니다");
                    break;

                case 1:
                    Util.Log(Util.LOG_TYPE.ERROR, "[TradeInit] 업무 키 입력 잘못됨");
                    break;

                case 2:
                    Util.Log(Util.LOG_TYPE.ERROR, "[TradeInit] 계좌 비밀번호가 잘못되었습니다");
                    break;

                case 3:
                    Util.Log(Util.LOG_TYPE.ERROR, "[TradeInit] 취소되었습니다");
                    break;
            }

			m_Listener.SetAccountList(m_CPUtil.AccountNumber);
            m_CpConclusion.Received += CpConclusion_Received;
            m_CpConclusion.Subscribe();
        }

		public List<AutoSeller.ITEM> UpdateItems()
		{
			List<AutoSeller.ITEM> aItems = new List<AutoSeller.ITEM>();

			if (Config.GetAccount() == "")
				return aItems;

			m_6033.SetInputValue(0, Config.GetAccount());
			m_6033.SetInputValue(1, Config.GetSubAccount());
			m_6033.BlockRequest2(1);

			string strCodeList = "";

			bool bContinue = true;
			while(bContinue == true)
			{
				int iCount = m_6033.GetHeaderValue(7);
				for(int i = 0; i < iCount; i++)
				{
					AutoSeller.ITEM Item = new AutoSeller.ITEM();

					Item.m_strCodeName = m_6033.GetDataValue(0, i);
					if(Item.m_strCodeName != "")
					{
						Item.m_strCode = m_6033.GetDataValue(12, i);
						Item.m_iItemCnt = m_6033.GetDataValue(7, i);
						Item.m_iAssessedValue = m_6033.GetDataValue(9, i);
						Item.m_iValuationGains = m_6033.GetDataValue(10, i);
						Item.m_dYield = m_6033.GetDataValue(11, i);
						Item.m_iAvailableQuantity = m_6033.GetDataValue(15, i);
						Item.m_dBookUnitPrice = m_6033.GetDataValue(17, i);
						Item.m_iProfitUnitPrice = m_6033.GetDataValue(18, i);
						Item.m_iSellableCnt = m_6033.GetDataValue(15, i);

						if(strCodeList.Length > 0)
							strCodeList += ",";
						strCodeList += Item.m_strCode;

						aItems.Add(Item);
					}
				}

				bContinue = (m_6033.Continue != 0);
				if(bContinue == true)
					m_6033.BlockRequest2(1);
			}

			if (strCodeList.Length > 0)
			{
				DSCBO1Lib.StockMst2 StockMst = new DSCBO1Lib.StockMst2();
				StockMst.SetInputValue(0, strCodeList);
				StockMst.BlockRequest2(0);
				int iCnt = StockMst.GetHeaderValue(0);
				for (int i = 0; i < iCnt; i++)
				{
					string strCode = StockMst.GetDataValue(0, i);
					int iPrice = StockMst.GetDataValue(3, i);
					AutoSeller.ITEM Item = aItems.FirstOrDefault(s => s.m_strCode == strCode);
					if (Item != default(AutoSeller.ITEM))
						Item.m_iCurPrice = iPrice;
				}
			}

			// sync
			List<string> aRemoveKeys = new List<string>();
			foreach(var Item in m_aStockCur)
			{
				if(aItems.Any(s => s.m_strCode == Item.Key) == false)
					aRemoveKeys.Add(Item.Key);
			}

			foreach(var Key in aRemoveKeys)
			{
				m_aStockCur[Key].m_StockCur.Unsubscribe();
				m_aStockCur[Key].m_Jpbid.Unsubscribe();
				m_aStockCur.Remove(Key);
			}

			return aItems;
		}

		public List<AutoSeller.NCITEM> UpdateNC()
		{
			List<AutoSeller.NCITEM> NCItems = new List<AutoSeller.NCITEM>();

			CPTRADELib.CpTd5339 CP5339 = new CPTRADELib.CpTd5339();
			CP5339.SetInputValue(0, Config.GetAccount());
			CP5339.SetInputValue(1, Config.GetSubAccount());
			CP5339.BlockRequest2(0);

			bool bContinue = true;
			while(bContinue)
			{
				int iCnt = CP5339.GetHeaderValue(5);
				for(int i = 0; i<iCnt; i++)
				{
					AutoSeller.NCITEM Item = new AutoSeller.NCITEM();

					Item.m_strCode = CP5339.GetDataValue(3, i);
					Item.m_strCodeName = CP5339.GetDataValue(4, i);
					Item.m_strDesc = CP5339.GetDataValue(5, i);
					Item.m_iOrderPrice = CP5339.GetDataValue(7, i);
					Item.m_iRemainCnt = CP5339.GetDataValue(11, i);

					NCItems.Add(Item);
				}

				bContinue = (CP5339.Continue != 0);
				if(bContinue)
					CP5339.BlockRequest2(0);
			}

			return NCItems;
		}

		private void CpConclusion_Received()
		{
			string strType = m_CpConclusion.GetHeaderValue(14);
			int iType = 0;
			int.TryParse(strType, out iType);
			bool bBid = (m_CpConclusion.GetHeaderValue(12) == "2");

			string strCode = m_CpConclusion.GetHeaderValue(9);
			string strCodeName = m_CpConclusion.GetHeaderValue(2);
			int iConclusionCnt = m_CpConclusion.GetHeaderValue(3);
			int iPrice = m_CpConclusion.GetHeaderValue(4);
			string strCancel = m_CpConclusion.GetHeaderValue(16);
			int iBookValue = m_CpConclusion.GetHeaderValue(21);
			int iRemainCnt = m_CpConclusion.GetHeaderValue(23);
			string strBidOrAsk = bBid ? "매수" : "매도";

			switch (iType)
			{
				case 1: // 체결
					m_Listener.UpdateItem(strCode, strCodeName, bBid, iConclusionCnt, iRemainCnt, iBookValue);
					Util.Log(bBid?Util.LOG_TYPE.BUY:Util.LOG_TYPE.SELL, string.Format("{0}:{1} {2} 체결 ({3:n0}원 {4}주)", strCodeName, strCode, strBidOrAsk, iPrice, iConclusionCnt));
					break;

				case 2: // 확인
					Util.Log(Util.LOG_TYPE.VERVOSE, string.Format("{0}:{1} {2} 확인", strCodeName, strCode, strBidOrAsk));
					break;

				case 3: // 거부
					Util.Log(Util.LOG_TYPE.VERVOSE, string.Format("{0}:{1} {2} 거부", strCodeName, strCode, strBidOrAsk));
					break;

				case 4: // 접수
					Util.Log(bBid ? Util.LOG_TYPE.BUY : Util.LOG_TYPE.SELL, string.Format("{0}:{1} {2} 접수 ({3:n0}원 {4}주)", strCodeName, strCode, strBidOrAsk, iPrice, iConclusionCnt));
					break;
			}
		}

		public int GetLimitRemainCountTrade()
		{
			return m_CPCybos.GetLimitRemainCount(CPUTILLib.LIMIT_TYPE.LT_TRADE_REQUEST);
		}

		public int GetLimitRemainCountRQ()
		{
			return m_CPCybos.GetLimitRemainCount(CPUTILLib.LIMIT_TYPE.LT_NONTRADE_REQUEST);
		}

		public int GetLimitRemainCountSB()
		{
			return m_CPCybos.GetLimitRemainCount(CPUTILLib.LIMIT_TYPE.LT_SUBSCRIBE);
		}

		public bool IsConnected()
		{
			return (m_CPCybos.IsConnect==1);
		}

		public STOCK_CUR_ITEM GetItem(string strCode)
		{
			if (m_aStockCur.ContainsKey(strCode) == false)
				return null;

			return m_aStockCur[strCode];
		}

		public void Subscribe(AutoSeller.ITEM Item)
		{
			if(m_aStockCur.Any(s => s.Key == Item.m_strCode) == false)
			{
				STOCK_CUR_ITEM StockCur = new STOCK_CUR_ITEM();
				StockCur.m_Listener = m_Listener;
				StockCur.m_CybosHelper = this;
				StockCur.m_strCode = Item.m_strCode;
				StockCur.m_strCodeName = Item.m_strCodeName;

				DSCBO1Lib.StockJpbid2 JpBid = new DSCBO1Lib.StockJpbid2();
				JpBid.SetInputValue(0, Item.m_strCode);
				JpBid.BlockRequest2(0);
				for(int i=0; i<10; i++)
				{
					StockCur.m_aiAskPrice[i] = JpBid.GetDataValue(0, i);
					StockCur.m_aiAskCount[i] = JpBid.GetDataValue(2, i);
					StockCur.m_aiBidPrice[i] = JpBid.GetDataValue(1, i);
					StockCur.m_aiBidCount[i] = JpBid.GetDataValue(3, i);
				}

				StockCur.m_StockCur = new DSCBO1Lib.StockCur();
				StockCur.m_StockCur.SetInputValue(0, Item.m_strCode);
				StockCur.m_StockCur.Received += StockCur.OnRecievedPrice;
				StockCur.m_StockCur.Subscribe();
				StockCur.m_Jpbid = new DSCBO1Lib.StockJpbid();
				StockCur.m_Jpbid.SetInputValue(0, Item.m_strCode);
				StockCur.m_Jpbid.Received += StockCur.OnReceivedCall;
				StockCur.m_Jpbid.SubscribeLatest();
				StockCur.m_iCurPrice = Item.m_iCurPrice;
				StockCur.m_iMaxPrice = Item.m_iCurPrice;
				m_aStockCur.Add(Item.m_strCode, StockCur);
			}
		}

		int GetUnitValue(int iValue)
		{
			int iUnit = 1;
			if(iValue < 1000)
				iUnit = 1;
			else if(iValue < 5000)
				iUnit = 5;
			else if(iValue < 10000)
				iUnit = 10;
			else if(iValue < 50000)
				iUnit = 50;
			else if(iValue < 100000)
				iUnit = 100;
			else if(iValue < 500000)
				iUnit = 500;
			else
				iUnit = 1000;

			return iUnit;
		}

		int GetCallUnitValue(int iValue)
		{
			int iUnit = GetUnitValue(iValue);
			return (iValue/iUnit)*iUnit;
		}

		public void SellItem(string strCode, int iCnt, int iAskPrice)
		{
			int iSellPrice = iAskPrice-GetUnitValue(iAskPrice);

			CPTRADELib.CpTd0311 Td0311 = new CPTRADELib.CpTd0311();
			Td0311.SetInputValue(0, "1");
			Td0311.SetInputValue(1, Config.GetAccount());
			Td0311.SetInputValue(2, Config.GetSubAccount());
			Td0311.SetInputValue(3, strCode);
			Td0311.SetInputValue(4, iCnt);
			Td0311.SetInputValue(5, iSellPrice);
			Td0311.SetInputValue(8, "01");
			Td0311.BlockRequest2(0);
		}
	}
}
