using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Vinno.IUS.Common.Log;
using Vinno.IUS.Common.Network.Leaf;
using Vinno.IUS.Common.Network.Tcp;
using Vinno.IUS.Common.Network.Transfer;
using Vinno.IUS.Common.Utilities;
using Vinno.vCloud.Common.FIS.AfterSales;
using Vinno.vCloud.Common.FIS.FLYINSONOLogin;
using Vinno.vCloud.Common.FIS.LiveVideos;
using Vinno.vCloud.Common.FIS.Remedicals;
using Vinno.vCloud.Common.Storage;
using Vinno.vCloud.FIS.CrossPlatform.Common;
using Vinno.vCloud.FIS.CrossPlatform.Common.Enum;
using Vinno.vCloud.FIS.CrossPlatform.Common.LiveVideo;
using Vinno.vCloud.Protocol.Infrastructures;
using Vinno.vCloud.Protocol.Messages;
using Vinno.vCloud.Protocol.Messages.Client;
using Vinno.vCloud.Protocol.Messages.Client.AfterSales;
using Vinno.vCloud.Protocol.Messages.Client.AssignTermianl;
using Vinno.vCloud.Protocol.Messages.Client.AssignTerminal;
using Vinno.vCloud.Protocol.Messages.Client.Live;
using Vinno.vCloud.Protocol.Messages.Client.Storage;
using Vinno.vCloud.Protocol.Messages.Login;
using Vinno.vCloud.Protocol.Messages.Terminal;
using Vinno.vCloud.Protocol.Messages.Terminal.Login;

namespace Vinno.vCloud.Common.FIS
{
    internal class vCloudTerminal : IvCloudTerminal
    {
        private readonly Dictionary<TerminalFeatureType, object> _features = new Dictionary<TerminalFeatureType, object>();
        private readonly ConnectionInfo _connectionInfo;
        private readonly string _deviceName;
        private readonly string _password;
        private readonly bool _isUserDefined;
        private readonly LoginSource _loginSource;
        private readonly string _url;
        private readonly int _usScreenWidth;
        private readonly int _usScreenHeight;
        private readonly int _connectionCheckCycle;
        private readonly int _heartRateCycle;
        private readonly bool _isUseOldTerminalSdk;

        private string _clientId;
        private TerminalStatus _status;
        private ClientLeaf _leaf;
        private ConnectionChecker _connectionChecker;
        private HeartRateKeeper _heartRateKeeper;
        private bool _disposed;
        private string _deviceCode;
        private string _sessionId;
        private int _reconnectCounter;

        /// <summary>
        /// Gets the global remedical working folder.
        /// </summary>
        internal static string WorkingFolder { get; private set; }

        /// <summary>
        /// Raised when the status is changed.
        /// </summary>
        public event EventHandler StatusChanged;

        /// <summary>
        /// Gets the unique id.
        /// </summary>
        public string UniqueId { get; private set; }

        /// <inheritdoc />
        /// <summary>
        /// Gets the status of the Terminal
        /// </summary>
        /// <remarks>
        /// </remarks>
        public TerminalStatus Status
        {
            get => _status;
            private set
            {
                if (_status != value)
                {
                    _status = value;
                    CommonParameter.Instance.DeviceStatus = (EnumDeviceStatus)_status;
                    OnStatusChanged();
                }
            }
        }

        public string DeviceCode => _deviceCode;

        public string Url => _url;

