using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SuperSocket.Common;
using SuperSocket.SocketBase;
using SuperSocket.SocketBase.Command;
using SuperSocket.SocketBase.Protocol;
using System.Reflection;


namespace TZONE.GPSSocketServer
{
    /// <summary>
    /// It is the kind of protocol that
    /// the first two bytes of each command are { 0x68, 0x68 }
    /// and the last two bytes of each command are { 0x0d, 0x0a }
    /// and the 16th byte (data[15]) of each command indicate the command type
    /// if data[15] = 0x10, the command is a keep alive one
    /// if data[15] = 0x1a, the command is position one
    /// </summary>
    public class GPSRequestFilter : RequestFilterBase<BinaryRequestInfo>
    {
        //private static byte[] m_StartMark = new byte[] { 0x24, 0x24 };
        //private static byte[] m_EndMark = new byte[] { 0x0d, 0x0a };

        private SearchMarkState<byte> m_StartSearchState;
        private SearchMarkState<byte> m_EndSearchState;

        private bool m_FoundStart = false;


        //////////////////////////////////////////////////////////////////////////


        private static List<byte[]> m_StartMark = new List<byte[]> 
        { 
            /************************************************************************/
            /*                            OTA                                       */
            /************************************************************************/
            new byte[] { 0x40, 0x56, 0x54, 0x44,} ,  //@VTD
            new byte[] { 0x40, 0x52, 0x45, 0x51,} ,  //@REQ
            new byte[] { 0x40, 0x53, 0x41, 0x56,} ,  //@SAV
            new byte[] { 0x40, 0x56, 0x45, 0x45,} ,  //@VEE
            new byte[] { 0x40, 0x52, 0x45, 0x50,} ,  //@REP
            new byte[] { 0x40, 0x53, 0x41, 0x50,} ,  //@SAP
            /************************************************************************/
            /*                          正常上报数据                                */
            /************************************************************************/
            new byte[] { 0x24, 0x24 } ,              //$$
            new byte[] { 0x24, 0x56 } ,              //$V
            new byte[] { 0x24, 0x54 } ,              //$T
            new byte[] { 0x24, 0x53 } ,              //$S
            new byte[] { 0x24, 0x48 } ,              //$H
            new byte[] { 0x24, 0x55 }                //$U
        };

        private static List<byte[]> m_EndMark = new List<byte[]> 
        { 
            /************************************************************************/
            /*                            OTA                                       */
            /************************************************************************/
             new byte[] { 0x23},               //#
             new byte[] { 0x23},               //#
             new byte[] { 0x23},               //#
             new byte[] { 0x23},               //#
             new byte[] { 0x23},               //#
             new byte[] { 0x23},               //#
             /************************************************************************/
             /*                           正常上报数据                               */
             /************************************************************************/
             new byte[] { 0x0d, 0x0a },        //\r\n
             new byte[] { 0x23, 0x0d, 0x0a },  //#\r\n
             new byte[] { 0x0d, 0x0a },        //\r\n
             new byte[] { 0x0d, 0x0a },        //\r\n
             new byte[] { 0x0d, 0x0a },        //\r\n
             new byte[] { 0x23}                //#
        };

        private int protocolType = 0;

        //////////////////////////////////////////////////////////////////////////


        public GPSRequestFilter()
        {
            //m_StartSearchState = new SearchMarkState<byte>(m_StartMark);
            //m_EndSearchState = new SearchMarkState<byte>(m_EndMark);
        }

        public override BinaryRequestInfo Filter(byte[] readBuffer, int offset, int length, bool toBeCopied, out int left)
        {
            left = 0;
            for (int i = 0, j = m_StartMark.Count; i < j; i++)
            {
                var sm = readBuffer.SearchMark(offset, length, m_StartMark[i]);
                if (sm != null)
                {
                    protocolType = i;
                    m_StartSearchState = new SearchMarkState<byte>(m_StartMark[protocolType]);
                    m_EndSearchState = new SearchMarkState<byte>(m_EndMark[protocolType]);
                    break;
                }
            }
            if (!m_FoundStart)
            {
                var pos = readBuffer.SearchMark(offset, length, m_StartSearchState);

                //Don't cache invalid data
                if (pos < 0)
                    return null;

                //Found start mark
                m_FoundStart = true;

                int searchEndMarkOffset = pos + m_StartMark[protocolType].Length;

                //The end mark could not exist in this round received data
                if (offset + length <= searchEndMarkOffset)
                {
                    AddArraySegment(m_StartMark[protocolType], 0, m_StartMark[protocolType].Length, false);
                    return null;
                }

                int searchEndMarkLength = offset + length - searchEndMarkOffset;

                var endPos = readBuffer.SearchMark(searchEndMarkOffset, searchEndMarkLength, m_EndSearchState);

                if (endPos < 0)
                {
                    AddArraySegment(readBuffer, pos, length + offset - pos, toBeCopied);
                    return null;
                }

                int parsedLen = endPos - pos + m_EndMark[protocolType].Length;
                left = length - parsedLen;

                var requestInfo = CreateCommandInfo(readBuffer.CloneRange(pos, parsedLen), m_StartMark[protocolType].Length);

                ResetState();

                return requestInfo;
            }
            else
            {
                var endPos = readBuffer.SearchMark(offset, length, m_EndSearchState);
                //Haven't found end mark
                if (endPos < 0)
                {
                    AddArraySegment(readBuffer, offset, length, toBeCopied);
                    return null;
                }

                //Found end mark
                int parsedLen = endPos - offset + m_EndMark[protocolType].Length;
                left = length - parsedLen;

                byte[] commandData = new byte[BufferSegments.Count + parsedLen];

                if (BufferSegments.Count > 0)
                    BufferSegments.CopyTo(commandData, 0, 0, BufferSegments.Count);

                Array.Copy(readBuffer, offset, commandData, BufferSegments.Count, parsedLen);

                var requestInfo = CreateCommandInfo(commandData, m_StartMark[protocolType].Length);

                ResetState();

                return requestInfo;
            }
        }

        private void ResetState()
        {
            m_StartSearchState.Matched = 0;
            m_EndSearchState.Matched = 0;
            m_FoundStart = false;

            ClearBufferSegments();
        }
        private BinaryRequestInfo CreateCommandInfo(byte[] data,int sml)
        {
#if DEBUG
            string a = BitConverter.ToString(data, 0, sml);
#endif
            return new BinaryRequestInfo(BitConverter.ToString(data, 0, sml), data);
        }
    }
}
