using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Data;
using System.Reflection;
using System.Collections;

namespace NSC
{
    /// <summary>
    /// 功能:网络报文编码器
    /// 作者:王昊昇
    /// 时间:2012.11.8
    /// </summary>
    internal static class PacketsEncoder
    {
        /// <summary>
        /// 写入实体开始部分
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        private static void WriteEntityBegin(System.Xml.XmlTextWriter xtw)
        {
            xtw.WriteStartElement("Items");
        }

        /// <summary>
        /// 写入任意元素的结束部分
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        private static void WriteElementEnd(System.Xml.XmlTextWriter xtw)
        {
            xtw.WriteEndElement();
        }

        /// <summary>
        /// 写入网络数据传输数据开始部分
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        private static void WriteNetDataBegin(System.Xml.XmlTextWriter xtw)
        {
            xtw.WriteStartElement("NetworkStreamData");
        }

        /// <summary>
        /// 写入服务定义开始部分
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        private static void WriteServiceDefineBegin(System.Xml.XmlTextWriter xtw)
        {
            xtw.WriteStartElement("ServiceDefine");
        }

        /// <summary>
        /// 写入参数定义开始部分
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        private static void WriteParameterDefineBegin(System.Xml.XmlTextWriter xtw)
        {
            xtw.WriteStartElement("Param");
        }

        /// <summary>
        /// 写入返回值定义开始部分
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        private static void WriteReturnDefineBegin(System.Xml.XmlTextWriter xtw)
        {
            xtw.WriteStartElement("Return");
        }

        /// <summary>
        /// 写入实体属性开始部分
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        private static void WriteEntityAttributeBegin(System.Xml.XmlTextWriter xtw)
        {
            xtw.WriteStartElement("Item");
        }

        /// <summary>
        /// 写入实体属性
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        /// <param name="publicProperties">实体公共属性列表</param>
        /// <param name="entity">实体引用</param>
        private static void WriteEntityAttribute(System.Xml.XmlTextWriter xtw, PropertyInfo[] publicProperties, object entity)
        {
            foreach (var p in publicProperties)
            {
                var v = p.GetValue(entity, null);
                if (v != null)
                {
                    if (v.GetType().IsValueType || v is string)
                    {
                        xtw.WriteAttributeString(p.Name, v.ToString());
                    }
                }
                else
                {
                    xtw.WriteAttributeString(p.Name, "");
                }
            }
        }

        /// <summary>
        /// 写入链接属性
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        /// <param name="value">链接属性值</param>
        private static void WriteLinkAttribute(System.Xml.XmlTextWriter xtw, string value)
        {
            xtw.WriteAttributeString("Link", value);
        }

        /// <summary>
        /// 写入值属性
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        /// <param name="value">属性值</param>
        private static void WriteValueAttribute(System.Xml.XmlTextWriter xtw, string value)
        {
            xtw.WriteAttributeString("Value", value);
        }

        /// <summary>
        /// 写入固件类型属性
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        /// <param name="value">固件类型</param>
        private static void WriteFirmwareModelAttribute(System.Xml.XmlTextWriter xtw, string value)
        {
            xtw.WriteAttributeString("FirmwareModel", value);
        }

        /// <summary>
        /// 写入固件版本属性
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        /// <param name="value">固件版本</param>
        private static void WriteFirmwareVersionAttribute(System.Xml.XmlTextWriter xtw, string value)
        {
            xtw.WriteAttributeString("FirmwareVersion", value);
        }

        /// <summary>
        /// 写入用户姓名属性
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        /// <param name="value">用户姓名属性</param>
        private static void WriteUserNameAttribute(System.Xml.XmlTextWriter xtw, string value)
        {
            xtw.WriteAttributeString("UserName", value);
        }

        /// <summary>
        /// 写入服务代码属性
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        /// <param name="value">服务代码</param>
        private static void WriteServiceNameAttribute(System.Xml.XmlTextWriter xtw, string value)
        {
            xtw.WriteAttributeString("ServiceName", value);
        }

        /// <summary>
        /// 写入执行异常属性
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        /// <param name="value">异常属性值</param>
        private static void WriteExceptionAttribute(System.Xml.XmlTextWriter xtw, string value)
        {
            xtw.WriteAttributeString("Exception", value);
        }

        /// <summary>
        /// 写入同步方式属性
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        /// <param name="value">同步方式属性值</param>
        private static void WriteSyncTypeAttribute(System.Xml.XmlTextWriter xtw, string value)
        {
            xtw.WriteAttributeString("SyncType", value);
        }

        /// <summary>
        /// 写入服务流水号属性
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        /// <param name="value">服务流水号属性值</param>
        private static void WriteServiceSNAttribute(System.Xml.XmlTextWriter xtw, string value)
        {
            xtw.WriteAttributeString("ServiceSN", value);
        }