        public vCloudTerminal(ConnectionInfo connectionInfo, bool isUsedOldSdk = false)
        {
            _isUseOldTerminalSdk = isUsedOldSdk;
            _usScreenHeight = connectionInfo.USScreenHeight;
            _usScreenWidth = connectionInfo.USScreenWidth;
            _connectionInfo = connectionInfo;
            _deviceName = connectionInfo.Account.Name;
            _password = connectionInfo.Account.Password;
            _isUserDefined = connectionInfo.Account.IsUserDefined;
            _url = connectionInfo.ServerUrl;
            _loginSource = CommonParameter.Instance.IsSonopost ? LoginSource.WindowSonopost : LoginSource.UltrasoundMachine;
            _clientId = IdHelper.Generate<string>();
            //Set the default value 10 seconds.
            _connectionCheckCycle = 10;
            //Set the default value 3 minutes.
            _heartRateCycle = 3;
            _status = TerminalStatus.Offline;
            switch (CrossPlatformHelper.Instance.Platform)
            {
                case EnumPlatform.Windows:
                    if (CommonParameter.Instance.IsSonopost)
                    {
                        WorkingFolder = connectionInfo.FISFolder;
                    }
                    else
                    {
                        if (Directory.Exists("D:"))
                        {
                            WorkingFolder = @"D:\FISRepository";
                        }
                        else
                        {
                            WorkingFolder = connectionInfo.FISFolder;
                        }
                    }
                    break;

                case EnumPlatform.Linux:
                    DirectoryHelper.CreateDirectory(@"/userdata");
                    if (Directory.Exists(@"/userdata"))
                    {
                        WorkingFolder = @"/userdata/FISRepository";
                    }
                    else
                    {
                        WorkingFolder = connectionInfo.FISFolder;
                    }
                    break;

                default:
                    WorkingFolder = connectionInfo.FISFolder;
                    break;
            }
            DirectoryHelper.CreateDirectory(WorkingFolder);
            if (!_isUseOldTerminalSdk)
            {
                CreateLeaf();
                CreateConnectionChecker();
                if (!connectionInfo.IsOnlyUsedToRegister)
                {
                    CPCombineSmartPushConfiguration.Instance.Initialize(connectionInfo.FISFolder);
                    UploadHelper.GetAuthorization = GetAuthorization;
                }
            }
        }

        /// <inheritdoc />
        /// <summary>
        /// Get the feature instance by feature enum.
        /// </summary>
        /// <typeparam name="T">The interface of the feature</typeparam>
        /// <param name="featureType">The feature type</param>
        /// <returns>The instance of the feature</returns>
        /// <remarks>
        /// If the feature doesn't exist, this method will return null.
        /// </remarks>
        public T GetFeature<T>(TerminalFeatureType featureType) where T : IFeature
        {
            if (_features.ContainsKey(featureType))
            {
                var feature = _features[featureType];
                if (feature is T t)
                {
                    return t;
                }
            }
            return default(T);
        }

        /// <summary>
        /// Connect to server.
        /// </summary>
        internal void Connect(string uniqueId = "", string terminalId = "")
        {
            try
            {
                if (!_isUseOldTerminalSdk)
                {
                    var loginResult = NewLogin();
                    if (!_isUserDefined && loginResult.Status == LoginStatus.WrongAccount)
                    {
                        if (string.IsNullOrEmpty(_connectionInfo.Organization))
                        {
                            Status = TerminalStatus.OrganizationMissing;
                            return;
                        }
                        if (RegisterAccount())
                        {
                            NewLogin();
                        }
                        else
                        {
                            Status = TerminalStatus.LoginFailed;
                        }
                    }
                }
                else
                {
                    UniqueId = uniqueId;
                    _deviceCode = terminalId;
                    ReleaseFeatures();
                    UpdateFeatures(_connectionInfo.EnabledFeatures);
                    Status = TerminalStatus.Logon;
                }
            }
            catch (Exception e)
            {
                Logger.WriteLineError($"Terminal {_deviceName} login url:{_url} failed {e}");
                Status = TerminalStatus.LoginFailed;
            }
        }

        public bool Register()
        {
            return RegisterAccount();
        }

