using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FObjBase;
using System.ComponentModel;
using System.Collections.Specialized;

namespace FLY.Thick.BulkDataModule
{
    public class BulkDataServiceClient : FObj, INotifyPropertyChanged,IBulkDataService
    {

        private UInt32 mServerID = 0;
        public UInt32 ServerID
        {
            get { return mServerID; }
            set
            {
                if (mServerID != value)
                {
                    mServerID = value;
                    NotifyPropertyChanged("ServerID");
                }
            }
        }


        private IFConn mConn;
        public IFConn Conn
        {
            get { return mConn; }
            set
            {
                mConn = value;
                NotifyPropertyChanged("Conn");
            }
        }

        private bool isconnected;
        /// <summary>
        /// 准备好，只有在 IsReady=true ，发送的命名才能执行，才能推送
        /// </summary>
        public bool IsReady
        {
            get
            {
                return isconnected;
            }
            protected set
            {
                if (isconnected != value)
                {
                    isconnected = value;
                    NotifyPropertyChanged("IsReady");
                }
            }
        }

        #region IBulkDataService
        class DataRequest
        {
            public object Param;
            public object AsyncState;
            public AsyncCBHandler AsyncDelegate;
            public override bool Equals(object obj)
            {
                if (!(obj is DataRequest))
                    return false;
                DataRequest dr = obj as DataRequest;
                if (!AsyncState.Equals(dr.AsyncState))
                    return false;
                if (!IsSameParam(Param, dr.Param))
                    return false;

                return true;
            }

