using System;
using System.Data;
using System.Collections;
using System.Windows.Forms;
using System.ComponentModel;
using Core.Mes.Common;
using Core.Mes.IBaseInterface;
using System.Threading;
using System.Diagnostics;
using System.IO;

namespace Core.Mes.ServerFrameWork
{
    /// <summary>
    /// RemotingHelp 的摘要说明。
    /// </summary>
    public class RemotingServer
    {
        private double _priorityFactor = 1;
        public double PriorityFactor
        {
            get { return _priorityFactor; }
        }

        private string _externServerName = "";
        private ExtSrvDaemon.ShowExtSrvEvent ServiceStatusChanged = null;

        public string ExternServerName
        {
            get { return _externServerName; }
        }

        public RemotingServer(string _ServerName, Hashtable _MainServiceKeys)
        {
            _externServerName = _ServerName;
            MainServiceKeys = _MainServiceKeys;
        }

        public bool Enable = true;
        public RemoteServerStatus Valid = RemoteServerStatus.Normal;
        private Hashtable _htServiceUrlList = new Hashtable();
        private Hashtable _htServices = new Hashtable();
        public Hashtable HTServices
        {
            get { return _htServices; }
        }
        private Hashtable MainServiceKeys;

        public void CreateServiceInfo(string ServerName, string URL, string MD5_KEY, string SHA256_KEY)
        {
            if (!_htServiceUrlList.ContainsKey(ServerName))
            {
                _htServiceUrlList.Add(ServerName, URL);
            }
            ServiceObject so = null;
            if (!_htServices.ContainsKey(ServerName))
            {
                so = new ServiceObject();
                _htServices.Add(ServerName, so);
            }
            else
            {
                so = (ServiceObject)_htServices[ServerName];
            }

            so.ServerName = ServerName;
            so.MD5_KEY = MD5_KEY;
            so.SHA256_KEY = SHA256_KEY;

            if (so.instance == null)
            {
                so.instance = (ICommon)Activator.GetObject(typeof(ICommon), URL);
            }
            else if (so.URL != URL)
            {
                so.instance = (ICommon)Activator.GetObject(typeof(ICommon), URL);
            }
            so.URL = URL;
        }

        public void CompareMainService(string ServerName, string Main_MD5_KEY, string Main_SHA256_KEY)
        {
            ServiceObject so = null;
            if (_htServices.ContainsKey(ServerName))
            {
                so = (ServiceObject)_htServices[ServerName];
                if (Main_MD5_KEY == so.MD5_KEY && Main_SHA256_KEY == so.SHA256_KEY)
                {
                    so.Valid = ServiceObjectStatus.Normal;
                }
                else
                {
                    so.Valid = ServiceObjectStatus.UnMatch;
                }
            }
        }

        public void DeleteServiceInfo(string ServerName)
        {
            if (_htServiceUrlList.ContainsKey(ServerName)) _htServiceUrlList.Remove(ServerName);
            if (_htServices.ContainsKey(ServerName)) _htServices.Remove(ServerName);
        }

        public void DisableServiceInfo(string ServerName)
        {
            if (_htServices.ContainsKey(ServerName))
            {
                ServiceObject so = (ServiceObject)_htServices[ServerName];
                so.Valid = ServiceObjectStatus.Unable;
            }
        }

        public void SetChangeEvent(Core.Mes.ServerFrameWork.ExtSrvDaemon.ShowExtSrvEvent ssc_event)
        {
            ServiceStatusChanged += new ExtSrvDaemon.ShowExtSrvEvent(ssc_event);
        }

        public void SetEnableFlag(string ServerName, bool enable)
        {
            if (_htServices.ContainsKey(ServerName))
            {
                ServiceObject so = ((ServiceObject)_htServices[ServerName]);
                so.Enable = enable;
            }
        }

        public void CheckHeartBeat()
        {
            Thread t = new Thread(ListenHeartBeat);
            t.Start();
        }

