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

namespace arDev
{

    public abstract partial class arRS232 : ISerialComm, IDisposable
    {
        protected System.IO.Ports.SerialPort _device;
        protected ManualResetEvent _mre;
        protected byte[] LastReceiveBuffer = new byte[] { };
        /// <summary>
        /// 최종 전송 메세지
        /// </summary>
        public byte[] lastSendBuffer = new byte[] { };
        //public int ValidCheckTimeMSec { get; set; } = 5000;
        protected List<byte> tempBuffer = new List<byte>();
        protected Boolean findSTX = false;
        public string ErrorMessage { get; set; }
        public DateTime LastConnTime { get; set; }
        public DateTime LastConnTryTime { get; set; }
        public DateTime lastSendTime;
        /// <summary>
        /// 메세지 수신시 사용하는 내부버퍼
        /// </summary>
        protected List<byte> _buffer = new List<byte>();
        /// <summary>
        /// 데이터조회간격(초)
        /// </summary>
        public float ScanInterval { get; set; }

        // public byte[] LastRecvData;
        public string LastRecvString
        {
            get
            {
                if (LastReceiveBuffer == null) return String.Empty;
                else return System.Text.Encoding.Default.GetString(LastReceiveBuffer);
            }
        }
        /// <summary>
        /// 마지막으로 데이터를 받은 시간
        /// </summary>
        public DateTime lastRecvTime;


        public int WriteError = 0;
        public string WriteErrorMessage = string.Empty;
        public int WaitTimeout { get; set; } = 1000;
        public int MinRecvLength { get; set; } = 1;
        /// <summary>
        /// 포트이름
        /// </summary>
        [Description("시리얼 포트 이름")]
        [Category("설정"), DisplayName("Port Name")]
        public string PortName
        {
            get
            {
                if (_device == null) return string.Empty;
                else return _device.PortName;
            }
            set
            {
                if (this.IsOpen)
                {
                    Message?.Invoke(this, new MessageEventArgs("포트가 열려있어 포트이름을 변경할 수 없습니다", true));
                }
                else if (String.IsNullOrEmpty(value) == false)
                    _device.PortName = value;
                else
                {
                    Message?.Invoke(this, new MessageEventArgs("No PortName", true));
                }
            }
        }

        public int BaudRate
        {
            get
            {
                if (_device == null) return 0;
                else return _device.BaudRate;
            }
            set
            {
                if (this.IsOpen)
                {
                    Message?.Invoke(this, new MessageEventArgs("포트가 열려있어 BaudRate(를) 변경할 수 없습니다", true));
                }
                else if (value != 0)
                    _device.BaudRate = value;
                else Message?.Invoke(this, new MessageEventArgs("No baud rate", true));
            }
        }


        public arRS232()
        {
            _device = new System.IO.Ports.SerialPort();
            this.BaudRate = 9600;
            ScanInterval = 10;
            _device.DataReceived += barcode_DataReceived;
            _device.ErrorReceived += this.barcode_ErrorReceived;
            _device.WriteTimeout = 5000;
            _device.ReadTimeout = 5000;
            // _device.DtrEnable = false;
            _device.ReadBufferSize = 8192;
            _device.WriteBufferSize = 8192;

            ErrorMessage = string.Empty;
            lastRecvTime = DateTime.Parse("1982-11-23");
            LastConnTime = DateTime.Parse("1982-11-23");
            LastConnTryTime = DateTime.Parse("1982-11-23");
            lastRecvTime = DateTime.Parse("1982-11-23");
            this._mre = new ManualResetEvent(true);
        }

        ~arRS232()
        {
            Dispose(false);
        }


        // Flag: Has Dispose already been called?
        bool disposed = false;

        // Public implementation of Dispose pattern callable by consumers.
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        // Protected implementation of Dispose pattern.
        protected virtual void Dispose(bool disposing)
        {
            if (disposed)
                return;

            if (disposing)
            {
                // Free any other managed objects here.
                //
            }

            _device.DataReceived -= barcode_DataReceived;
            _device.ErrorReceived -= this.barcode_ErrorReceived;

            // Free any unmanaged objects here.
            //
            disposed = true;
        }

        public Boolean Open()
        {
            try
            {
                _device.Open();
                return IsOpen;
            }
            catch (Exception ex)
            {
                ErrorMessage = ex.Message;
                Message.Invoke(this, new MessageEventArgs(ex.Message, true));
                return false;
            }
        }
        public string GetHexString(Byte[] input)
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            foreach (byte b in input)
                sb.Append(" " + b.ToString("X2"));
            return sb.ToString();
        }

