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

namespace RCUHost.Implement
{
    public class UpdateConfigReceiver : GenericReceiverBase, IUpdateConfigReceiver
    {
        private static log4net.ILog logger = log4net.LogManager.GetLogger(typeof(UpdateConfigReceiver));
        private const int BLOCK_SIZE = 512;		//TFTP块大小512字节
        
        public void Update(HostUpdate hostUpdate, IList<Host> hosts)
        {
            if (hosts == null || hosts.Count == 0)
            {
                throw new ApplicationException("升级失败，没有找到需要升级的主机。");
            }
            if (String.IsNullOrEmpty(hostUpdate.Href))
            {
                throw new ApplicationException("升级失败，无效的配置文件。");
            }
            string updateFile = Tools.GetApplicationPath() + hostUpdate.Href;
            if (!File.Exists(updateFile))
            {
                throw new ApplicationException("升级失败，【" + updateFile + "】不是有效的配置文件。");
            }
            FileInfo fileInfo = new FileInfo(updateFile);
            ushort blockNum = (ushort)Math.Ceiling((double)fileInfo.Length / BLOCK_SIZE);
            //升级配置文件
            foreach (var host in hosts)
            {
                SendUpdateRequest(host, hostUpdate.Md5, blockNum);
                //this.updateHostList.Add(new UpdateHostWorker(host, hostUpdate));
            }
        }

        public override void Process(ReceiverContext context)
        {
            //base.Process(context);
            //int startIndex = StructConverter.SizeOf(context.SystemHeader);
            //UpdateConfigPacketReply? reply = DecodeUpdateConfigPacketReply(context.Data, startIndex);
            //if (reply.HasValue)
            //{
            //    logger.Error(string.Format("收到配置文件升级回复命令（{0}:{1}）：{2}", context.RemoteEndPoint.Address.ToString(), context.RemoteEndPoint.Port, Tools.ByteToString(context.Data)));
            //    var updateHostWorker = updateHostList.FirstOrDefault(r => r.Host.HostNumber == context.SystemHeader.Value.HostNumber.ToString());
            //    if (updateHostWorker != null)
            //    {
            //        //主机升级后重启会发送回复搜索命令，这时更新版本号
            //        if (reply.Value.Status == UpdateHostPacketReply.Ready)
            //        {
            //            updateHostWorker.Update();
            //        }
            //        else
            //        {
            //            updateHostList.Remove(updateHostWorker);
            //        }
            //    }
            //}
        }

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

        #region Private Methods

        /// <summary>
        /// 发送升级指令到RCU主机
        /// </summary>
        /// <param name="host"></param>
        /// <param name="updateFileMd5">升级文件MD5值</param>
        public void SendUpdateRequest(Host host, string updateFileMd5, ushort blockNum)
        {
            byte[] data = CreateUpdateConfigRequestPacket(updateFileMd5, blockNum);
            Send(data, host.HostNumber, host.MAC);// host.IP, host.Port);
        }

        /// <summary>
        /// 创建升级配置数据请求数据包
        /// </summary>
        /// <param name="updateFileMd5">升级文件MD5值</param>
        /// <param name="blockNum">块数量</param>
        /// <returns></returns>
        private byte[] CreateUpdateConfigRequestPacket(string updateFileMd5, ushort blockNum)
        {
            SystemHeader systemHeader = CreateSystemHeader();
            int headerSize = StructConverter.SizeOf(systemHeader);

#if DEBUG
            byte[] ipArr = IPAddress.Parse(Tools.GetLocalIP()).GetAddressBytes();
#else
            byte[] ipArr = IPAddress.Parse(MessageIP).GetAddressBytes();
#endif
            byte[] portArr = BitConverter.GetBytes((ushort)MessagePort);
            uint[] md5Arr = Tools.MD5StringToUIntArray(updateFileMd5);

            using (var stream = new MemoryStream())
            {
                stream.Seek(headerSize, SeekOrigin.Begin);

                stream.Write(BitConverter.GetBytes(md5Arr[0]), 0, 4);
                stream.Write(BitConverter.GetBytes(md5Arr[1]), 0, 4);
                stream.Write(BitConverter.GetBytes(md5Arr[2]), 0, 4);
                stream.Write(BitConverter.GetBytes(md5Arr[3]), 0, 4);
                stream.Write(BitConverter.GetBytes(blockNum), 0, 2);
                stream.Write(ipArr, 0, ipArr.Length);
                stream.Write(portArr, 0, portArr.Length);
                stream.Write(new byte[] { 0, 0 }, 0, 2);

                //填充 SystemHeader
                stream.Seek(0, SeekOrigin.Begin);
                systemHeader.FrameLength = (ushort)stream.Length;
                systemHeader.FrameNo = 0xFFFF;
                byte[] headerData = StructConverter.StructToBytes(systemHeader);
                stream.Write(headerData, 0, headerData.Length);

                return stream.ToArray();
            }
        }

        /// <summary>
        /// 解码 UpdateHostPacketReply
        /// </summary>
        /// <param name="data"></param>
        /// <param name="startIndex"></param>
        /// <returns></returns>
        private UpdateConfigPacketReply? DecodeUpdateConfigPacketReply(byte[] data, int startIndex)
        {
            return StructConverter.BytesToStruct(data, startIndex, typeof(UpdateConfigPacketReply)) as UpdateConfigPacketReply?;
        }

        #endregion
    }
}
