﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Timers;


namespace AutobotServer
{
    public class ServerInstance : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            var h = PropertyChanged;
            if (null != h) h(this, new PropertyChangedEventArgs(propertyName));
        }

        #region Data
        private int m_iInstanceId;
        private ServerInstanceState m_eServerState;

        private ServerCaller m_ServerCaller;
        private Form1 m_pParentForm;
        private ServerManager m_pParentManager;
        private String m_sClientID;
        private String m_sClientIDShort;

        private int m_iMessageCount;
        private DateTime m_oLastAlive;

        public String ShortClientID { get { return this.m_sClientIDShort; } set { if (value != this.m_sClientIDShort) { this.m_sClientIDShort = value; OnPropertyChanged("m_sClientIDShort"); } } }
        public String ClientID { get { return this.m_sClientID; } set { if (value != this.m_sClientID) { this.m_sClientID = value; OnPropertyChanged("m_sClientID"); } } }
        public BindingList<StatusPair> m_lStats;
        #endregion //Data

        public enum ServerInstanceState
        {
            Idle,
            Getting_MessageSize,
            Getting_Message,
        };

        public ServerInstance(  int a_iIntanceId
                              , System.Net.Sockets.Socket a_SocketForClient
                              , Form1 a_pParentForm
                              , ServerManager a_pParentManager
                            )
        {
            m_iInstanceId = a_iIntanceId;
            m_pParentForm = a_pParentForm;
            m_pParentManager = a_pParentManager;
            m_iMessageCount = 0;
            m_lStats = new BindingList<StatusPair>();
            m_oLastAlive = DateTime.Now;

            m_eServerState = ServerInstanceState.Idle;

            m_ServerCaller = new ServerCaller(a_pParentForm, a_SocketForClient);
            m_ServerCaller.TcpOutReceived += new TcpDataReceivedHandler(this.TcpOutputStream);
            m_ServerCaller.TcpErrorReceived += new TcpDataReceivedHandler(this.TcpErrorStream);
            m_ServerCaller.HasDataEventCB += new HasDataHandler(this.HasData);
            m_ServerCaller.Completed += new EventHandler(this.processCompletedOrCanceled);
            m_ServerCaller.Cancelled += new EventHandler(this.processCompletedOrCanceled);
            m_ServerCaller.Failed += new System.Threading.ThreadExceptionEventHandler(this.processFailed);
            m_ServerCaller.Start();

            ClientID = "Waiting";
            ShortClientID = "Waiting";
        }
        #region Ui Helper
        public void BindUi(DataGridView a_dg)
        {
            a_dg.DataSource = m_lStats;
        }
        #endregion
        
        #region Methods
        public void Cancel()
        {
            m_ServerCaller.Cancel();
            m_ServerCaller = null;
        }

        public void SendMessage(String a_Msg)
        {
            if (null!=m_ServerCaller)
            {
                string sMsgHeader = "Praveen_" + a_Msg.Length.ToString("D8") + "_" + m_iMessageCount.ToString("D7");
                m_ServerCaller.SendData(sMsgHeader + a_Msg);
                ++m_iMessageCount;
            }
        }

        private void LostConnection()
        {
            if (null != m_ServerCaller)
            {
                m_ServerCaller.Cancel();
                m_ServerCaller = null;
            }

            m_pParentManager.ServerInstanceDied(this);
        }

        public void HasData(object sender, TcpDataReceivedEventArgs e)
        {
            if (null == m_ServerCaller)
            {
                return;
            }
            if (m_eServerState == ServerInstanceState.Idle)
            {
                m_eServerState = ServerInstanceState.Getting_MessageSize;
                m_ServerCaller.ReadAmount(24);
            }
        }

        public void TcpOutputStream(object sender, TcpDataReceivedEventArgs e)
        {
            if (null == m_ServerCaller)
            {
                return;
            }

            if (m_eServerState == ServerInstanceState.Getting_MessageSize)
            {
                if (24 == e.Text.Length && e.Text.Substring(0, 8) == "Praveen_")
                {
                    int iSize = Convert.ToInt32(e.Text.Substring(8, 8));
                    int iMsgNumber = Convert.ToInt32(e.Text.Substring(17, 7));
                    m_eServerState = ServerInstanceState.Getting_Message;
                    m_ServerCaller.ReadAmount(iSize);
                }
                else
                {
                    LostConnection();
                }
            }
            else if (m_eServerState == ServerInstanceState.Getting_Message)
            {
                m_oLastAlive = DateTime.Now;
                ProcessClientMessage(e.Text);
                m_eServerState = ServerInstanceState.Idle;
            }
        }

        public void TcpErrorStream(object sender, TcpDataReceivedEventArgs e)
        {
            LostConnection();
        }

        public void processCompletedOrCanceled(object sender, EventArgs e)
        {
            LostConnection();
        }

        public void processFailed(object sender, System.Threading.ThreadExceptionEventArgs e)
        {
            LostConnection();
        }

        public bool TryPing()
        {
            TimeSpan oElapsed = DateTime.Now - m_oLastAlive;
            if(oElapsed.Seconds < 20)
            {
                return true;
            }
            else if (oElapsed.Seconds < 40)
            {
                SendMessage("ping");
                return true;
            }
            else if (oElapsed.Seconds < 60)
            {
                return true;
            }
            return false;
        }
        private void UpdateStats(string a_sStats)
        {
            m_pParentForm.SuspendLayoutForAutoM(this);
            foreach (var oPair in m_lStats)
            {
                oPair.CurrentCount = 0;
            }

            String[] sList = a_sStats.Split(',');
            foreach (var sData in sList)
            {
                String[] sCouple = sData.Split(':');
                int iVal = 0;
                if(2==sCouple.Length && Int32.TryParse(sCouple[1], out iVal))
                {
                    bool bFound = false;
                    foreach (var oPair in m_lStats)
                    {
                        if (oPair.Status.Equals(sCouple[0]))
                        {
                            oPair.CurrentCount = iVal;
                            bFound = true;
                            break;
                        }
                    }

                    if (!bFound)
                    {
                        m_lStats.Add(new StatusPair(sCouple[0], iVal));
                    }
                }
            }
            m_pParentForm.ResumeLayoutForAutoM(this);
        }

        private void ProcessClientMessage(String a_sMsg)
        {
            // we expect only 1 System Message per message from server and needs to be lowercase
            if (a_sMsg.StartsWith("chat ") && a_sMsg.Length > 5)
            {
                string sCommand = "[" + ShortClientID +"]" + a_sMsg.Substring(5);
                m_pParentForm.IncomingChatMsg(sCommand);
                return;
            }
            else if (a_sMsg.StartsWith("summary ") && a_sMsg.Length > 8)
            {
                UpdateStats(a_sMsg.Substring(8));
                return;
            }
            else if (a_sMsg.StartsWith("machine ") && a_sMsg.Length > 8)
            {
                ClientID = a_sMsg.Substring(8);
                string[] sIds = ClientID.Split(',');
                if(sIds.Length > 0)
                {
                    ShortClientID = sIds[0];
                }
                else
                {
                    ShortClientID = ClientID;
                }
                return;
            }
            else if (a_sMsg.StartsWith("sysresponse ") && a_sMsg.Length > 12)
            {
                string sCommand = a_sMsg.Substring(12);
                m_pParentForm.IncomingResponse(sCommand);
                return;
            }

            String[] sList = a_sMsg.Split(',');
            foreach (var aCmd in sList)
            {
                string oCmd = aCmd.Trim();
                if (oCmd.StartsWith("ping"))
                {
                    SendMessage("pong");
                }
                else if (oCmd.StartsWith("pong"))
                {
                }
                else
                {
                    //Unknown
                }
            }


        }

        #endregion // Methods

    };
}
