using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; 
using Windows.Networking.Sockets;
using System.IO;
using Windows.Storage.Streams;
using Windows.Networking;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Security.Cryptography.Core;
using Windows.Security.Cryptography;

namespace PlaneGcsSdk.Contract.EhNetUWP
{
    public class PLNet
    {
        #region Properties
        public StreamOBJ MyStreamObj;
        /// <summary>
        /// 连接的IP
        /// </summary>
        public string ServerIP { get; protected set; }
        /// <summary>
        /// 连接的端口
        /// </summary>
        public int TCPPort { get; protected set; }
        public bool IsClosing { get; protected set; }
        public bool IsOpen { get; protected set; }
        /// <summary>
        /// 真实名
        /// </summary>
        public string MyNick { get; protected set; }
        /// <summary>
        /// 获取登录随机码
        /// </summary>
        public string MyRND { get { return sMyTempRnd; } }
        /// <summary>
        /// 发送文件列表 
        /// </summary>
        public List<FileOBJ> SendFileList { get; protected set; }
        /// <summary>
        /// 接收文件列表
        /// </summary>
        public List<FileOBJ> RevFileList { get; protected set; }
        /// <summary>
        /// 用户列表 
        /// </summary>
        public List<UserProperty> FriendList { get; protected set; }
        /// <summary>
        /// 发送流列表
        /// </summary>
        public List<StreamOBJ> SendStreamList { get; protected set; }
        public List<StreamOBJ> RevStreamList { get; protected set; }
        #endregion

        #region Fields
        string sMyTempRnd; //登录随机数
        bool LoginFlag = false; //登录状态
        string sMyID; //登录用户名
        string sMyPassword; //登录密码 
        
        string sMyIP; //用户IP
        Encoding _enconding;
        int iStreamID = -1;
        int iFileID = 0;

        protected StreamSocket _socket; //连接对象
        protected IInputStream _inputStream;
        protected Stream _outputStream;
        protected bool _canFlush = true;
        protected const uint READ_BUFFER_SIZE = 2047;
        #endregion

        #region Events 
        public event EventHandler<LoginEventArgs> Login;
        public event EventHandler<DiscoveredOneUserEventArgs> DiscoveredOneUser;
        public event EventHandler RefreshFriendListCompleted;
        public event EventHandler<UserStateChangeEventArgs> UserStateChange;
        public event EventHandler Disconnect;
        public event EventHandler<CommandArrivalEventArgs> CommandArrival;
        public event EventHandler<ExceptionThrownEventArgs> ExceptionThrown;
        public event EventHandler<StartSendFileEventArgs> StartSendFile;
        #endregion

        public PLNet()
        {
            FriendList = new List<UserProperty>();
            SendFileList = new List<FileOBJ>();
            RevFileList = new List<FileOBJ>();
            SendStreamList = new List<StreamOBJ>();
            RevStreamList = new List<StreamOBJ>();
        }

        #region Public Methods
        /// <summary>
        /// 建立连接
        /// </summary>
        /// <param name="sServerIP"></param>
        /// <param name="sUsername"></param>
        /// <param name="sPassword"></param>
        /// <param name="md5Password"></param>
        /// <returns></returns>
        public async Task Start(string sServerIP, string sUsername, string sPassword, string md5Password = null)
        {
            Close();
            IsClosing = false;

            if (_enconding == null)
            {
                _enconding = await DBCSEncoding.GetDBCSEncoding("GB2312");
            }

            sMyID = sUsername;
            if (md5Password == null)
                sMyPassword = GetStrMd5Hash(sMyID + sPassword);
            else
                sMyPassword = md5Password;

            ServerIP = sServerIP; //登录用户的IP地址
            TCPPort = 52510;  //登录用户的端口

            _socket = new StreamSocket(); //创建连接对象 
            await _socket.ConnectAsync(new HostName(ServerIP), TCPPort.ToString()).AsTask().ConfigureAwait(false); //执行连接操作

            IsOpen = true;
            _inputStream = _socket.InputStream;
            _outputStream = _socket.OutputStream.AsStreamForWrite();
            await Task.Run(() => TCPClientThread());
        }