        /// <summary>
        /// Register Account
        /// </summary>
        /// <returns></returns>
        private bool RegisterAccount()
        {
            using (var request = MessagePool.GetMessage<AddTerminalAccountRequest>())
            {
                request.TerminalName = _deviceName;
                request.TerminalModel = _connectionInfo.DeviceMode;
                request.TerminalPassword = _password;
                request.OrganizationName = _connectionInfo.Organization;
                request.IsLive = true;
                var addTerminalAccountServerResult = _leaf?.Send(request);
                var addTerminalAccountResult = ResultMessage.Convert(addTerminalAccountServerResult);
                if (addTerminalAccountResult == CCR.OK)
                {
                    Logger.WriteLineInfo($"VCloudTerminal Register Account Success,OrganizaitonName is {_connectionInfo?.Organization}");
                    return true;
                }
                else
                {
                    Logger.WriteLineError($"VCloudTerminal Register Account Fail,the result is not CCR.OK,OrganizaitonName is {_connectionInfo?.Organization}");
                    return false;
                }
            }
        }

        public void Disconnect()
        {
            if (!_isUseOldTerminalSdk)
            {
                var status = TerminalStatus.Logoff;
                try
                {
                    SendStopPushStreamResult();
                    using (var request = MessagePool.GetMessage<NewTerminalLogoffRequest>())
                    {
                        request.Name = _deviceName;
                        request.Password = _password;
                        request.Source = _loginSource;
                        request.IsUserDefinedAccount = _isUserDefined;
                        var result = _leaf?.Send(request);
                        var logoffResult = LogoffResult.Convert(result);
                        switch (logoffResult.Status)
                        {
                            case LogoffStatus.Success:
                                status = TerminalStatus.Logoff;
                                break;

                            case LogoffStatus.WrongAccount:
                                status = TerminalStatus.WrongAccount;
                                break;

                            case LogoffStatus.WrongPassword:
                                status = TerminalStatus.WrongPassword;
                                break;
                        }
                    }
                }
                catch (Exception e)
                {
                    Logger.WriteLineError($"Disconnect terminal {_deviceName} error {e}");
                }
                finally
                {
                    Release();
                    FLYINSONOUserManager.Instance.Clear();
                    Status = status;
                }
            }
            else
            {
                try
                {
                    Release();
                    FLYINSONOUserManager.Instance.Clear();
                    Status = TerminalStatus.Logoff;
                }
                catch (Exception e)
                {
                    Logger.WriteLineError($"Disconnect terminal with old Sdk {_deviceName} error {e}");
                }
            }
        }

        public IEnumerable<string> GetOrganizations(string keyWord)
        {
            using (var request = MessagePool.GetMessage<FindOrganizationByKeyWordRequest>())
            {
                request.KeyWord = keyWord;
                var result = _leaf?.Send(request);
                var findOrganizationInfosResult = FindOrganizationInfosResult.Convert(result);
                if (findOrganizationInfosResult?.OrganizationInfos != null)
                {
                    return findOrganizationInfosResult.OrganizationInfos.Select(i => i.Name);
                }
            }

            return new List<string>();
        }

        public bool IsTerminalOrganizationMissging()
        {
            return Status == TerminalStatus.OrganizationMissing;
        }

        public bool UpdatelTerminalOrganizationName(string organizationName, bool createNewOrganization)
        {
            using (var request = MessagePool.GetMessage<UpdateTerminalOrganizationNameRequest>())
            {
                request.TerminalName = _deviceName;
                request.OrganizationName = organizationName;
                request.IsCreateNewOrganization = createNewOrganization;
                var result = _leaf?.Send(request);
                var updateTerminalResult = ResultMessage.Convert(result);
                if (updateTerminalResult == CCR.OK)
                {
                    Logger.WriteLineInfo($"vCloudTerminal UpdatelTerminalOrganizationName Success,TerminalName:{_deviceName},OrganizationName:{organizationName},CreateNewOrganization:{createNewOrganization}");
                    return true;
                }
                else
                {
                    Logger.WriteLineError($"vCloudTerminal UpdatelTerminalOrganizationName Failed,TerminalName:{_deviceName},OrganizationName:{organizationName},CreateNewOrganization:{createNewOrganization}");
                    return false; //update organization name fail
                }
            }
        }