        /// <summary>
        /// 写入参数名称属性
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        /// <param name="value">参数名称属性值</param>
        private static void WriteParameterNameAttribute(System.Xml.XmlTextWriter xtw, string value)
        {
            xtw.WriteAttributeString("Name", value);
        }

        /// <summary>
        /// 写入参数类型属性
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        /// <param name="value">参数类型属性值</param>
        private static void WriteParameterTypeAttribute(System.Xml.XmlTextWriter xtw, string value)
        {
            xtw.WriteAttributeString("Type", value);
        }

        /// <summary>
        /// 写入DataTable参数
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        /// <param name="n">数据</param>
        /// <param name="ds">附件</param>
        /// <param name="i">附件索引</param>
        private static void WriteParameterDataTable(System.Xml.XmlTextWriter xtw, ParameterStruct n, DataSet ds, ref int i)
        {
            string tabName = "Tab" + i.ToString();

            WriteLinkAttribute(xtw, tabName);
            WriteValueAttribute(xtw, "");

            DataTable table = (n.Data as DataTable).Copy();
            table.TableName = tabName;
            ds.Tables.Add(table);
            i++;
        }

        /// <summary>
        /// 写入实体参数
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        /// <param name="n">数据</param>
        private static void WriteParameterEntity(System.Xml.XmlTextWriter xtw, ParameterStruct n)
        {
            WriteEntityBegin(xtw);

            PropertyInfo[] publicProperties = Common.AssemblyUtil.EntityFactory.GetProperties(n.Data);

            WriteEntityAttributeBegin(xtw);

            WriteEntityAttribute(xtw, publicProperties, n.Data);

            WriteElementEnd(xtw);

            WriteElementEnd(xtw);
        }

        /// <summary>
        /// 写入数组参数
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        /// <param name="n">数据</param>
        private static void WriteParameterArray(System.Xml.XmlTextWriter xtw, ParameterStruct n)
        {
            WriteLinkAttribute(xtw, "");
            WriteEntityBegin(xtw);

            foreach (var s in n.Data as string[])
            {
                WriteEntityAttributeBegin(xtw);

                WriteValueAttribute(xtw, s);

                WriteElementEnd(xtw);
            }

            WriteElementEnd(xtw);
        }

        /// <summary>
        /// 写入实体列表参数
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        /// <param name="n">数据</param>
        private static void WriteParameterEntityList(System.Xml.XmlTextWriter xtw, ParameterStruct n)
        {
            WriteLinkAttribute(xtw, "");
            IList list = (IList)n.Data;
            if (list.Count > 0)
            {
                WriteEntityBegin(xtw);

                if (list[0].GetType().IsValueType || list[0].GetType() == typeof(string))
                {
                    //写元素
                    foreach (var m in list)
                    {
                        WriteEntityAttributeBegin(xtw);

                        WriteValueAttribute(xtw, m.ToString());

                        WriteElementEnd(xtw);
                    }
                }
                else
                {
                    PropertyInfo[] publicProperties =Common.AssemblyUtil.EntityFactory.GetProperties(list[0]);

                    //写元素
                    foreach (var m in list)
                    {
                        publicProperties = Common.AssemblyUtil.EntityFactory.GetProperties(m);

                        WriteEntityAttributeBegin(xtw);

                        WriteEntityAttribute(xtw, publicProperties, m);

                        WriteElementEnd(xtw);
                    }
                }

                WriteElementEnd(xtw);
            }
        }

        /// <summary>
        /// 写入二进制参数
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        /// <param name="n">数据</param>
        /// <param name="i">附件索引</param>
        /// <param name="extData">扩展数据流</param>
        private static void WriteParameterBinary(System.Xml.XmlTextWriter xtw, ParameterStruct n, ref int i, ref Dictionary<int, byte[]> extData)
        {
            WriteLinkAttribute(xtw, i.ToString());
            WriteValueAttribute(xtw, "");

            extData.Add(i, n.Data as byte[]);

            i++;
        }