        /// <summary>
        /// 포트가 열려있는지 확인
        /// </summary>
        [Description("현재 시리얼포트가 열려있는지 확인합니다")]
        [Category("정보"), DisplayName("Port Open")]
        public Boolean IsOpen
        {
            get
            {
                if (_device == null) return false;
                return _device.IsOpen;
            }
        }

        public virtual bool Close()
        {
            if (_device != null && _device.IsOpen)
            {
                _device.DiscardInBuffer();
                _device.DiscardOutBuffer();
                _device.Close(); //dispose에서는 포트를 직접 클리어하지 않게 해뒀다.
                return true;
            }
            else return false;
        }
        protected Boolean RaiseRecvData()
        {
            return RaiseRecvData(LastReceiveBuffer.ToArray(), false);
        }
        /// <summary>
        /// 수신받은 메세지를 발생 시킵니다
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public virtual Boolean RaiseRecvData(byte[] Data, bool udpatelastbuffer)
        {
            //181206 - 최종수신 메세지 기록
            lastRecvTime = DateTime.Now;
            if (udpatelastbuffer && Data != null)
            {
                if (LastReceiveBuffer == null || LastReceiveBuffer.Length != Data.Length)
                {
                    LastReceiveBuffer = new byte[Data.Length];
                    Array.Copy(Data, LastReceiveBuffer, Data.Length);
                }
            }

            try
            {
                Message?.Invoke(this, new MessageEventArgs(Data, true));    //recvmessage
                if (ProcessRecvData(Data) == false)
                {
                    //Message?.Invoke(this, new MessageEventArgs(Data, true));    //recvmessage
                    Message?.Invoke(this, new MessageEventArgs(this.ErrorMessage, true));   //errormessage
                    return false;
                }
                else
                {

                    return true;
                }
            }
            catch (Exception ex)
            {
                this.ErrorMessage = ex.Message;
                this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
                return false;
            }
        }

        /// <summary>
        /// 수신받은 자료를 처리한다
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public abstract bool ProcessRecvData(byte[] data);

        #region "Internal Events"

        void barcode_ErrorReceived(object sender, System.IO.Ports.SerialErrorReceivedEventArgs e)
        {
            Message?.Invoke(this, new MessageEventArgs(e.ToString(), true));
        }

        byte[] buffer = new byte[] { };
        void barcode_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {

            try
            {
                int ReadCount = _device.BytesToRead;

                buffer = new byte[ReadCount];
                _device.Read(buffer, 0, buffer.Length);

                System.Text.StringBuilder LogMsg = new StringBuilder();

                byte[] remainBuffer;
            Repeat:
                if (CustomParser(buffer, out remainBuffer))
                {
                    //분석완료이므로 받은 데이터를 버퍼에 기록한다
                    if (LastReceiveBuffer == null || (LastReceiveBuffer.Length != tempBuffer.Count))
                        Array.Resize(ref LastReceiveBuffer, tempBuffer.Count);
                    Array.Copy(tempBuffer.ToArray(), LastReceiveBuffer, tempBuffer.Count);
                    tempBuffer.Clear();

                    //수신메세지발생
                    RaiseRecvData();
                    if (remainBuffer != null && remainBuffer.Length > 0)
                    {
                        //버퍼를 변경해서 다시 전송을 해준다.
                        Array.Resize(ref buffer, remainBuffer.Length);
                        Array.Copy(remainBuffer, buffer, remainBuffer.Length);
                        goto Repeat; //남은 버퍼가 있다면 진행을 해준다.
                    }
                }

            }
            catch (Exception ex)
            {
                //if (IsOpen)
                //{
                //    //_device.DiscardInBuffer();
                //    //_device.DiscardOutBuffer();
                //}
                ErrorMessage = ex.Message;
                this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
            }

        }


        #endregion

        #region "External Events"


        /// <summary>
        /// 오류 및 기타 일반 메세지
        /// </summary>
        public event EventHandler<MessageEventArgs> Message;

        #endregion

        #region "Event Args"

        /// <summary>
        /// 데이터를 수신할떄 사용함(RAW 포함)
        /// </summary>
        public class ReceiveDataEventArgs : EventArgs
        {
            private byte[] _buffer = null;