        /// <summary>
        /// 关闭连接
        /// </summary>
        public void Close()
        {
            IsClosing = true;
            IsOpen = false;
            _inputStream?.Dispose();
            _outputStream?.Dispose();
            _socket?.Dispose();
            if (SendFileList != null)
                SendFileList.Clear();
            if (RevFileList != null)
                RevFileList.Clear();
            Disconnect?.Invoke(this, EventArgs.Empty); //触发事件Disconnect
        }

        /// <summary>
        /// 发送命令
        /// </summary>
        /// <param name="friendIDs"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        public async Task SendCommand(string friendIDs, string msg)
        {
            string commandtext = "*" + friendIDs + "|" + msg;
            await SendCmd(commandtext);
        }

        /// <summary>
        /// 发送数据流，给多个用户同时发送数据
        /// </summary>
        /// <param name="friendIDs"></param>
        /// <param name="myData"></param>
        /// <returns></returns>
        public async Task SendStream(string friendIDs, byte[] myData)
        {
            string[] myFriends = friendIDs.Split(',');
            for (int i = 0; i < myFriends.Length; i++)
            {
                iStreamID += 1;
                MyStreamObj = new StreamOBJ();
                MyStreamObj.StreamID = iStreamID;
                MyStreamObj.StreamData = myData;
                MyStreamObj.RevUserID = myFriends[i];
                SendStreamList.Add(MyStreamObj);
                string streamtext = "*" + myFriends[i] + "|SB|" + MyStreamObj.StreamID.ToString() + "|" + MyStreamObj.StreamData.Length.ToString();
                await SendCmd(streamtext);
            }
        }

        /// <summary>
        /// 发送普通文件
        /// </summary>
        /// <param name="friendID"></param>
        /// <param name="sFilenames"></param>
        /// <returns></returns>
        public async Task SendFileCommon(string friendID, string[] sFilenames)
        {
            string filetype = "F";
            await SendFileCommon(friendID, sFilenames, filetype);
        }

        /// <summary>
        /// 发送图片文件
        /// </summary>
        /// <param name="friendID"></param>
        /// <param name="sFilenames"></param>
        /// <returns></returns>
        public async Task SendPicture(string friendID, string[] sFilenames)
        {
            string filetype = "P";
            await SendFileCommon(friendID, sFilenames, filetype);
        }

        /// <summary>
        /// 发送视频文件
        /// </summary>
        /// <param name="friendID"></param>
        /// <param name="sFilenames"></param>
        /// <returns></returns>
        public async Task SendVideo(string friendID, string[] sFilenames)
        {
            string filetype = "V";
            await SendFileCommon(friendID, sFilenames, filetype);
        }

        /// <summary>
        /// 发送文件公共函数
        /// </summary>
        /// <param name="friendID"></param>
        /// <param name="sFilenames"></param>
        /// <param name="filetype"></param>
        /// <returns></returns>
        public async Task SendFileCommon(string friendID, string[] sFilenames, string filetype)
        {
            for (int i = 0; i < sFilenames.Length; i++)
            {
                FileOBJ tmpFileObj = new FileOBJ();
                tmpFileObj.FileID = iFileID;
                iFileID += 1;
                FileInfo MyFileInfo = new FileInfo(sFilenames[i]);
                tmpFileObj.Filename = MyFileInfo.Name;
                tmpFileObj.FilePath = MyFileInfo.DirectoryName;
                tmpFileObj.FileLength = MyFileInfo.Length;
                tmpFileObj.FileModDate = filetype + MyFileInfo.LastWriteTime.ToString(); //filetype表示发送文件的类型
                tmpFileObj.RevUserID = friendID;
                tmpFileObj.State = FileState.WaitConfim;
                tmpFileObj.tmpFileStream = new FileStream(sFilenames[i], FileMode.Open, FileAccess.Read);
                SendFileList.Add(tmpFileObj);
                StartSendFile?.Invoke(this, new StartSendFileEventArgs(SendFileList.Count - 1));
                string filetext = "FB" + friendID + "|" + tmpFileObj.FileID.ToString() + "|" + tmpFileObj.Filename + "|" + tmpFileObj.FileLength.ToString() + "|" + tmpFileObj.FileModDate;
                await SendCmd(filetext);
            }
        }

