using Edge.Core.Processor;
using Edge.Core.Processor.Dispatcher.Attributes;
using Edge.Core.UniversalApi;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace AreaIntrudeDetecterServer
{
    [MetaPartsDescriptor(
        "lang-zh-cn:区域闯入检测lang-zh-tw:區域闖入檢測lang-en-us:Area intrude detecter",
        "lang-zh-cn:接收某区域的闯入行为,此程序作为服务器(接收端)需要与检测客户端配合使用(如基于视频流分析的jetson inference framework)" +
        "lang-en-us:接收某区域的闯入行为,此程序作为服务器(接收端)需要与检测客户端配合使用(如基于视频流分析的jetson inference framework)",
        new[] { "lang-zh-cn:摄像头lang-en-us:Cameralang-zh-tw:攝像頭" })]
    public class App : IAppProcessor
    {
        private IServiceProvider services;
        public string MetaConfigName { get; set; }

        public class Area
        {
            public string Name { get; set; }
            public int BndBox_LeftTop_X { get; set; }
            public int BndBox_RightBottom_X { get; set; }
            public int BndBox_LeftTop_Y { get; set; }
            public int BndBox_RightBottom_Y { get; set; }
        }

        /// <summary>
        /// basically info can refer https://rawgit.com/dusty-nv/jetson-inference/python/docs/html/python/jetson.inference.html#detectNet
        /// </summary>
        public class Detection
        {
            public string AreaName { get; set; }

            public int? ClassID { get; set; }
            public string ClassName { get; set; }

            public double Confidence { get; set; }

            public int? Height { get; set; }

            public int? Width { get; set; }
        }

        private Timer timelyFireGenericAlarmTimer;
        private int busy = 0;
        private List<Detection> cache = new List<Detection>();
        public App(int appConfig, IServiceProvider services)
        {
            this.services = services;

            this.timelyFireGenericAlarmTimer = new Timer(async (o) =>
            {
                if (0 != Interlocked.CompareExchange(ref this.busy, 1, 0))
                    return;
                try
                {
                    var groupByAreaName = cache.GroupBy(d => new { d.AreaName, d.ClassName });
                    var universalApiHub = this.services.GetRequiredService<UniversalApiHub>();
                    foreach (var g in groupByAreaName)
                    {
                        foreach (var ig in g)
                        {
                            await universalApiHub.ClearAndFirePersistGenericAlarm(this,
                                new GenericAlarm()
                                {
                                    Category = $"区域闯入检测",
                                    Title = $"{this.GetFriendlyAreaName(g.Key.AreaName)} - {this.GetFriendlyClassName(ig.ClassName)}闯入",
                                    Detail = $"系统有{(Math.Round(ig.Confidence, 2) * 100).ToString().Substring(0, 2)}%的确信度认为有{ig.ClassName} (ClassId: {ig.ClassID})闯入至区域: {this.GetFriendlyAreaName(g.Key.AreaName)}",
                                    Severity = GenericAlarmSeverity.Warning
                                }, g => g.Title, g => g.Title);
                        }
                    }

                    this.cache.Clear();
                }
                finally
                {
                    this.busy = 0;
                }
            }, null, 0, 1500);
        }

        int maxWaitTimes = 20;

        [UniversalApi]
        public async Task<bool> Report(IEnumerable<Detection> input)
        {
            Console.WriteLine(input.Select(d => $"Detect in area: {d.AreaName} has object: {d.ClassName} with conf: {d.Confidence}").Aggregate((acc, n) => acc + " | " + n));
            int waitTimes = 0;
            while (0 != Interlocked.CompareExchange(ref this.busy, 1, 0))
            {
                Thread.Sleep(50);
                waitTimes++;
                if (waitTimes > maxWaitTimes)
                    return false;
            }
            try
            {
                this.cache.AddRange(input);
            }
            finally { this.busy = 0; }

            return true;
        }

        private string GetFriendlyAreaName(string rawName)
        {
            switch (rawName.ToLower())
            {
                case "indoor0": return "消防通道A";
                case "indoor1": return "消防通道B";
                case "indoor2": return "消防通道C";
                case "indoor3": return "消防通道D";
                case "indoor4": return "消防通道E";
                case "outdoor0": return "卸油区通道A";
                case "outdoor1": return "卸油区通道B";
                case "outdoor2": return "油罐区通道A";
                case "outdoor3": return "油罐区通道B";
                case "outdoor4": return "油罐区通道C";
                default: return rawName;
            }
        }

        private string GetFriendlyClassName(string rawName)
        {
            switch (rawName.ToLower())
            {
                case "person": return "人员";
                case "chair": return "座椅";
                case "bird": return "鸟类";
                case "truck": return "卡车";
                case "car": return "小汽车";
                default: return rawName;
            }
        }

        private List<Area> savedAreas = new List<Area>();
        [UniversalApi]
        public async Task<bool> SaveAreas(IEnumerable<Area> input)
        {
            this.savedAreas.AddRange(input);
            return true;
        }

        [UniversalApi]
        public async Task<IEnumerable<Area>> GetAreas()
        {
            return this.savedAreas;
        }

        [UniversalApi]
        public async Task<bool> ClearAreas()
        {
            this.savedAreas.Clear();
            return true;
        }

        public void Init(IEnumerable<IProcessor> processors)
        {
            //throw new NotImplementedException();
        }
    }
}