        static bool Last_Status = false;
        static DateTime Last_UpdateTime = DateTime.Now;
        private void ListenHeartBeat()
        {
            do
            {
                try
                {
                    SimpleReturnObject outInfo = new SimpleReturnObject();
                    string MainService = ((ServiceObject)(MainServiceKeys["ServerCommon"])).URL + @"/ServerCommon";
                    ReturnObjectEx rtn = (ReturnObjectEx)ExecuteMethod("ServerCommon", "Core.Mes.ServerCommon.UserInfoManager", "ControlIt", new object[] { MainService }, out outInfo);
                    if (outInfo.ErrCode == 1314 && rtn.RealObject != null)
                    {
                        if (rtn.RealObject.ToString() == MainService)
                        {
                            if (!Last_Status || Last_UpdateTime.AddSeconds(30).CompareTo(DateTime.Now) <= 0)
                            {
                                LoadExternServices();
                                Last_UpdateTime = DateTime.Now;
                                if (ServiceStatusChanged != null)
                                {
                                    ServiceStatusChanged();
                                }
                            }

                            this.Valid = RemoteServerStatus.Normal;
                            Last_Status = true;
                        }
                        else
                        {
                            this.Valid = RemoteServerStatus.ControlByOthers;
                        }
                    }
                    else
                    {
                        if (Last_Status)
                        {
                            if (ServiceStatusChanged != null)
                            {
                                ServiceStatusChanged();
                            }
                        }
                        this.Valid = RemoteServerStatus.Unable;
                        Last_Status = false;
                    }
                }
                catch
                {
                    if (Last_Status)
                    {
                        if (ServiceStatusChanged != null)
                        {
                            ServiceStatusChanged();
                        }
                    }
                    this.Valid = RemoteServerStatus.Unable;
                    Last_Status = false;
                }
                Thread.Sleep(5000);
            } while (true);
        }