        /// <summary>
        /// 写入参数及返回信息
        /// </summary>
        /// <param name="xtw">XML写入器</param>
        /// <param name="n">数据</param>
        /// <param name="ds">附件</param>
        /// <param name="i">附件索引</param>
        /// <param name="extData">扩展数据流</param>
        private static void WriteParameters(System.Xml.XmlTextWriter xtw, ParameterStruct n, DataSet ds, ref int i, ref Dictionary<int, byte[]> extData)
        {
            if (n.Data == null)
            {
                throw new Common.Exceptions.ParameterCannotNullException();
            }
            if (n.Data is DataTable)
            {
                WriteParameterTypeAttribute(xtw, "DataTable");
                WriteParameterDataTable(xtw, n, ds, ref i);
            }
            else if (n.Data is string[])
            {
                WriteParameterTypeAttribute(xtw, "Array");
                WriteParameterArray(xtw, n);
            }
            else if (n.Data is byte[])
            {
                WriteParameterTypeAttribute(xtw, "Binary");
                WriteParameterBinary(xtw, n, ref i, ref extData);
            }
            else if (n.Data is IList)
            {
                WriteParameterTypeAttribute(xtw, "IList");
                WriteParameterEntityList(xtw, n);
            }
            else if (n.Data.GetType().IsValueType)
            {
                WriteParameterTypeAttribute(xtw, n.Data.GetType().Name);
                WriteLinkAttribute(xtw, "");
                WriteValueAttribute(xtw, n.Data.ToString());
            }
            else if (n.Data is string)
            {
                WriteParameterTypeAttribute(xtw, "String");
                WriteLinkAttribute(xtw, "");
                WriteValueAttribute(xtw, n.Data.ToString());
            }
            else
            {
                WriteParameterTypeAttribute(xtw, "Entity");
                WriteLinkAttribute(xtw, "");
                WriteParameterEntity(xtw, n);
            }
        }

        /// <summary>
        /// 由网络数据结构转换到内存流
        /// </summary>
        /// <param name="data">服务请求或响应的数据</param>
        /// <returns>返回转换后的内存流</returns>
        internal static MemoryStream ConvertToStream(NetServiceStruct data)
        {
            MemoryStream ms = new MemoryStream();

            //附件
            using (DataSet ds = new DataSet())
            {
                Dictionary<int, byte[]> extData = new Dictionary<int, byte[]>();

                //主数据部分
                using (MemoryStream tmpStream = new MemoryStream())
                {
                    System.Xml.XmlTextWriter xtw = new System.Xml.XmlTextWriter(tmpStream, Common.TextUtil.EncodingHelper.GB2312);
                    xtw.WriteStartDocument();

                    WriteNetDataBegin(xtw);
                    WriteFirmwareModelAttribute(xtw, data.FirmwareModel);
                    WriteFirmwareVersionAttribute(xtw, data.FirmwareVersion);
                    WriteUserNameAttribute(xtw, data.UserName);

                    WriteServiceDefineBegin(xtw);
                    WriteServiceNameAttribute(xtw, data.ServiceName);
                    WriteExceptionAttribute(xtw, data.ExceptionType.GetHashCode().ToString());
                    WriteSyncTypeAttribute(xtw, data.SyncType.GetHashCode().ToString());
                    WriteServiceSNAttribute(xtw, "");

                    int i = 0;

                    //参数部分
                    foreach (var n in data.Params)
                    {
                        WriteParameterDefineBegin(xtw);
                        WriteParameterNameAttribute(xtw, n.Key);

                        WriteParameters(xtw, n.Value, ds, ref i, ref extData);

                        WriteElementEnd(xtw);
                    }

                    //返回值部分
                    if (data.Returns != null)
                    {
                        foreach (var n in data.Returns)
                        {
                            WriteReturnDefineBegin(xtw);
                            WriteParameterNameAttribute(xtw, n.Key);

                            WriteParameters(xtw, n.Value, ds, ref i, ref extData);

                            WriteElementEnd(xtw);
                        }
                    }

                    WriteElementEnd(xtw);
                    WriteElementEnd(xtw);

                    xtw.WriteEndDocument();

                    xtw.Flush();

                    ms.Write(BitConverter.GetBytes((int)tmpStream.Length), 0, 4);
                    ms.Write(tmpStream.ToArray(), 0, (int)tmpStream.Length);
                }

                //写附件
                if (ds.Tables.Count > 0)
                {
                    using (MemoryStream tmpStream = DataSetToMemoryStream(ds))
                    {
                        ms.Write(BitConverter.GetBytes(0), 0, 4);//DataTable数据类型标识
                        ms.Write(BitConverter.GetBytes((int)tmpStream.Length), 0, 4);
                        ms.Write(tmpStream.ToArray(), 0, (int)tmpStream.Length);
                    }
                }

                //写二进制附件
                foreach (var d in extData)
                {
                    ms.Write(BitConverter.GetBytes(1), 0, 4);//二进制数据类型标识
                    ms.Write(BitConverter.GetBytes(d.Key), 0, 4);
                    ms.Write(BitConverter.GetBytes(d.Value.Length), 0, 4);
                    ms.Write(d.Value, 0, d.Value.Length);
                }
            }

            return ms;
        }

        /// <summary>
        /// 将数据表集合转换为内存流
        /// </summary>
        /// <param name="dataSet">数据表集合</param>
        /// <returns>返回转换后的内存流</returns>
        internal static MemoryStream DataSetToMemoryStream(DataSet dataSet)
        {
            //创建内存流
            MemoryStream ms = new MemoryStream();

            //将数据以xml方式写入流
            dataSet.WriteXml(ms, XmlWriteMode.WriteSchema);

            //返回字节数组
            return ms;
        }
    }
}