import 'dart:core';
import 'dart:io';
import 'dart:typed_data';
import 'marketdatamodels.dart';

class MarketData
    {
        String host = '192.168.123.136';
        int port = 8093;

        bool RouterRunning = false;
        bool IsLoggedIn = false;
        String UserId;
        String ServerKey;

        Socket socket = null;

        int AppId = 3;
        String VersionNo = "8.7";
        String ReleaseNo = "1.0";
        String BuildNo = "1.0";
        String SystemId = "SP_F";
        int LangNo = 0;

        void Function(TickerResponse) OnTickerResp = null;
        void Function(PriceResponse) OnPriceResp = null;        

        MarketData(String userId, String spKey)
        {
            UserId = userId;
            ServerKey = spKey;
            Start();
        }

        void Start()
        {
            Reconnect();
        }

        void Reconnect() async
        {
            socket = await Socket.connect(host, port);
            print('Connected to: ${socket.remoteAddress.address}:${socket.remotePort}');

            // listen for responses from the server
            socket.listen(

              // handle data from the server
              (Uint8List data) {
                  final serverResponse = String.fromCharCodes(data);
                  //print('RECV: $serverResponse');
                  ProcessRecv(serverResponse);
              },

              // handle errors
              onError: (error) {
                  print(error);
                  socket.destroy();
              },

              // handle server ending connection
              onDone: () {
                  print('Server Disconnected.');
                  socket.destroy();
              },
            );

            RouterRunning = true;

            Login();
        }

        void Login() async
        {
            var LoginTime = DateTime.now().millisecondsSinceEpoch;
            var strMsg = '4104,0,$UserId,$ServerKey,$AppId,$VersionNo,$ReleaseNo,$BuildNo,$SystemId,$LoginTime,$LangNo\r\n';
            await SendMessage(strMsg);          
        }

        Future<void> SendMessage(String message) async 
        { 
            //if (RouterRunning)
            {
                //print('SEND: $message');
                socket.write(message);
                await Future.delayed(Duration(milliseconds: 50));
            }
        }

        void ProcessRecv(String str)
        {
            if (str.startsWith("4104"))
            {
                if (str.endsWith("0\r\n"))
                {
                    IsLoggedIn = true;
                }
            }
            else if (str.startsWith("4102"))
            {
                var res = DecodePriceProtocol(str);
                if (OnPriceResp != null) OnPriceResp(res);
            }
            else if (str.startsWith("4111"))
            {
                var res = DecodeTickerProtocol(str);
                if (OnTickerResp != null) OnTickerResp(res);
            }
        }

        PriceResponse DecodePriceProtocol(String prot)
        {
            try
            {
                var pr = new PriceResponse();
                var ss = prot.split(",");

                if (ss.length >= 58)
                {
                    pr.prodCode = ss[2];
                    pr.productName = ss[3];
                    pr.productType = int.parse(ss[4]);
                    pr.contractSize = int.parse(ss[5]);
                    pr.expiryDate = int.parse(ss[6]);
                    pr.instrumentCode = ss[7];
                    pr.currency = ss[8];
                    pr.strike = double.parse(ss[9]);
                    pr.callPut = ss[10];
                    pr.underlying = ss[11];

                    pr.bidPrice = [0, 0, 0, 0, 0];
                    pr.bidQty = [0, 0, 0, 0, 0];

                    pr.bidPrice[0] = double.parse(ss[12]);
                    pr.bidQty[0] = int.parse(ss[13]);
                    pr.bidPrice[1] = double.parse(ss[14]);
                    pr.bidQty[1] = int.parse(ss[15]);
                    pr.bidPrice[2] = double.parse(ss[16]);
                    pr.bidQty[2] = int.parse(ss[17]);
                    pr.bidPrice[3] = double.parse(ss[18]);
                    pr.bidQty[3] = int.parse(ss[19]);
                    pr.bidPrice[4] = double.parse(ss[20]);
                    pr.bidQty[4] = int.parse(ss[21]);

                    pr.askPrice = [0, 0, 0, 0, 0];
                    pr.askQty = [0, 0, 0, 0, 0];

                    pr.askPrice[0] = double.parse(ss[22]);
                    pr.askQty[0] = int.parse(ss[23]);
                    pr.askPrice[1] = double.parse(ss[24]);
                    pr.askQty[1] = int.parse(ss[25]);
                    pr.askPrice[2] = double.parse(ss[26]);
                    pr.askQty[2] = int.parse(ss[27]);
                    pr.askPrice[3] = double.parse(ss[28]);
                    pr.askQty[3] = int.parse(ss[29]);
                    pr.askPrice[4] = double.parse(ss[30]);
                    pr.askQty[4] = int.parse(ss[31]);

                    pr.lastPrice = [0, 0, 0, 0, 0];
                    pr.lastQty = [0, 0, 0, 0, 0];

                    pr.lastPrice[0] = double.parse(ss[32]);
                    pr.lastQty[0] = int.parse(ss[33]);
                    pr.lastPrice[1] = double.parse(ss[34]);
                    pr.lastQty[1] = int.parse(ss[35]);
                    pr.lastPrice[2] = double.parse(ss[36]);
                    pr.lastQty[2] = int.parse(ss[37]);
                    pr.lastPrice[3] = double.parse(ss[38]);
                    pr.lastQty[3] = int.parse(ss[39]);
                    pr.lastPrice[4] = double.parse(ss[40]);
                    pr.lastQty[4] = int.parse(ss[41]);

                    pr.openInterest = int.parse(ss[42]);
                    pr.turnoverAmount = double.parse(ss[43]);
                    pr.turnoverVolume = int.parse(ss[44]);
                    //pr.reserved1 = double.parse(ss[45]);
                    //pr.reserved2 = double.parse(ss[46]);
                    pr.equilibriumPrice = double.parse(ss[47]);

                    pr.open = double.parse(ss[48]);
                    pr.high = double.parse(ss[49]);
                    pr.low = double.parse(ss[50]);
                    pr.previousClose = double.parse(ss[51]);
                    pr.previousCloseDate = double.parse(ss[52]);
                    //pr.notUsed = double.parse(ss[53]);
                    pr.tradeStateNo = double.parse(ss[54]);

                    pr.lotSize = int.parse(ss[55]);
                    pr.tickSize = int.parse(ss[56]);
                    pr.updateTime = DateTime.now().millisecondsSinceEpoch;

                    // ignore everything after newline
                    int posNL = ss[57].indexOf("\r\n");
                    if (posNL >= 0)
                    {
                        var dt = ss[57].substring(0, posNL);
                        pr.updateTime = int.parse(dt);
                    }
                    return pr;
                }
            }
            catch(e) { }
            return null;
        }

        TickerResponse DecodeTickerProtocol(String prot)
        {
            try
            {
                var tr = new TickerResponse();
                var ss = prot.split(",");

                if (ss.length >= 11)
                {
                    tr.tickerTime = int.parse(ss[2]);
                    tr.prodCode = ss[3];
                    tr.price = double.parse(ss[4]);
                    tr.qty = int.parse(ss[5]);
                    tr.dealSrc = int.parse(ss[6]);              
                    tr.hitSide = ss[8];
                    tr.buySideBroker = ss[9];
                    tr.sellSideBroker = "";

                    // adjust price if decInPrc > 0
                    var dec = int.parse(ss[7]);
                    if (dec > 0)
                    {
                        var mul = 10 * dec;
                        var prc = tr.price / mul;
                        tr.price = prc;
                    }

                    // ignore everything after newline
                    int posNL = ss[10].indexOf("\r\n");
                    if (posNL >= 0)
                    {
                        tr.sellSideBroker = ss[10].substring(0, posNL);
                    }
                    return tr;
                }
            }
            catch(e) { }
            return null;
        }

        void SubcribePrice(List<String> prods)
        {
            prods.forEach((element) => _SubcribePrice(element));
        }

        void _SubcribePrice(String prod)
        {
            var strMsg = '4107,0,$prod,0,1,0\r\n';
            SendMessage(strMsg);
        }

        void UnSubscribePrice(List<String> prods)
        {
            prods.forEach((element) => _UnSubscribePrice(element));
        }

        void _UnSubscribePrice(String prod)
        {
            var strMsg = '4108,0,$prod,0,0\r\n';
            SendMessage(strMsg);
        }

        void SubcribeTicker(List<String> prods)
        {
            prods.forEach((element) => _SubcribeTicker(element));
        }

        void _SubcribeTicker(String prod)
        {
            var strMsg = '4107,0,$prod,0,1,2\r\n';
            SendMessage(strMsg);
        }

        void UnSubscribeTicker(List<String> prods)
        {
            prods.forEach((element) => _UnSubscribeTicker(element));
        }

        void _UnSubscribeTicker(String prod)
        {
            var strMsg = '4108,0,$prod,0,2\r\n';
            SendMessage(strMsg);
        }
    }