        public void SyncFiles()
        {
            try
            {
                ArrayList FileNames = new ArrayList();
                ArrayList FileBytes = new ArrayList();
                ArrayList FileMD5s = new ArrayList();
                ArrayList FileSHA256s = new ArrayList();

                foreach (string ServiceName in MainServiceKeys.Keys)
                {
                    ServiceObject mso = (ServiceObject)(MainServiceKeys[ServiceName]);
                    if (MainServiceKeys.ContainsKey(ServiceName))
                    {
                        if (_htServices.Contains(ServiceName))
                        {
                            ServiceObject so = (ServiceObject)(_htServices[ServiceName]);
                            if (so.MD5_KEY == mso.MD5_KEY && so.SHA256_KEY == mso.SHA256_KEY)
                            {
                                continue;
                            }
                        }
                        FileNames.Add(Path.GetFileName(mso.DLL_FileName));


                        FileStream fs = new FileStream(mso.DLL_FileName, FileMode.Open, FileAccess.Read);
                        byte[] file_content = null;
                        if (fs.Length > 0)
                        {
                            file_content = new byte[fs.Length];
                            fs.Read(file_content, 0, file_content.GetLength(0));
                        }
                        fs.Close();
                        FileBytes.Add(file_content);

                        FileMD5s.Add(mso.MD5_KEY);
                        FileSHA256s.Add(mso.SHA256_KEY);
                    }
                    else
                    {
                        throw new Exception(string.Format("无法获取主服务器的服务信息[{0}]", ServiceName));
                    }
                }

                if (FileNames.Count > 0)
                {
                    try
                    {
                        SimpleReturnObject outInfo = new SimpleReturnObject();
                        ReturnObjectEx rtn = (ReturnObjectEx)ExecuteMethod("ServerListener", "Core.Mes.ServerListener.ServerListener", "SyncFiles",
                            new object[] { FileNames, FileBytes, FileMD5s, FileSHA256s }, out outInfo);
                        if (outInfo.ErrCode == 0)
                        {
                            MessageBox.Show("同步成功!", "同步服务器");
                        }
                        else
                        {
                            MessageBox.Show("同步失败!\n " + outInfo.ErrMessage, "同步服务器", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        }
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show("同步失败!\n " + ex.Message, "同步服务器", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "同步错误!", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        public void LoadExternServices()
        {
            try
            {
                SimpleReturnObject outInfo = new SimpleReturnObject();
                ReturnObjectEx rtn = (ReturnObjectEx)ExecuteMethod("ServerCommon", "Core.Mes.ServerCommon.UserInfoManager", "GetServerList", null, out outInfo);
                if (outInfo.ErrCode != 0)
                {
                    throw new Exception("获取服务器服务列表失败:" + outInfo.ErrMessage);
                }
                DataSet ds = (DataSet)(UnBoxing(rtn, out outInfo));
                if (outInfo.ErrCode != 0)
                {
                    throw new Exception("获取服务器服务列表失败:" + outInfo.ErrMessage);
                }
                if (ds != null && ds.Tables.Contains("ServiceList"))
                {
                    foreach (DataRow dr in ds.Tables["ServiceList"].Rows)
                    {
                        string ServerName = dr["servername"].ToString();
                        CreateServiceInfo(ServerName, dr["URL"].ToString() + "/" + ServerName, dr["MD5_KEY"].ToString(), dr["SHA256_KEY"].ToString());
                        if (MainServiceKeys.ContainsKey(ServerName))
                        {
                            ServiceObject keys = (ServiceObject)(MainServiceKeys[ServerName]);
                            CompareMainService(ServerName, keys.MD5_KEY, keys.SHA256_KEY);
                        }
                        else
                        {
                            CompareMainService(ServerName, ".", ".");
                        }
                    }

                    ArrayList UnexistsSrv = new ArrayList();
                    foreach (string ServerName in _htServiceUrlList.Keys)
                    {
                        DataRow[] drs = ds.Tables[0].Select(string.Format("servername = '{0}'", ServerName));
                        if (drs == null || drs.GetLength(0) == 0)
                        {
                            UnexistsSrv.Add(ServerName);
                        }
                    }
                    if (UnexistsSrv.Count > 0)
                    {
                        foreach (string ServerName in UnexistsSrv)
                        {
                            DisableServiceInfo(ServerName);
                        }
                    }
                }

                if (ds != null && ds.Tables.Contains("CONFIG"))
                {
                    DataRow[] drs = ds.Tables["CONFIG"].Select("NAME='PriorityFactor'");
                    if (drs != null && drs.GetLength(0) > 0)
                    {
                        this._priorityFactor = (double)drs[0]["VALUE"];
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.Print(ex.Message);
            }
        }

        public ReturnObjectEx ExecuteMethod(CallingMessage par, out string errorInfo)
        {
            SimpleReturnObject outInfo = new SimpleReturnObject(0, "");
            CallingMessageEx parEx = new CallingMessageEx();
            parEx.FromCallingMessage(par);

            ReturnObjectEx obj = ExecuteMethod(parEx, out outInfo);
            errorInfo = outInfo.ErrMessage;
            return obj;
        }

        public ReturnObjectEx ExecuteMethod(string serverName, string className, string methodName, object[] args, out string errorInfo)
        {
            //==	解析参数
            CallingMessageEx parEx = new CallingMessageEx();
            parEx.ServerName = serverName;
            parEx.ClassName = className;
            parEx.MethodName = methodName;
            parEx.args = args;
            parEx.RedirectLimit = 0;

            parEx.ServerType = MesServerType.IComponentContainServer;
            parEx.TransType = NetWorkTransType.Remoting;
            parEx.visitType = VisitType.Method;

            SimpleReturnObject outInfo = new SimpleReturnObject(0, "");

            ReturnObjectEx obj = ExecuteMethod(parEx, out outInfo);
            errorInfo = outInfo.ErrMessage;
            return obj;
        }

        public ReturnObjectEx ExecuteMethod(string serverName, string className, string methodName, object[] args, out SimpleReturnObject outInfo)
        {
            //==	解析参数
            CallingMessageEx parEx = new CallingMessageEx();
            parEx.ServerName = serverName;
            parEx.ClassName = className;
            parEx.MethodName = methodName;
            parEx.args = args;
            parEx.RedirectLimit = 0;

            parEx.ServerType = MesServerType.IComponentContainServer;
            parEx.TransType = NetWorkTransType.Remoting;
            parEx.visitType = VisitType.Method;

            outInfo.ErrCode = 0;
            outInfo.ErrMessage = "";

            return ExecuteMethod(parEx, out outInfo);
        }

        public ReturnObjectEx ExecuteMethod(CallingMessage par, out SimpleReturnObject outInfo)
        {
            outInfo = new SimpleReturnObject();
            outInfo.ErrCode = 0;
            outInfo.ErrMessage = "";

            CallingMessageEx parEx = new CallingMessageEx();
            parEx.FromCallingMessage(par);
            return ExecuteMethod(parEx, out outInfo);
        }

        public ReturnObjectEx ExecuteMethod(CallingMessageEx parEx, out SimpleReturnObject outInfo)
        {
            outInfo = new SimpleReturnObject();
            outInfo.ErrCode = 0;
            outInfo.ErrMessage = "";

            if (!FindRemoteServer(parEx.ServerName))
            {
                outInfo.ErrCode = 10012;
                outInfo.ErrMessage = "未找到请求的服务！[3]";
                return new ReturnObjectEx(null, outInfo.ErrCode, outInfo.ErrMessage);
            }
            try
            {
                ICommon executor = ((ServiceObject)(_htServices[parEx.ServerName])).instance;
                do
                {
                    ReturnObjectEx rtnObj = executor.MethodHandlerEx(parEx, new ValidateInfo());

                    if (!string.IsNullOrEmpty(rtnObj.ReDirectURL) && parEx.RedirectLimit == 0)
                    {
                        outInfo.ErrCode = 1;
                        outInfo.ErrMessage = "重定位次数过多！";
                        return new ReturnObjectEx(null, outInfo.ErrCode, outInfo.ErrMessage);
                    }
                    else if (!string.IsNullOrEmpty(rtnObj.ReDirectURL))
                    {
                        parEx.RedirectLimit--;
                        try
                        {
                            executor = (ICommon)Activator.GetObject(typeof(ICommon), rtnObj.ReDirectURL);
                        }
                        catch
                        {
                            executor = ((ServiceObject)(_htServices[parEx.ServerName])).instance;
                            parEx.RedirectLimit = 0;
                        }
                    }
                    else
                    {
                        outInfo.ErrCode = rtnObj.ErrCode;
                        outInfo.ErrMessage = rtnObj.ErrMessage;

                        return rtnObj;
                    }
                } while (parEx.RedirectLimit >= 0);
                return new ReturnObjectEx(null, 1, "未能运行服务！");
            }
            catch (Exception ex)
            {
                outInfo.ErrCode = 1;
                outInfo.ErrMessage = ex.Message;
                return new ReturnObjectEx(null, outInfo.ErrCode, outInfo.ErrMessage);
            }
        }


        public bool FindRemoteServer(string serverName)
        {
            if (!_htServices.Contains(serverName))
                return GetServerUrl(serverName);
            return true;
        }
        private bool GetServerUrl(string serverName)
        {
            if (!_htServiceUrlList.Contains(serverName))
                return false;

            return GetServer(serverName);
        }
        private bool GetServer(string serverName)
        {
            try
            {
                if (!_htServices.Contains(serverName))
                {
                    CreateServiceInfo(serverName, _htServiceUrlList[serverName].ToString(), "", "");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return false;
            }
            return true;
        }

        public void InitServiceUrlList(string serverName, string url)
        {
            if (!_htServiceUrlList.ContainsKey(serverName))
                _htServiceUrlList.Add(serverName, url);
        }

        public object UnBoxing(ReturnObjectEx rtnObj, out SimpleReturnObject outInfo)
        {
            ReturnObject rtn = new ReturnObject();
            rtn.ErrCode = rtnObj.ErrCode;
            rtn.ErrMessage = rtnObj.ErrMessage;
            rtn.RealDataSet = rtnObj.RealDataSet;
            rtn.RealObject = rtnObj.RealObject;

            return UnBoxing(rtn, out outInfo);
        }

        public object UnBoxing(ReturnObject rtnObj, out SimpleReturnObject outInfo)
        {
            outInfo.ErrCode = 0;
            outInfo.ErrMessage = "";
            try
            {
                if (rtnObj.RealDataSet != null && rtnObj.RealDataSet.Tables.Count > 0 &&
                    rtnObj.RealDataSet.Tables.Contains("RETURN_RESULT"))
                {
                    DataTable dt_rus = rtnObj.RealDataSet.Tables["RETURN_RESULT"];
                    if (dt_rus.Rows.Count > 0)
                    {
                        int IsCompressed = (int)dt_rus.Rows[0]["IsCompressed"];
                        int UnCompress_size = (int)dt_rus.Rows[0]["UnCompress_size"];
                        int Compress_size = (int)dt_rus.Rows[0]["Compress_size"];

                        if (IsCompressed == 1)
                        {
                            byte[] _rtn_comp = (byte[])rtnObj.RealObject;
                            if (_rtn_comp.GetLength(0) != Compress_size)
                            {
                                outInfo.ErrCode = 1;
                                outInfo.ErrMessage = "接受的数据大小和服务端发送的数据大小不一致，请重试! [可能是网络原因造成]";
                                return null;
                            }
                            byte[] _rtn = Utility.Decompress(_rtn_comp);
                            if (_rtn.GetLength(0) != UnCompress_size)
                            {
                                outInfo.ErrCode = 1;
                                outInfo.ErrMessage = "接受的数据大小和服务端发送的数据大小不一致，请重试! [可能是网络原因造成]";
                                return null;
                            }
                            rtnObj.RealDataSet.Tables.Remove("RETURN_RESULT");
                            rtnObj.RealDataSet.AcceptChanges();

                            rtnObj.RealObject = Utility.ReSerializable(_rtn);
                        }
                    }
                }

                return rtnObj.RealObject;
            }
            catch (Exception ex)
            {
                outInfo.ErrCode = 1;
                outInfo.ErrMessage = ex.Message;
                return null;
            }
        }
    }

    public enum RemoteServerStatus { Normal = 1, Unable = 2, PauseServer = 3, ControlByOthers = 4, };
    public enum ServiceObjectStatus { Normal = 11, Unable = 12, PauseService = 13, UnMatch = 14, ServiceLost = 15 };
    public class ServiceObject
    {
        public string ServerName;
        public string URL;

        public string MD5_KEY;
        public string SHA256_KEY;

        public string Description;
        public string DLL_FileName;

        public bool Enable = true;
        public ServiceObjectStatus Valid = ServiceObjectStatus.Normal;

        public ICommon instance = null;
    }
}