            /// <summary>
            /// 바이트배열의 버퍼값
            /// </summary>
            public byte[] Value { get { return _buffer; } }

            /// <summary>
            /// 버퍼(바이트배열)의 데이터를 문자로 반환합니다.
            /// </summary>
            public string StrValue
            {
                get
                {
                    //return string.Empty; 

                    if (_buffer == null || _buffer.Length < 1) return string.Empty;
                    else return System.Text.Encoding.Default.GetString(_buffer);
                }
            }
            public ReceiveDataEventArgs(byte[] buffer)
            {
                _buffer = buffer;
            }
        }

        /// <summary>
        /// 메세지를 강제 발생
        /// </summary>
        /// <param name="mt"></param>
        /// <param name="message"></param>
        protected virtual void RaiseMessage(MessageType mt, string message)
        {
            this.Message?.Invoke(this, new MessageEventArgs(mt, message));
        }
        public enum MessageType
        {
            Normal,
            Error,
            Send,
            Recv,
        }

        public class MessageEventArgs : EventArgs
        {
            public MessageType MsgType { get; set; }
            private string _message = string.Empty;

            /// <summary>
            /// Recv,Send,Normal,Error 모두 지원
            /// </summary>
            public string Message { get { return _message; } }

            private byte[] _data = null;

            /// <summary>
            /// Recv,Send에서만 값이 존재 합니다
            /// </summary>
            public byte[] Data { get { return _data; } }
            public MessageEventArgs(string Message, bool isError = false)
            {
                if (isError) MsgType = MessageType.Error;
                else MsgType = MessageType.Normal;
                _message = Message;
            }
            public MessageEventArgs(MessageType msgtype, string Message)
            {
                MsgType = msgtype;
                _message = Message;
                _data = System.Text.Encoding.Default.GetBytes(Message);
            }

            public MessageEventArgs(byte[] buffer, bool isRecv = true)
            {
                if (isRecv) MsgType = MessageType.Recv;
                else MsgType = MessageType.Send;
                _data = new byte[buffer.Length];
                Array.Copy(buffer, _data, Data.Length);
                _message = System.Text.Encoding.Default.GetString(_data);
            }

        }

        #endregion



        protected abstract bool CustomParser(byte[] buf, out byte[] remainBuffer);

        /// <summary>
        /// 포트가 열려있거나 데이터 수신시간이 없는경우 false를 반환합니다
        /// </summary>
        public Boolean IsValid
        {
            get
            {
                if (IsOpen == false) return false;
                if (lastRecvTime.Year == 1982) return false;
                var ts = DateTime.Now - lastRecvTime;
                if (ts.TotalSeconds > (this.ScanInterval * 2.5)) return false;
                return true;
            }
        }
        protected bool WriteData(string cmd)
        {
            return WriteData(System.Text.Encoding.Default.GetBytes(cmd));
        }
        /// <summary>
        /// 포트에 쓰기(barcode_DataReceived 이벤트로 메세지수신)
        /// </summary>
        protected Boolean WriteData(byte[] data)
        {
            Boolean bRet = false;

            //171205 : 타임아웃시간추가
            if (!_mre.WaitOne(WaitTimeout))
            {
                ErrorMessage = $"WriteData:MRE:WaitOne:TimeOut {WaitTimeout}ms";
                this.Message?.Invoke(this, new MessageEventArgs(ErrorMessage, true));
                return false;
            }

            _mre.Reset();

            //Array.Resize(ref data, data.Length + 2);

            try
            {
                lastSendTime = DateTime.Now;
                if (lastSendBuffer == null) lastSendBuffer = new byte[data.Length]; //171113
                else Array.Resize(ref lastSendBuffer, data.Length);
                Array.Copy(data, lastSendBuffer, data.Length);

                for (int i = 0; i < data.Length; i++)
                    _device.Write(data, i, 1);

                //_device.Write(data, 0, data.Length);

                //171113
                this.Message?.Invoke(this, new MessageEventArgs(data, false));

                bRet = true;
                WriteError = 0;
                WriteErrorMessage = string.Empty;
            }
            catch (Exception ex)
            {
                // this.isinit = false;
                this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
                bRet = false;
                WriteError += 1; //연속쓰기오류횟수
                WriteErrorMessage = ex.Message;
            }
            finally
            {
                _mre.Set();
            }
            return bRet;
        }
    }
}
