using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Common;
using Dao;
using Domain;
using RCUHost.Protocols;

namespace RCUHost.Implement
{
    public class ServiceReceiver : GenericReceiverBase, IServiceReceiver
    {
        private static log4net.ILog logger = log4net.LogManager.GetLogger(typeof(ServiceReceiver));
        private ConcurrentDictionary<string, ConcurrentDictionary<int, ServiceStatus>> roomsServices = new ConcurrentDictionary<string, ConcurrentDictionary<int, ServiceStatus>>();
        public IHostRepository HostRepository { get; set; }
        public IAlarmSettingRepository AlarmSettingRepository { get; set; }
        public IRoomServiceRepository RoomServiceRepository { get; set; }
        public IRoomServiceRecordRepository RoomServiceRecordRepository { get; set; }

        public void SetService(Host host, int serviceCode, int status)
        {
            byte[] data = CreateServiceControlDataPacket(serviceCode, status);
            SendAndPushCommandQueue(data, host.HostNumber, host.MAC);// host.IP, host.Port);
        }

        public override void Process(ReceiverContext context)
        {
            bool bkb = GenericReceiverBase.DealWwith(context);
            if (bkb == false)
            {
                return;
            }
            Reply(context);
            Host host = HostRepository.GetByHostNumber(context.SystemHeader.Value.HostNumber.ToString());//.RemoteEndPoint.Address.ToString());
            if (host != null)
            {
                try
                {
                    int offset = StructConverter.SizeOf(context.SystemHeader);
                    int length = context.Data.Length - offset - 2;
                    using (MemoryStream stream = new MemoryStream(context.Data, offset, length))
                    {
                        var services = DecodeServices(host.IP, stream);
                        foreach (var service in services)
                        {
                            if (service.Value.Status != 2)
                            {
                                //处理服务
                                ProcessServices(host, service.Value);
                                //设置Host表的保险箱状态
                                if (service.Value.Code == 12)
                                {
                                    HostRepository.SetSafeBoxStatus(host, service.Value.Status);
                                }
                                //处理异常
                                ProcessAbnormities(host, service.Value);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    if (logger.IsErrorEnabled)
                    {
                        logger.Error(context.RemoteEndPoint.Address.ToString() + ":" + context.RemoteEndPoint.Port.ToString() + ":" + Tools.ByteToString(context.Data));
                        logger.Error("解析【" + host.HostNumber + "】客房服务数据出错。", ex);
                    }
                }
            }
        }

        /// <summary>
        /// 处理服务
        /// </summary>
        /// <param name="host"></param>
        /// <param name="status"></param>
        private void ProcessServices(Host host, ServiceStatus status)
        {
            var serviceSetting = AlarmSettingRepository.Get(host.SysHotel.ID, 'B', status.Code);
            if (serviceSetting != null && status.Status != 2)
            {
                //更新客房服务状态
                var roomService = RoomServiceRepository.Get(host.ID, serviceSetting.Code);
                if (roomService != null && roomService.Status != Convert.ToBoolean(status.Status))
                {
                    roomService.Status = Convert.ToBoolean(status.Status);
                    roomService.StartTime = DateTime.Now;
                    RoomServiceRepository.Update(roomService);
                }

                if (status.Status == 0)
                {
                    //更新清除服务记录状态和时间
                    var serviceRecord = RoomServiceRecordRepository.Get(host.ID, serviceSetting.Code, true);
                    if (serviceRecord != null)
                    {
                        serviceRecord.Status = false;
                        serviceRecord.EndTime = DateTime.Now;
                        RoomServiceRecordRepository.Update(serviceRecord);
                    }
                }
                else if (status.Status == 1)
                {
                    //判断数据库中是否存在服务开启记录，不存在才去记录服务
                    var existServiceRecord = RoomServiceRecordRepository.Get(host.ID, serviceSetting.Code, true);
                    if (existServiceRecord == null)
                    {
                        var serviceRecord = new RoomServiceRecord();
                        serviceRecord.HostID = host.ID;
                        serviceRecord.RoomNumber = host.RoomNumber;
                        serviceRecord.AlarmCode = serviceSetting.Code;
                        serviceRecord.Name = serviceSetting.Name;
                        serviceRecord.Status = true;
                        serviceRecord.StartTime = DateTime.Now;
                        RoomServiceRecordRepository.Save(serviceRecord);
                    }
                }
                //给FreeGo（福瑞狗）上报服务状态
                //FreeGoOperation.UploadService(host.SysHotel.Code, host.RoomNumber, serviceSetting.Name, status.Status.ToString());
            }
        }
        /// <summary>
        /// 处理异常
        /// </summary>
        /// <param name="host"></param>
        /// <param name="status"></param>
        private void ProcessAbnormities(Host host, ServiceStatus status)
        {
            #region 处理“非客人在保险箱开”异常

            var abnormitySetting = AlarmSettingRepository.Get(host.SysHotel.ID, 'C', "C03", status.Code);
            if (abnormitySetting != null)
            {
                var abnormity = RoomServiceRepository.Get(host.ID, abnormitySetting.Code);
                bool isUpdated = false;
                if (status.Status == 1)
                {
                    //保险箱开 如果非客人卡且出租房态，则报警
                    if (host.RoomCard != null && host.RoomCard.RoomCardType != null && host.RoomCard.RoomCardType.ID != 0x20 &&
                        host.RoomStatus != null && host.RoomStatus.ID == 0x02)
                    {
                        isUpdated = true;
                        if (abnormity != null && !abnormity.Status)
                        {
                            abnormity.Status = true;
                            abnormity.StartTime = DateTime.Now;
                            RoomServiceRepository.Update(abnormity);
                            //插入保险箱开的记录
                            var abnormityRecord = RoomServiceRecordRepository.Get(host.ID, abnormitySetting.Code, true);
                            if (abnormityRecord == null)
                            {
                                abnormityRecord = RoomServiceRecordRepository.CreateServiceRecord(host, abnormitySetting);
                                RoomServiceRecordRepository.Save(abnormityRecord);
                            }
                        }
                    }
                }
                //保险箱关或未接保险箱（关掉报警）
                if (!isUpdated)
                {
                    if (abnormity != null && abnormity.Status)
                    {
                        abnormity.Status = false;//取消报警
                        abnormity.StartTime = DateTime.Now;
                        RoomServiceRepository.Update(abnormity);
                        //更新保险箱关的记录
                        var abnormityRecord = RoomServiceRecordRepository.Get(host.ID, abnormitySetting.Code, true);
                        if (abnormityRecord != null)
                        {
                            abnormityRecord.Status = false;
                            abnormityRecord.EndTime = DateTime.Now;
                            RoomServiceRecordRepository.Update(abnormityRecord);
                        }
                    }
                }
            }

            #endregion

            #region 处理“退房保险箱关”异常

            abnormitySetting = AlarmSettingRepository.Get(host.SysHotel.ID, 'C', "C04", status.Code);
            var abnormity1 = RoomServiceRepository.Get(host.ID, "C04");
            if (abnormitySetting != null && abnormity1 != null)
            {
                bool isUpdated = false;
                if (status.Status == 0)
                {
                    //保险箱关 如果是“退房”状态，则报警				
                    if (host.RoomStatus != null && host.RoomStatus.ID == 0x08)
                    {
                        isUpdated = true;
                        if (!abnormity1.Status)
                        {
                            abnormity1.Status = true;
                            abnormity1.StartTime = DateTime.Now;
                            RoomServiceRepository.Update(abnormity1);
                            //异常记录
                            var abnormityRecord = RoomServiceRecordRepository.Get(host.ID, abnormitySetting.Code, true);
                            if (abnormityRecord == null)
                            {
                                abnormityRecord = RoomServiceRecordRepository.CreateServiceRecord(host, abnormitySetting);
                                RoomServiceRecordRepository.Save(abnormityRecord);
                            }
                        }
                    }
                }
                if (!isUpdated)
                {
                    //保险箱开或未接保险箱（关掉报警）
                    if (abnormity1.Status)
                    {
                        abnormity1.Status = false;
                        abnormity1.StartTime = DateTime.Now;
                        RoomServiceRepository.Update(abnormity1);
                        //异常记录
                        var abnormityRecord = RoomServiceRecordRepository.Get(host.ID, abnormitySetting.Code, true);
                        if (abnormityRecord != null)
                        {
                            abnormityRecord.Status = false;
                            abnormityRecord.EndTime = DateTime.Now;
                            RoomServiceRecordRepository.Update(abnormityRecord);
                        }
                    }
                }
            }

            #endregion
        }

        /// <summary>
        /// 创建服务控制数据包
        /// </summary>
        /// <param name="devices"></param>
        /// <returns></returns>
        public byte[] CreateServiceControlDataPacket(int serviceCode, int status)
        {
            using (MemoryStream buffer = new MemoryStream())
            {
                using (BinaryWriter writer = new BinaryWriter(buffer))
                {
                    var headerLen = StructConverter.SizeOf(typeof(SystemHeader));
                    buffer.Seek(headerLen, SeekOrigin.Begin);

                    writer.Write((byte)1);					//写入服务数量
                    writer.Write((byte)serviceCode);		//写入服务代码
                    writer.Write((byte)status);				//写入服务状态
                    writer.Write(new byte[] { 0, 0 }, 0, 2);//预留两字节CRC16校验码

                    buffer.Seek(0, SeekOrigin.Begin);

                    var header = CreateSystemHeader((int)buffer.Length - headerLen);

                    var headerData = StructConverter.StructToBytes(header);

                    writer.Write(headerData, 0, headerData.Length);

                    return buffer.ToArray();
                }
            }
        }

        public override CommandType CommandType
        {
            get { return CommandType.Service; }
        }

        /// <summary>
        /// 解析服务状态
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        private ConcurrentDictionary<int, ServiceStatus> DecodeServices(string hostIP, Stream stream)
        {
            ConcurrentDictionary<int, ServiceStatus> services = null;
            if (roomsServices.ContainsKey(hostIP))
            {
                services = roomsServices[hostIP];
            }
            else
            {
                services = new ConcurrentDictionary<int, ServiceStatus>();
                roomsServices[hostIP] = services;
            }
            using (BinaryReader reader = new BinaryReader(stream))
            {
                try
                {
                    int count = reader.ReadByte();//读取服务数量
                    for (int i = 0; i < count; i++)
                    {
                        ServiceStatus serviceStatus = null;
                        int serviceCode = reader.ReadByte();//读取服务代码
                        if (services.ContainsKey(serviceCode))
                        {
                            serviceStatus = services[serviceCode];
                        }
                        else
                        {
                            serviceStatus = new ServiceStatus();
                            services[serviceCode] = serviceStatus;
                        }

                        serviceStatus.Code = serviceCode;
                        serviceStatus.Status = reader.ReadByte();//读取服务状态
                    }
                }
                catch (Exception) { }
                return services;
            }
        }
    }

	internal class ServiceStatus
	{
		private int code;
		private int status = 2;
		public int Code 
		{
			get { return this.code; }
			set 
			{
				this.code = value;
			}
		}

		public int Status
		{
			get { return this.status; }
			set
			{
				this.status = value;
			}
		}
	}

	internal delegate void ServiceChangedDelegate(string hostIP, ServiceStatus serviceStatus);

	internal class RoomServiceCache
	{
		private Hashtable services = new Hashtable();

		public string HostIP { get; set; }

		public event ServiceChangedDelegate ServiceChanged;

		public void AddOrUpdate(int code, int status)
		{
			ServiceStatus service = services[code] as ServiceStatus;
			if (service != null)
			{
				if (service.Status != status)
				{
					service.Status = status;

					RasieServiceChanged(service);
				}
			}
			else 
			{
				service = new ServiceStatus();
				service.Code = code;
				service.Status = status;

				services[code] = service;

				RasieServiceChanged(service);
			}
		}

		public IList<ServiceStatus> Services
		{
			get 
			{
				IList<ServiceStatus> values = new List<ServiceStatus>();

				foreach (var service in services.Values)
				{
					values.Add((ServiceStatus)service);
				}

				return values;
			}
		}

		private void RasieServiceChanged(ServiceStatus serviceStatus)
		{
			if (ServiceChanged != null)
			{
				ServiceChanged(HostIP, serviceStatus);
			}
		}
	}
}