        /// <summary>
        /// 发送数据公共函数
        /// </summary>
        /// <param name="cmdStr"></param>
        /// <param name="addBytes"></param>
        /// <returns></returns>
        public async Task SendCmd(string cmdStr, byte[] addBytes = null)
        {
            byte[] tmpSendbytes = null;
            tmpSendbytes = _enconding.GetBytes(cmdStr);
            int nret;
            int strLen = LenB(cmdStr);
            if (addBytes == null)
            {
                tmpSendbytes = BitConverter.GetBytes((ushort)strLen);
                Array.Resize(ref tmpSendbytes, strLen + 2);
                nret = _enconding.GetBytes(cmdStr, 0, cmdStr.Length, tmpSendbytes, 2);
            }
            else
            {
                byte[] tmpBytes;
                long allLenth = strLen + addBytes.Length;
                tmpBytes = BitConverter.GetBytes(allLenth);
                Array.Resize(ref tmpBytes, strLen + 2);
                nret = _enconding.GetBytes(cmdStr, 0, cmdStr.Length, tmpBytes, 2);
                tmpSendbytes = AddByte(tmpBytes, addBytes);
            }
            try
            {
                if (tmpSendbytes != null)
                {
                    await _outputStream.WriteAsync(tmpSendbytes, 0, tmpSendbytes.Length).ConfigureAwait(false);
                    if (_canFlush)
                    {
                        await _outputStream.FlushAsync().ConfigureAwait(false);
                    }
                }
            }
            catch (Exception ex)
            {
                _canFlush = false;
                Close();
                ExceptionThrown?.Invoke(this, new ExceptionThrownEventArgs(ex));
            }
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// 启动一个接收数据的线程
        /// </summary>
        private async void TCPClientThread()
        {
            uint i; //接收到的数据的长度
            byte[] byteBuffer = null; //暂存接收到的数据  
            int comLength;
            bool ReadFlag = false;
            bool nRet;
            byte[] Tmpbyte;

            IBuffer tempbuf = new Windows.Storage.Streams.Buffer(READ_BUFFER_SIZE); //建立一个buffer
            await _inputStream.ReadAsync(tempbuf, READ_BUFFER_SIZE, InputStreamOptions.Partial);
            i = tempbuf.Length;
            byteBuffer = new byte[i];
            tempbuf.CopyTo(byteBuffer); //将数据输出 

            while (i != 0)
            {
                if (byteBuffer.Length > 3)
                {
                    comLength = BitConverter.ToUInt16(byteBuffer, 0);
                    if (LoginFlag == false && comLength > 70)
                    {
                        #region
                        if (byteBuffer.Length >= 2)
                        {
                            byte[] firstchar = new byte[1];
                            firstchar[0] = byteBuffer[1];
                            if (_enconding.GetString(firstchar) != "L")
                                i = 0;
                        }
                        else
                        {
                            ReadFlag = true;
                        }
                        #endregion
                    }
                    else
                    {
                        #region
                        if (byteBuffer.Length >= comLength + 1)
                        {
                            Tmpbyte = MidByte(byteBuffer, 3, comLength); //截取字节
                            if (Tmpbyte.Length < 4)
                                nRet = false;
                            nRet = await ProcessData(Tmpbyte);
                            if (nRet == true)
                            {
                                byteBuffer = RightByte(byteBuffer, byteBuffer.Length - comLength - 2); //截取字节
                                if (byteBuffer.Length <= 0)
                                    ReadFlag = true;
                            }
                            else
                            {
                                return;
                            }
                        }
                        else
                        {
                            ReadFlag = true;
                        }
                        #endregion
                    }
                }
                else
                {
                    ReadFlag = true;
                }
                if (ReadFlag == true)
                {
                    #region
                    ReadFlag = false;
                    try
                    {
                        tempbuf = new Windows.Storage.Streams.Buffer(READ_BUFFER_SIZE); //建立一个buffer
                        await _inputStream.ReadAsync(tempbuf, READ_BUFFER_SIZE, InputStreamOptions.Partial);
                        i = tempbuf.Length;
                        byte[] addbyte = new byte[i];
                        tempbuf.CopyTo(addbyte);
                        byteBuffer = AddByte(byteBuffer, addbyte);
                    }
                    catch(Exception ex)
                    {
                        i = 0;
                    }
                    #endregion
                }
            }
        }

        /// <summary>
        /// 处理接收到的数据
        /// </summary>
        /// <param name="tmpbyte"></param>
        /// <returns></returns>
        private async Task<bool> ProcessData(byte[] tmpbyte)
        {
            string recestr = _enconding.GetString(tmpbyte); //这里本应是GBK编码 
            UserProperty tmpUserinfo = new UserProperty();
            int tmpIndex;

            switch (recestr[0])
            {
                case 'R':
                    sMyTempRnd = recestr.Substring(1);
                    await SendCmd("L" + sMyID + "|" + GetStrMd5Hash(sMyPassword + sMyTempRnd));
                    sMyTempRnd = sMyTempRnd.Substring(0, sMyTempRnd.Length - 1);
                    break;
                case 'U': //收到用户信息
                    string sUserinfo = recestr.Substring(1);
                    switch (sUserinfo[0])
                    {
                        #region
                        case 'A': //用户列表
                            #region
                            sUserinfo = sUserinfo.Substring(1);
                            tmpUserinfo.UserID = Fin(1, sUserinfo, "|");
                            tmpUserinfo.UserNick = Fin(2, sUserinfo, "|");
                            tmpUserinfo.UserIndex = FriendList.Count;
                            tmpUserinfo.UserMessage = new List<String>();
                            FriendList.Add(tmpUserinfo);
                            DiscoveredOneUser?.Invoke(this, new DiscoveredOneUserEventArgs(tmpUserinfo));
                            #endregion
                            break;
                        case 'O': //用户上线
                            #region
                            sUserinfo = sUserinfo.Substring(1);
                            tmpIndex = GetUserIndexByID(Fin(1, sUserinfo, "|"));
                            if (tmpIndex >= 0)
                            {
                                tmpUserinfo = FriendList[tmpIndex]; //修改用户在线状态
                                tmpUserinfo.UserOnline = true;
                                FriendList[tmpIndex] = tmpUserinfo;
                                UserStateChange?.Invoke(this, new UserStateChangeEventArgs(tmpUserinfo));
                            }
                            #endregion
                            break;
                        case 'F': //用户下线
                            #region
                            sUserinfo = sUserinfo.Substring(1);
                            string tmpFriendID = Fin(1, sUserinfo, "|");
                            tmpIndex = GetUserIndexByID(tmpFriendID);
                            if (tmpIndex >= 0)
                            {
                                tmpUserinfo = FriendList[tmpIndex];
                                tmpUserinfo.UserOnline = false;
                                FriendList[tmpIndex] = tmpUserinfo;
                                //处理发送文件残留
                                for (int i = 0; i < SendFileList.Count; i--)
                                {
                                    if (SendFileList[i].RevUserID == tmpFriendID)
                                    {
                                        SendFileList[i].tmpFileStream.Dispose();
                                        SendFileList.RemoveAt(i);
                                    }
                                }
                                for (int j = 0; j < RevFileList.Count; j--)
                                {
                                    if (RevFileList[j].RevUserID == tmpFriendID)
                                    {
                                        RevFileList[j].tmpFileStream.Dispose();
                                        RevFileList.RemoveAt(j);
                                    }
                                }
                                UserStateChange?.Invoke(this, new UserStateChangeEventArgs(tmpUserinfo));
                            }
                            #endregion
                            break;
                        case 'D':
                            break;
                        case 'C': //被别人添加为好友时会收到
                            break;
                        case 'E':
                            RefreshFriendListCompleted?.Invoke(this, EventArgs.Empty);
                            break;
                        default:
                            break;
                            #endregion
                    }
                    break;
                case 'L': //登陆
                    recestr = recestr.Substring(1);
                    switch (recestr[0])
                    {
                        #region
                        case 'P':
                            Login?.Invoke(this, new LoginEventArgs(LoginState.LoginPwdError));
                            break;
                        case 'O':
                            LoginFlag = true;
                            MyNick = recestr.Substring(1).Split('|')[0];
                            Login?.Invoke(this, new LoginEventArgs(LoginState.LoginOk));
                            await SendCmd("UG");
                            break;
                        case 'U':
                            Login?.Invoke(this, new LoginEventArgs(LoginState.LoginNoUser));
                            break;
                        case 'E':
                            Login?.Invoke(this, new LoginEventArgs(LoginState.LoginOtherError));
                            break;
                        default:
                            break;
                            #endregion
                    }
                    break;
                case '*':  //收到命令，格式为: *+FriendID+|+Msg
                    string tmpStr = recestr.Substring(1);
                    string tmpstr2 = Fin(2, tmpStr, "|");
                    switch (tmpstr2[0])
                    {
                        #region
                        case 'S':
                            break;
                        default:
                            string friendid = Fin(1, tmpStr, "|");
                            string msgcontent = tmpStr.Substring(tmpStr.IndexOf("|") + 1);
                            CommandArrival?.Invoke(this, new CommandArrivalEventArgs(friendid, msgcontent));
                            break;
                            #endregion
                    }
                    break;
                default:
                    break;
            }
            return true;
        }

        private int LenB(string str)
        {
            return _enconding.GetBytes(str).Count();
        }

        /// <summary>
        /// 获取一个字节数组中的某些字节，从start个字节开始，获取length个字节,第几个是从1开始的
        /// </summary>
        /// <param name="tbyte"></param>
        /// <param name="start"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        private byte[] MidByte(byte[] tbyte, int start, int length)
        {
            byte[] tmpMidByte = new byte[length];
            //for (int i = 0; i < length; i++)
            //{
            //    tmpMidByte[i] = tbyte[i + start - 1];
            //}
            for (int i = start - 1; i <= length - 1 + start - 1; i++)
            {
                tmpMidByte[i - (start - 1)] = tbyte[i];
            }
            return tmpMidByte;
        }

        /// <summary>
        /// 获取某个字节数组的后面的字节，获取length个字节
        /// </summary>
        /// <param name="tbyte"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        private byte[] RightByte(byte[] tbyte, int length)
        {
            byte[] temRightByte = new byte[length];
            temRightByte = MidByte(tbyte, tbyte.Length - length + 1, length);
            return temRightByte;
        }

        /// <summary>
        /// 获取某个字节数组的前面的字节，获取length个字节
        /// </summary>
        /// <param name="tbyte"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        private byte[] LeftByte(byte[] tbyte, int length)
        {
            byte[] temLeftByte = new byte[length];
            temLeftByte = MidByte(tbyte, 1, length);
            return temLeftByte;
        }

        /// <summary>
        /// 拼接两个字节,将addbyte加在srcbyte后面
        /// </summary>
        /// <param name="srcbyte"></param>
        /// <param name="addbyte"></param>
        /// <returns></returns>
        private byte[] AddByte(byte[] srcbyte, byte[] addbyte)
        {
            byte[] resultbyte = new byte[srcbyte.Length + addbyte.Length];
            for (int i = 0; i < srcbyte.Length; i++)
            {
                resultbyte[i] = srcbyte[i];
            }
            for (int j = 0; j < addbyte.Length; j++)
            {
                resultbyte[j + srcbyte.Length] = addbyte[j];
            }
            return resultbyte;
        }

        /// <summary>
        /// 根据ID获取列表中序号
        /// </summary>
        /// <param name="tmpUserID"></param>
        /// <returns></returns>
        protected int GetUserIndexByID(string tmpUserID)
        {
            for (int i = 0; i < FriendList.Count; i++)
            {
                if (FriendList.ElementAt(i).UserID == tmpUserID)
                    return i;
            }
            return -1;
        }

        /// <summary>
        /// 分割字符串，第Xnum个字符，字符串为SrcStr，分割特征为SpStr
        /// </summary>
        /// <param name="xnum"></param>
        /// <param name="srcStr"></param>
        /// <param name="spStr"></param>
        /// <returns></returns>
        private string Fin(int xnum, string srcStr, string spStr)
        {
            string[] tmpStr;
            char[] FF = new char[1];
            FF[0] = spStr[0];
            tmpStr = srcStr.Split(FF);
            if ((xnum > 0) && (xnum <= tmpStr.Length))
                return tmpStr[xnum - 1];
            else
                return "";
        }

        /// <summary>
        /// 计算字符串的MD5哈希值，输入为字符串strtohash，输出为字符串strHash
        /// </summary>
        /// <param name="strtohash"></param>
        /// <returns></returns>
        private string GetStrMd5Hash(string strtohash)
        {

            byte[] tmpbyte = _enconding.GetBytes(strtohash);

            IBuffer buffMsg = CryptographicBuffer.CreateFromByteArray(tmpbyte);

            HashAlgorithmProvider objAlgProv = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Md5);

            IBuffer tmpbuffer = objAlgProv.HashData(buffMsg);

            uint i = tmpbuffer.Length;
            byte[] mybyte = new byte[i];
            tmpbuffer.CopyTo(mybyte);

            CryptographicBuffer.CopyToByteArray(tmpbuffer, out mybyte);

            StringBuilder sBuilder = new StringBuilder();
            for (int k = 0; k < mybyte.Length; k++)
            {
                sBuilder.Append(mybyte[k].ToString("x2"));
            }

            string strHash = sBuilder.ToString();

            return strHash;
        }