        public string GetOrganizationName()
        {
            var organizationName = string.Empty;
            try
            {
                using (var request = MessagePool.GetMessage<FindTerminalByNameRequest>())
                {
                    request.TerminalName = _deviceName;
                    var result = _leaf?.Send(request);
                    if (result != null)
                    {
                        var findTerminalByNameResult = FindTerminalByNameResult.Convert(result);
                        if (findTerminalByNameResult != null)
                        {
                            organizationName = findTerminalByNameResult.Termianl?.OrganizationName;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.WriteLineError($"Vcloud terminal get organization name by terminal   failed:{ex}");
            }
            return organizationName;
        }

        /// <summary>
        /// Update enabled feature types.
        /// </summary>
        /// <param name="enabledFeatureTypes">The enabled feature types.</param>
        public void UpdateFeatures(IEnumerable<TerminalFeatureType> enabledFeatureTypes)
        {
            CommonParameter.Instance.IsFeatureReleased = false;
            if (!_isUseOldTerminalSdk)
            {
                var removedFeatures = _features.Keys.Where(f => !enabledFeatureTypes.Contains(f)).ToList();
                foreach (var feature in removedFeatures)
                {
                    var disposable = _features[feature] as IDisposable;
                    disposable?.Dispose();
                    _features.Remove(feature);
                }

                foreach (var enabledFeature in enabledFeatureTypes)
                {
                    if (!_features.ContainsKey(enabledFeature))
                    {
                        var feature = GetFeature(enabledFeature);
                        _features.Add(enabledFeature, feature);
                    }
                }
            }
            else
            {
                var removedFeatures = _features.Keys.Where(f => !enabledFeatureTypes.Contains(f)).ToList();
                foreach (var feature in removedFeatures)
                {
                    if (feature == TerminalFeatureType.Consultation)
                    {
                        var disposable = _features[feature] as IDisposable;
                        disposable?.Dispose();
                        _features.Remove(feature);
                    }
                }

                foreach (var enabledFeature in enabledFeatureTypes)
                {
                    if (enabledFeature == TerminalFeatureType.Consultation)
                    {
                        if (!_features.ContainsKey(enabledFeature))
                        {
                            var feature = GetFeature(enabledFeature);
                            _features.Add(enabledFeature, feature);
                        }
                    }
                }
            }
        }

        public void Dispose()
        {
            if (!_disposed)
            {
                Release();
                _disposed = true;
            }
            GC.SuppressFinalize(this);
        }

        private void SendStopPushStreamResult()
        {
            if (!string.IsNullOrWhiteSpace(_deviceCode))
            {
                using (var request = MessagePool.GetMessage<StopPushStreamResult>())
                {
                    request.TerminalId = _deviceCode;
                    var result = _leaf?.Send(request);
                    var ok = ResultMessage.Convert(result);
                    if (ok == null || ok.ResultCode != 0)
                        throw new InvalidOperationException("Send reply to server error.");
                }
            }
        }

        private string GetAuthorization(string fileName)
        {
            try
            {
                var authorization = string.Empty;
                var getAuthorizationRequest = new GetAuthorizationRequest();
                getAuthorizationRequest.FileName = fileName;
                var storageResult = _leaf?.Send(getAuthorizationRequest);
                var getAuthorizationResult = GetAuthorizationResult.Convert(storageResult);
                if (getAuthorizationResult != null)
                {
                    authorization = getAuthorizationResult.Authorization;
                }
                return authorization;
            }
            catch (Exception e)
            {
                throw new Exception($"get authentication form server error:{e}");
            }
        }

        private NewTerminalLoginResult4 NewLogin()
        {
            using (var request = MessagePool.GetMessage<NewTerminalLoginRequest4>())
            {
                request.Name = _deviceName;
                request.Password = _password;
                request.SupportLive = true;
                request.ClientId = _clientId;
                request.IsUserDefinedAccount = _isUserDefined;
                request.Source = _loginSource;
                request.TerminalVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
                var result = _leaf?.Send(request);
                var loginResult = NewTerminalLoginResult4.Convert(result);
                if (loginResult != null)
                {
                    UniqueId = loginResult.TerminalUniqueId;
                    ////TODO(FISJuly)
                    //StorageType = loginResult.StorageType;
                    HandleLoginResult(loginResult.Status, loginResult.AccountId, loginResult.AccountSessionId);
                }
                else
                {
                    throw new InvalidOperationException("Login failed.");
                }

                return loginResult;
            }
        }

        private void HandleLoginResult(LoginStatus loginStatus, string accountId, string accountSessionId)
        {
            if (loginStatus == LoginStatus.Success)
            {
                Status = TerminalStatus.Logoning;
                _sessionId = accountSessionId;
                _deviceCode = accountId;
                CreateHeartRateKeeper();
                ReleaseFeatures();
                UpdateFeatures(_connectionInfo.EnabledFeatures);
                UpdateTerminalInfo();
                _reconnectCounter = 0;
                Status = TerminalStatus.Logon;
                Logger.WriteLineInfo($"{_deviceName} Login url:{_url} successed, UniqueId:{UniqueId}");
            }
            else if (loginStatus == LoginStatus.WrongPassword)
            {
                Status = TerminalStatus.WrongPassword;
            }
            else if (_isUserDefined && loginStatus == LoginStatus.WrongAccount)
            {
                Status = TerminalStatus.WrongAccount;
            }
            else
            {
                Status = TerminalStatus.LoginFailed;
                Logger.WriteLineWarn($"Login to server error:{loginStatus}");
            }
        }

        private void UpdateTerminalInfo()
        {
            try
            {
                Logger.WriteLineInfo($"begin ChangeTerminalDeviceInfoRequest SerialNumber:{_connectionInfo.SerialNumber} ,USMode:{_connectionInfo.DeviceMode}, USCPU:{_connectionInfo.USCPU}, USOS:{_connectionInfo.USOS}, USSoftware:{_connectionInfo.SoftwareVersion}, ");
                if (CommonParameter.Instance.IsSonopost)
                {
                    using (var request = MessagePool.GetMessage<ChangeTerminalDeviceInfoRequest1>())
                    {
                        request.TerminalId = _deviceCode;
                        request.SerialNumber = _connectionInfo.SerialNumber;
                        request.TerminalMode = _connectionInfo.DeviceMode;
                        request.TerminalCPU = _connectionInfo.USCPU;
                        request.TerminalOS = "";
                        request.SoftwareVersion = _connectionInfo.SoftwareVersion;
                        request.IsSonopost = true;
                        request.Capability = _deviceName;
                        request.SonopostVerson = SonopostVersonEnum.Sonopost2;
                        var result = _leaf?.Send(request);
                        var changeTerminalDeviceInfoResult = ChangeTerminalDeviceInfoResult.Convert(result);
                        if (changeTerminalDeviceInfoResult != null && changeTerminalDeviceInfoResult.IsSuccess)
                        {
                            Logger.WriteLineInfo($"ChangeTerminalDeviceInfoRequest success");
                        }
                        else
                        {
                            Logger.WriteLineError($"ChangeTerminalDeviceInfoRequest failed: {changeTerminalDeviceInfoResult?.Msg}");
                        }
                    }
                }
                else
                {
                    using (var request = MessagePool.GetMessage<ChangeTerminalDeviceInfoRequest>())
                    {
                        request.TerminalId = _deviceCode;
                        request.SerialNumber = _connectionInfo.SerialNumber;
                        request.TerminalMode = _connectionInfo.DeviceMode;
                        request.TerminalCPU = _connectionInfo.USCPU;
                        request.TerminalOS = _connectionInfo.USOS;
                        request.SoftwareVersion = _connectionInfo.SoftwareVersion;
                        request.IsSonopost = false;
                        request.Capability = _deviceName;
                        var result = _leaf?.Send(request);
                        var changeTerminalDeviceInfoResult = ChangeTerminalDeviceInfoResult.Convert(result);
                        if (changeTerminalDeviceInfoResult != null && changeTerminalDeviceInfoResult.IsSuccess)
                        {
                            Logger.WriteLineInfo($"ChangeTerminalDeviceInfoRequest success");
                        }
                        else
                        {
                            Logger.WriteLineError($"ChangeTerminalDeviceInfoRequest failed: {changeTerminalDeviceInfoResult?.Msg}");
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.WriteLineError($"ChangeTerminalDeviceInfoRequest error: {ex}");
            }
        }

        private void OnStatusChanged()
        {
            StatusChanged?.Invoke(this, EventArgs.Empty);
        }

        ////TODO(July):支里是否是超声机已在其他机器登录的原因,代码得看看
        private void CreateLeaf()
        {
            _leaf = new ClientLeaf(new LeafIdContext(), LeafMode.Dual, new TcpCreator(_url), "Terminal connection:");
            _leaf.MessageArrived += OnMessageArrived;
            if (!_leaf.Online)
            {
                _leaf.Close();
                Status = TerminalStatus.Offline;
            }
            else
            {
                _leaf.RegisterSetAccountDataMessageFunc(SetAccountDataToMessage);
                Status = TerminalStatus.Online;
            }
        }

        private void DisposeLeaf()
        {
            try
            {
                if (_leaf != null)
                {
                    _leaf.MessageArrived -= OnMessageArrived;
                    _leaf.Close();
                    _leaf = null;
                }
            }
            catch (Exception ex)
            {
                Logger.WriteLineError($"VCloudTerminal DisposeLeaf Error:{ex}");
            }
        }

        private void CreateHeartRateKeeper()
        {
            if (_heartRateKeeper == null)
            {
                _heartRateKeeper = new HeartRateKeeper(_sessionId, _leaf, _heartRateCycle);
                _heartRateKeeper.Start();
            }
        }

        private void DisposeHeartRateKeeper()
        {
            try
            {
                if (_heartRateKeeper != null)
                {
                    _heartRateKeeper.Stop();
                    _heartRateKeeper = null;
                }
            }
            catch (Exception ex)
            {
                Logger.WriteLineError($"VCloudTerminal DisposeHeartRateKeeper Error:{ex}");
            }
        }

        private void CreateConnectionChecker()
        {
            if (_connectionChecker == null)
            {
                _connectionChecker = new ConnectionChecker(_leaf, _connectionCheckCycle);
                _connectionChecker.Offlined += ConnectionCheckerOnOfflined;
                _connectionChecker.Start();
            }
        }

        private void DisposeConnectionChecker()
        {
            try
            {
                if (_connectionChecker != null)
                {
                    _connectionChecker.Offlined -= ConnectionCheckerOnOfflined;
                    _connectionChecker.Stop();
                    _connectionChecker = null;
                }
            }
            catch (Exception ex)
            {
                Logger.WriteLineError($"VCloudTerminal DisposeConnectionChecker Error:{ex}");
            }
        }

        private void OnMessageArrived(object sender, Message e)
        {
            var connectionReplacedNotification = ConnectionReplacedNotification.Convert(e);
            if (connectionReplacedNotification != null)
            {
                HandleConnectionReplacedNotification(connectionReplacedNotification);
            }
            var connectNotification = ConnectNotification.Convert(e);
            if (connectNotification != null)
            {
                HandleConnectNotification(connectNotification);
            }
        }

        private void HandleConnectionReplacedNotification(ConnectionReplacedNotification connectionReplacedNotification)
        {
            Dispose();
            Status = TerminalStatus.ReplacedNotification;
        }

        private void HandleConnectNotification(ConnectNotification connectNotification)
        {
            try
            {
                var afterSales = GetFeature<IAfterSales>(TerminalFeatureType.AfterSales);
                if (afterSales != null)
                {
                    afterSales.UpdateAfterSalesInfo(connectNotification.UserId, connectNotification.UserName);
                    using (var processReslut = MessagePool.GetMessage<ProcessReslut>())
                    {
                        processReslut.TargetId = connectNotification.UserId;
                        processReslut.RunTaskType = TaskType.Connect;
                        processReslut.RunTaskStatus = TaskStatus.Finished;
                        processReslut.Detail = DetailType.Connected;
                        processReslut.Percent = 1;
                        processReslut.Source = ProcessSource.FromTerminal;
                        processReslut.FileToken = string.Empty;
                        _leaf?.Send(processReslut);
                    }
                }
                else
                {
                    using (var processReslut = MessagePool.GetMessage<ProcessReslut>())
                    {
                        processReslut.TargetId = connectNotification.UserId;
                        processReslut.RunTaskType = TaskType.Connect;
                        processReslut.RunTaskStatus = TaskStatus.Finished;
                        processReslut.Detail = DetailType.ConnectedDeny;
                        processReslut.Percent = 0;
                        processReslut.Source = ProcessSource.FromTerminal;
                        processReslut.FileToken = string.Empty;
                        _leaf?.Send(processReslut);
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.WriteLineError($"HandleConnectNotification error:{ex}");
            }
        }

        private Message SetAccountDataToMessage(Message message)
        {
            if (message is ClientRequestMessage clientRequestMessage)
            {
                clientRequestMessage.AccountData = GetAccountDataMessage() as ClientAccountMessage;
                return clientRequestMessage;
            }
            return message;
        }

        private Message GetAccountDataMessage()
        {
            var accountData = MessagePool.GetMessage<ClientAccountMessage>();
            accountData.AccountId = _deviceCode ?? string.Empty;
            accountData.AccountName = _deviceName ?? string.Empty;
            accountData.SessionId = _sessionId ?? string.Empty;
            accountData.Source = _loginSource;
            return accountData;
        }

        private void ConnectionCheckerOnOfflined(object sender, EventArgs e)
        {
            Release();
            Status = TerminalStatus.Offline;

            //No need to reconnect after instance release
            if (_disposed) return;
            //Reconnect
            if (_reconnectCounter < 5)
            {
                if (!string.IsNullOrEmpty(_deviceName) && !string.IsNullOrEmpty(_password))
                {
                    CreateLeaf();
                    CreateConnectionChecker();
                    if (Status == TerminalStatus.Online)
                    {
                        Status = TerminalStatus.Reconnecting;
                        Connect();
                        _reconnectCounter++;
                    }
                }
            }
            else
            {
                Dispose();
            }
        }

        private void Release()
        {
            _deviceCode = string.Empty;
            DisposeConnectionChecker();
            DisposeHeartRateKeeper();
            ReleaseFeatures();
            DisposeLeaf();
        }

        private void ReleaseFeatures()
        {
            CommonParameter.Instance.IsFeatureReleased = true;
            foreach (var type in _features.Keys.ToList())
            {
                var feature = _features[type];
                var disposable = feature as IDisposable;
                disposable?.Dispose();
                _features.Remove(type);
            }
        }

        private IFeature GetFeature(TerminalFeatureType terminalFeatureType)
        {
            switch (terminalFeatureType)
            {
                case TerminalFeatureType.Remedical:
                    return new Remedical(_deviceCode, _leaf);

                case TerminalFeatureType.LiveVideo:
                    if (CommonParameter.Instance.IsSonopost)
                    {
                        return new LiveVideoForSonopost(_deviceCode, _deviceName, _leaf);
                    }
                    else
                    {
                        return new LiveVideo(_deviceCode, _deviceName, _leaf, _usScreenWidth, _usScreenHeight);
                    }

                case TerminalFeatureType.AfterSales:
                    return new AfterSales.AfterSales(_deviceCode, _leaf);

                case TerminalFeatureType.Upgrade:
                    return new Upgraders.Upgraders(_leaf);

                case TerminalFeatureType.Teaching:
                    return new Teaching.Teaching(_deviceCode, _leaf);

                case TerminalFeatureType.Consultation:
                    return new Consultation.Consultation(_deviceCode, _deviceName, _url, LoginSource.PersonalComputer, UniqueId);

                case TerminalFeatureType.Log:
                    return new Log.Log(_deviceCode, _leaf);

                default:
                    return null;
            }
        }
    }
}