using SFI.COMM.BUSINESS;
using SFI.COMM.ENTITY;
using SFI.COMM.LIB;
using SFI.COMM.SESSION;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SFI.COMM.BASESYSTEM
{
    public static class CommandController
    {
        private static ObservableCollection<ChannelCommand> IncomingCommands = new ObservableCollection<ChannelCommand>();
        private static ObservableCollection<ChannelCommand> OutgoingCommands = new ObservableCollection<ChannelCommand>();


        private static List<long> CompletedCommands { get; set; } = new List<long>();
        private static Thread MainThread { get; set; }
        private static List<Thread> ProcessorThreadList { get; set; } = new List<Thread>();
        private static bool IsRun { get; set; }



        public delegate object CommandEventDelegate(ChannelCommand Command_);


        public static void Start()
        {
            IsRun = true;
            MainThread = new Thread(() => Worker());
            MainThread.Start();
        }
        public static void Stop()
        {
            IsRun = false;

            foreach (Thread th_ in ProcessorThreadList)
                try
                {
                    if ((th_?.IsAlive) ?? false)
                        th_.Abort();
                }
                catch (Exception exc) { }


            try
            {
                if ((MainThread?.IsAlive) ?? false)
                    MainThread.Abort();
            }
            catch (Exception exc) { }
        }









        public static Task<ObjectResult<T>> CommandCall<T>(string ClientKey_, CommandTypes CommandType_, object Data_ = null) where T : class
        {
            ChannelCommand newCommand_ = new ChannelCommand();
            newCommand_.CommandKey = DateTime.Now.Ticks;
            newCommand_.CommandType = (int)CommandType_;
            newCommand_.TargetName = ClientKey_;
            newCommand_.SourceName = CommunicationController.ClientKey;
            newCommand_.Data = Data_;

            OutgoingCommands.Add(newCommand_);
            CommunicationController.Publish(newCommand_);

            int counter_ = 0;
            while (newCommand_.IsCompleted == 0 && counter_ < (5 * 30))
            {
                Task.Delay(200).Wait();
                counter_++;
            }

            if (counter_ >= (5 * 30))
                return Task.Run(() => new ObjectResult<T>()
                {
                    Success = false,
                    Code = $"COMMANDTYPE=>{CommandType_}: CLIENTNAME=>{ClientKey_} :METHODNAME=>CommandController.Call",
                    Message = "Request timeout!"
                });

            if (newCommand_.HasError == 1)
                return Task.Run(() => new ObjectResult<T>()
                {
                    Success = false,
                    Code = $"COMMANDTYPE=>{CommandType_}: {newCommand_.ErrorCode}",
                    Message = newCommand_.ErrorMessage
                });


            return Task.Run(() => new ObjectResult<T>()
            {
                Success = true,
                Data = (T)newCommand_.ResultData
            });
        }
        public static Task<ObjectResult<T>> LongCommandCall<T>(string ClientKey_, CommandTypes CommandType_, string StatusKey_, object Data_ = null) where T : class
        {
            ChannelCommand newCommand_ = new ChannelCommand();
            newCommand_.StatusKey = StatusKey_;
            newCommand_.CommandKey = DateTime.Now.Ticks;
            newCommand_.CommandType = (int)CommandType_;
            newCommand_.TargetName = ClientKey_;
            newCommand_.SourceName = CommunicationController.ClientKey;
            newCommand_.Data = Data_;

            OutgoingCommands.Add(newCommand_);
            CommunicationController.Publish(newCommand_);


            while (newCommand_.IsCompleted == 0)
                Task.Delay(2000).Wait();


            if (newCommand_.HasError == 1)
                return Task.Run(() => new ObjectResult<T>()
                {
                    Success = false,
                    Code = $"COMMANDTYPE=>{CommandType_}: {newCommand_.ErrorCode}",
                    Message = newCommand_.ErrorMessage
                });


            return Task.Run(() => new ObjectResult<T>()
            {
                Success = true,
                Data = (T)newCommand_.ResultData
            });
        }




      







        public static void RunCommand(ChannelCommand Command_)
        {
            if (Command_.CommandType == (int)CommandTypes.StatusUpdate && CommunicationController.SystemType == SystemTypes.WebServer)
                WebServerController.CommandStatusSet(Command_.StatusKey, Command_.StatusValue);
            else if (Command_.IsRequest == 1)
                IncomingCommands.Add(Command_);
            else
            {
                ChannelCommand command_ = OutgoingCommands.Where(t => t.CommandKey == Command_.CommandKey).FirstOrDefault();
                if (command_ != null)
                {
                    command_.ResultData = Command_.Data;
                    command_.HasError = Command_.HasError;
                    command_.ErrorCode = Command_.ErrorCode;
                    command_.ErrorMessage = Command_.ErrorMessage;
                    command_.IsCompleted = 1;
                }
            }
        }
        private static void Worker()
        {
            while (IsRun)
                try
                {
                    ChannelCommand cc_ = IncomingCommands.Where(t => !CompletedCommands.Any(k => t.CommandKey == k)).OrderBy(t => t.CommandKey).FirstOrDefault();
                    if (cc_ == null)
                    {
                        Task.Delay(500).Wait();
                        continue;
                    }

                    while (IsRun && ProcessorThreadList.FindAll(t => !t.IsAlive).Count >= CommunicationController.MaxThreadCount)
                        Task.Delay(500).Wait();


                    Thread thread_ = new Thread(() =>
                    {
                        CompletedCommands.Add(cc_.CommandKey);
                        IncomingCommands.Remove(cc_);
                        Processor(cc_);
                    });
                    thread_.Start();
                    ProcessorThreadList.Add(thread_);
                }
                catch (Exception exc)
                {

                }
        }
        private static void Processor(ChannelCommand Command_)
        {
            try
            {
                string target_ = Command_.SourceName;
                string source_ = Command_.TargetName;
                bool hasError_ = false;
                string errorCode_ = string.Empty;
                string errorMessage_ = string.Empty;
                object result_ = null;


                try
                {
                    result_ = WindowsServiceController.RunCommand(Command_);
                }
                catch (Exception exc)
                {
                    hasError_ = true;
                    errorCode_ = $"CLIENTNAME=>{CommunicationController.ClientKey}";
                    errorMessage_ = exc.StackTrace;
                }


                ChannelCommand newCommand_ = new ChannelCommand();
                newCommand_.CommandKey = Command_.CommandKey;
                newCommand_.CommandType = Command_.CommandType;
                newCommand_.IsRequest = 0;
                newCommand_.TargetName = target_;
                newCommand_.SourceName = source_;
                newCommand_.Data = result_;
                newCommand_.HasError = hasError_ ? 1 : 0;
                newCommand_.ErrorCode = errorCode_;
                newCommand_.ErrorMessage = errorMessage_;

                CommunicationController.Publish(newCommand_);
            }
            catch (Exception exc)
            {
            }
        }



        public static CommandCategories CommandCategory(this CommandTypes CommandType_)
        {
            switch (CommandType_)
            {
                case CommandTypes.StatusUpdate:
                case CommandTypes.Do_SQL_Settings:
                case CommandTypes.Get_Dir_List:
                case CommandTypes.Get_SQL_List:
                    return CommandCategories.Standart;

                default:
                    return CommandCategories.Long;
            }
        }
    }
}