        #endregion
    }
    #region Nested Type
    /// <summary>
    /// 用户信息结构体
    /// </summary>
    public struct UserProperty
    {
        public string UserID;
        public string UserNick;
        public string UserIP;
        public int UserIndex;
        public bool UserOnline;
        public List<string> UserMessage;
    }

    /// <summary>
    /// 文件状态
    /// </summary>
    public enum FileState
    {
        WaitConfim,
        StartSend, //开始发送，等待确认
        Sending, //正在发送中
        reSend, //断点续传中 
        SendError, //发送错误
        Completed, //发送完成 
        LoginOtherError, //其他错
        StartReveice, //开始接收，等待数据
        Receiving,   //接收中
        ReReceiving, //断点续传中
        ReceiveError
    }

    /// <summary>
    /// 登录状态
    /// </summary>
    public enum LoginState
    {
        LoginPwdError, //密码错
        LoginOk, //登陆成功
        LoginNoUser, //用户不存在
        LoginOtherError, //其他错
    }

    /// <summary>
    /// 文件结构体
    /// </summary>
    public struct FileOBJ
    {
        public string Filename;
        public string FilePath;
        public long FileLength;
        public string FileModDate;
        public int FileID;
        public string SendUserID;
        public string RevUserID;
        public FileStream tmpFileStream;
        public FileState State;
    }

    /// <summary>
    /// 数据流结构体
    /// </summary>
    public struct StreamOBJ
    {
        public byte[] StreamData;
        public ulong CurrPos;
        public int StreamID;
        public ulong Length;
        public string SendUserID;
        public string RevUserID;
    }
    #endregion 
}