            bool IsSameParam_PushNewFrameData(object src, object dest)
            {
                if ((src is BULKDATA_OBJ_INTERFACE.Pack_CallPushNewFrameDataParam)
                    && (dest is BULKDATA_OBJ_INTERFACE.Pack_CallPushNewFrameDataParam))
                {
                    BULKDATA_OBJ_INTERFACE.Pack_CallPushNewFrameDataParam fdparam 
                        = src as BULKDATA_OBJ_INTERFACE.Pack_CallPushNewFrameDataParam;
                    BULKDATA_OBJ_INTERFACE.Pack_CallPushNewFrameDataParam cfdparam
                        = dest as BULKDATA_OBJ_INTERFACE.Pack_CallPushNewFrameDataParam;
                    if (fdparam.mix == cfdparam.mix)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                return false;
            }
            bool IsSameParam_GetFrameData(object src, object dest)
            {
                if ((src is BULKDATA_OBJ_INTERFACE.Pack_CallClosePushFrameData) &&
                    (dest is BULKDATA_OBJ_INTERFACE.Pack_CallClosePushFrameData))
                {
                    BULKDATA_OBJ_INTERFACE.Pack_CallClosePushFrameData fdparam =
                        src as BULKDATA_OBJ_INTERFACE.Pack_CallClosePushFrameData;
                    BULKDATA_OBJ_INTERFACE.Pack_CallClosePushFrameData cfdparam =
                        dest as BULKDATA_OBJ_INTERFACE.Pack_CallClosePushFrameData;

                    if ((fdparam.bm == cfdparam.bm) && (fdparam.mix == cfdparam.mix))
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                return false;
            }
            bool IsSameParam_GetTrendData(object src, object dest)
            {
                if ((src is BULKDATA_OBJ_INTERFACE.Pack_CallClosePushTrendData) && 
                    (dest is BULKDATA_OBJ_INTERFACE.Pack_CallClosePushTrendData))
                {
                    BULKDATA_OBJ_INTERFACE.Pack_CallClosePushTrendData tdparam =
                        src as BULKDATA_OBJ_INTERFACE.Pack_CallClosePushTrendData;
                    BULKDATA_OBJ_INTERFACE.Pack_CallClosePushTrendData ctdparam =
                        dest as BULKDATA_OBJ_INTERFACE.Pack_CallClosePushTrendData;
                    if ((tdparam.mix == ctdparam.mix) &&
                        (tdparam.fromBoltNo == ctdparam.fromBoltNo) &&
                        (tdparam.toBoltNo == ctdparam.toBoltNo))
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                return false;
            }

            bool IsSameParam_GetFrameDataMix(object src, object dest)
            {
                if ((src is Pack_CallClosePushFrameDataMixRequest) &&
                    (dest is Pack_CallClosePushFrameDataMixRequest))
                {
                    return ((Pack_CallClosePushFrameDataMixRequest)src).IsSame(
                        dest as Pack_CallClosePushFrameDataMixRequest);
                }
                return false;
            }
            public bool IsSameParam(object src, object dest)
            {
                if (IsSameParam_GetFrameData(src, dest) == true)
                    return true;

                if (IsSameParam_GetTrendData(src, dest) == true)
                    return true;

                if (IsSameParam_PushNewFrameData(src, dest) == true)
                    return true;

                if (IsSameParam_GetFrameDataMix(src, dest) == true)
                    return true;
                return false;
            }

        }
        List<DataRequest> dataRequestList = new List<DataRequest>();

        #endregion

        #region 缓冲区
        private List<FR_DATA> m_fr_data = new List<FR_DATA>();
        public List<FR_DATA> Datas
        {
            get { return m_fr_data; }
        }
        public event NotifyDatasChangedEventHandler DatasChanged;

        private int count = 0;
        /// <summary>
        /// 数量
        /// </summary>
        public int Count
        {
            get { return count; }
            set
            {
                if (count != value)
                {
                    count = value;
                    NotifyPropertyChanged("Count");
                }
            }
        }
        private int currentbookmark = -1;
        /// <summary>
        /// 最新的bookmark
        /// </summary>
        public int CurrentBookmark
        {
            get { return currentbookmark; }
            set
            {
                if (currentbookmark != value)
                {
                    currentbookmark = value;
                    NotifyPropertyChanged("CurrentBookmark");
                }
            }
        }

        private int nbolts=80;
        /// <summary>
        /// 分区数
        /// </summary>
        public int NBolts
        {
            get { return nbolts; }
            protected set {
                if (nbolts != value)
                {
                    nbolts = value;
                    NotifyPropertyChanged("NBolts");
                }
            }
        }
        private int boltNo1st=0;
        /// <summary>
        /// 第1分区号
        /// </summary>
        public int BoltNo1st
        {
            get { return boltNo1st; }
            protected set
            {
                if (boltNo1st != value)
                {
                    boltNo1st = value;
                    NotifyPropertyChanged("BoltNo1st");
                }
            }
        }
        #endregion
        public BulkDataServiceClient():this(BULKDATA_OBJ_INTERFACE.ID)
        {
        }
        public BulkDataServiceClient(UInt32 id)
            : base()
        {

            ServerID = id;
            this.PropertyChanged += BulkDataServiceClient_PropertyChanged;
        }

        private void BulkDataServiceClient_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if ((e.PropertyName == "Count")||(e.PropertyName == "CurrentBookmark"))
            {
                if (Datas.Count() > Count)
                {
                    Datas.RemoveRange(0, Datas.Count() - Count);
                } 
            }
        }
        #region IBulkDataService

        public void GetFrameData(BULKDATA_OBJ_INTERFACE.Pack_CallGetFrameDataParam param, AsyncCBHandler AsyncDelegate, object AsyncState)
        {
            CurrObjSys.CallFunctionEx(mConn, mServerID, ID,
                BULKDATA_OBJ_INTERFACE.CALL_GET_FRAMEDATA,
                param.ToBytes());//自己管理 AsyncDelegate, 不使用ObjSys 的
            
            DataRequest dr = new DataRequest()
                    {
                        Param = param,
                        AsyncState = AsyncState,
                        AsyncDelegate = AsyncDelegate
                    };

            if (!dataRequestList.Exists(_dr => _dr.Equals(dr)))
            {
                dataRequestList.Add(dr);
            }
        }

        public void ClosePushFrameData(BULKDATA_OBJ_INTERFACE.Pack_CallClosePushFrameData param, object AsyncState)
        {
            FObjSys.Current.CallFunctionEx(
                mConn, mServerID, ID,
                BULKDATA_OBJ_INTERFACE.CALL_CLOSE_PUSH_FRAMEDATA,
                param.ToBytes());

            DataRequest dr = new DataRequest()
            {
                Param = param,
                AsyncState = AsyncState
            };
            dataRequestList.Remove(dr);
        }

        public void GetTrendData(BULKDATA_OBJ_INTERFACE.Pack_CallGetTrendDataParam param, AsyncCBHandler AsyncDelegate, object AsyncState)
        {

            CurrObjSys.CallFunctionEx(mConn, mServerID, ID,
                BULKDATA_OBJ_INTERFACE.CALL_GET_TRENDDATA,
                param.ToBytes());//自己管理 AsyncDelegate, 不使用ObjSys 的
            
            DataRequest dr = new DataRequest()
            {
                Param = param,
                AsyncState = AsyncState,
                AsyncDelegate = AsyncDelegate
            };

            if (!dataRequestList.Exists(_dr => _dr.Equals(dr)))
            {
                dataRequestList.Add(dr);
            }
        }

        public void ClosePushTrendData(BULKDATA_OBJ_INTERFACE.Pack_CallClosePushTrendData param, object AsyncState)
        {
            FObjSys.Current.CallFunctionEx(
                mConn, mServerID, ID,
                BULKDATA_OBJ_INTERFACE.CALL_CLOSE_PUSH_TRENDDATA,
                param.ToBytes());
            DataRequest dr = new DataRequest()
            {
                Param = param,
                AsyncState = AsyncState
            };
            dataRequestList.Remove(dr);
        }


        public void PushNewFrameData(
            BULKDATA_OBJ_INTERFACE.Pack_CallPushNewFrameDataParam param, AsyncCBHandler AsyncDelegate, object AsyncState) 
        {
            CurrObjSys.CallFunctionEx(mConn, mServerID, ID,
        BULKDATA_OBJ_INTERFACE.CALL_PUSH_NEWFRAMEDATA,
        param.ToBytes());//自己管理 AsyncDelegate, 不使用ObjSys 的

            DataRequest dr = new DataRequest()
            {
                Param = param,
                AsyncState = AsyncState,
                AsyncDelegate = AsyncDelegate
            };

            if (!dataRequestList.Exists(_dr => _dr.Equals(dr)))
            {
                dataRequestList.Add(dr);
            }
        }

        public void ClosePushNewFrameData(
            BULKDATA_OBJ_INTERFACE.Pack_CallClosePushNewFrameData param, object AsyncState) 
        {
            FObjSys.Current.CallFunctionEx(
        mConn, mServerID, ID,
        BULKDATA_OBJ_INTERFACE.CALL_CLOSE_PUSH_NEWFRAMEDATA,
        param.ToBytes());
            DataRequest dr = new DataRequest()
            {
                Param = param,
                AsyncState = AsyncState
            };
            dataRequestList.Remove(dr);
        }

        public void GetFrameDataMix(Pack_CallGetFrameDataMixRequest param, AsyncCBHandler AsyncDelegate, object AsyncState)
        {
            CurrObjSys.CallFunctionEx(mConn, mServerID, ID,
                BULKDATA_OBJ_INTERFACE.CALL_GET_FRAMEDATAMIX,
                param.ToBytes());//自己管理 AsyncDelegate, 不使用ObjSys 的

            DataRequest dr = new DataRequest()
            {
                Param = param,
                AsyncState = AsyncState,
                AsyncDelegate = AsyncDelegate
            };

            if (!dataRequestList.Exists(_dr => _dr.Equals(dr)))
            {
                dataRequestList.Add(dr);
            }
        }


        public void ClosePushFrameDataMix(Pack_CallClosePushFrameDataMixRequest param, object AsyncState)
        {
            FObjSys.Current.CallFunctionEx(
                mConn, mServerID, ID,
                BULKDATA_OBJ_INTERFACE.CALL_CLOSE_PUSH_FRAMEDATAMIX,
                param.ToBytes());

            DataRequest dr = new DataRequest()
            {
                Param = param,
                AsyncState = AsyncState
            };
            dataRequestList.Remove(dr);
        }

        public void Clear()
        {
            FObjSys.Current.CallFunctionEx(
                mConn, mServerID, ID,
                BULKDATA_OBJ_INTERFACE.CALL_CLEAR, null);
        }

        /// <summary>
        /// 只要关于AsyncState 的都要删除
        /// </summary>
        /// <param name="AsyncState"></param>
        public void ClosePush(Predicate<object> match) 
        {
            dataRequestList.RemoveAll(delegate(DataRequest dr)
            {
                return match(dr.AsyncState);
            });
        }
        #endregion
        #region FObj
        public override void ConnectNotify(IFConn from)
        {
            Conn = from;
            IsReady = Conn.IsConnected;
            if (!from.IsConnected)
            {
                //连接断开，删除全部！！！
                dataRequestList.Clear();
            }
            else
            {
                Datas.Clear();
                CurrentBookmark = -1;
                Count = 0;
                

                CurrObjSys.GetValueEx(
                    mConn, mServerID, ID,
                    BULKDATA_OBJ_INTERFACE.GET_PARAMS);

                CurrObjSys.CallFunctionEx(
                    mConn, mServerID, ID,
                    BULKDATA_OBJ_INTERFACE.CALL_GET_LIST_NEW_10, null);

                CurrObjSys.CallFunctionEx(
                    mConn, mServerID, ID,
                    BULKDATA_OBJ_INTERFACE.CALL_GET_LIST_OTHER, null);

                CurrObjSys.SenseConfigEx(
                    mConn, mServerID, ID, 0xffffffff,
                    SENSE_CONFIG.ADD);
            }
        }
        public override void PushCallFunction(IFConn from, UInt32 srcid, UInt32 magic, UInt16 funcid, byte[] retdata, object AsyncDelegate, object AsyncState)
        {
            switch (funcid)
            {
                case BULKDATA_OBJ_INTERFACE.CALL_PUSH_NEWFRAMEDATA:
                    {
                        BULKDATA_OBJ_INTERFACE.Pack_CallPushNewFrameDataReturn reponse = new BULKDATA_OBJ_INTERFACE.Pack_CallPushNewFrameDataReturn();
                        if (!reponse.TryParse(retdata))
                            return;
                        //当为push时，没有AsyncDelegate，AsyncState

                        List<DataRequest> drs = dataRequestList.FindAll(dr => dr.IsSameParam(dr.Param, reponse));
                        foreach (DataRequest dr in drs)
                        {
                            dr.AsyncDelegate(dr.AsyncState, reponse);
                        }
                    }break;
                case BULKDATA_OBJ_INTERFACE.CALL_GET_FRAMEDATA:
                    {
                        BULKDATA_OBJ_INTERFACE.Pack_CallGetFrameDataReturn reponse = new BULKDATA_OBJ_INTERFACE.Pack_CallGetFrameDataReturn();
                        if (!reponse.TryParse(retdata))
                            return;
                        //当为push时，没有AsyncDelegate，AsyncState

                        List<DataRequest> drs = dataRequestList.FindAll(dr => dr.IsSameParam(dr.Param, reponse));
                        foreach (DataRequest dr in drs)
                        {
                            dr.AsyncDelegate(dr.AsyncState, reponse);
                        }
                    } break;
                case BULKDATA_OBJ_INTERFACE.CALL_GET_TRENDDATA:
                    {
                        BULKDATA_OBJ_INTERFACE.Pack_CallGetTrendDataReturn reponse = new BULKDATA_OBJ_INTERFACE.Pack_CallGetTrendDataReturn();
                        if (!reponse.TryParse(retdata))
                            return;
                        List<DataRequest> drs = dataRequestList.FindAll(dr => dr.IsSameParam(dr.Param, reponse));
                        foreach (DataRequest dr in drs)
                        {
                            dr.AsyncDelegate(dr.AsyncState, reponse);
                        }
                    } break;
                case BULKDATA_OBJ_INTERFACE.CALL_GET_FRAMEDATAMIX:
                    {
                        Pack_CallGetFrameDataMixReponse reponse = new Pack_CallGetFrameDataMixReponse();
                        if (!reponse.TryParse(retdata))
                            return;
                        //当为push时，没有AsyncDelegate，AsyncState

                        List<DataRequest> drs = dataRequestList.FindAll(dr => dr.IsSameParam(dr.Param, reponse));
                        foreach (DataRequest dr in drs)
                        {
                            dr.AsyncDelegate(dr.AsyncState, reponse);
                        }
                    }
                    break;
                case BULKDATA_OBJ_INTERFACE.CALL_GET_LIST_NEW_10:
                    {
                        BULKDATA_OBJ_INTERFACE.Pack_CallGetListReponse p = new BULKDATA_OBJ_INTERFACE.Pack_CallGetListReponse();
                        if (!p.TryParse(retdata))
                            return;

                        AddList(p.list);
                    }
                    break;
                case BULKDATA_OBJ_INTERFACE.CALL_GET_LIST_OTHER:
                    {
                        BULKDATA_OBJ_INTERFACE.Pack_CallGetListReponse p = new BULKDATA_OBJ_INTERFACE.Pack_CallGetListReponse();
                        if (!p.TryParse(retdata))
                            return;

                        AddList(p.list);
                    }
                    break;
            }
        }
        public override void PushGetValue(IFConn from, uint srcid, ushort memid, byte[] infodata)
        {
            switch (memid)
            {
                case BULKDATA_OBJ_INTERFACE.GET_PARAMS:
                    {
                        BULKDATA_OBJ_INTERFACE.Pack_Params p = new BULKDATA_OBJ_INTERFACE.Pack_Params();
                        if (!p.TryParse(infodata))
                            return;
                        Count = p.count;
                        CurrentBookmark = p.currentbookmark;
                        NBolts = p.nbolts;
                        BoltNo1st = p.boltNo1st;
                    }
                    break;
            }
        }
        public override void PushInfo(IFConn from, uint srcid, ushort infoid, byte[] infodata)
        {
            switch (infoid)
            {
                case BULKDATA_OBJ_INTERFACE.PUSH_PARAMS:
                    {
                        PushGetValue(from, srcid, BULKDATA_OBJ_INTERFACE.GET_PARAMS, infodata);
                    }break;
                case BULKDATA_OBJ_INTERFACE.PUSH_CHANGED:
                    {
                        BULKDATA_OBJ_INTERFACE.Pack_PushChanged p = new BULKDATA_OBJ_INTERFACE.Pack_PushChanged();
                        
                        if (!p.TryParse(infodata))
                            return;
                        AddList(p.data);
                    }
                    break;
            }
        }
        #endregion
        void AddList(List<FR_DATA> datas)
        {
            if (Datas.Count == 0)
            {
                //没有数据，肯定插入到尾
                Datas.AddRange(datas);
                DatasChanged?.Invoke(
                    this,
                    new NotifyDatasChangedEventArgs()
                    {
                        Action = NotifyDatasChangedAction.Add,
                        Items = datas,
                        NewStartingIndex = 0
                    });
            }
            else
            {
                //原来已经有数据，肯定插入到头
                Datas.InsertRange(0, datas);
                DatasChanged?.Invoke(
                    this,
                    new NotifyDatasChangedEventArgs()
                    {
                        Action = NotifyDatasChangedAction.Add,
                        Items = datas,
                        NewStartingIndex = 0
                    });
            }
        }
        void AddList(FR_DATA data)
        {
            if (data == null)
            {
                Datas.Clear();
                DatasChanged?.Invoke(
                    this,
                    new NotifyDatasChangedEventArgs() { Action = NotifyDatasChangedAction.Reset });
                return;
            }
            
            int idx = Datas.FindIndex(d => { return d.bookmark == data.bookmark; });

            if (idx == -1)
            {
                //添加
                Datas.Add(data);
                DatasChanged?.Invoke(
                    this,
                    new NotifyDatasChangedEventArgs() {
                        Action = NotifyDatasChangedAction.Add,
                        Items = new List<FR_DATA>(new FR_DATA[] { data}),
                        NewStartingIndex = Datas.Count()-1
                    });
            }
            else
            {
                //修改
                Datas[idx] = data;
                DatasChanged?.Invoke(
                    this,
                    new NotifyDatasChangedEventArgs()
                    {
                        Action = NotifyDatasChangedAction.Replace,
                        Items = new List<FR_DATA>(new FR_DATA[] { data }),
                        NewStartingIndex = idx
                    });
            }
            
        }
        #region INotifyPropertyChanged 成员
        protected void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion


    }
}
