using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using UnityEngine;

/// <summary>
/// 二进制文件管理类
/// 电脑端逻辑 ,webgl不适用读取
/// BitConverter.GetBytes   Encoding.UTF8.GetBytes
/// 序列化自定义类 需要加特性 [System.Serializable]
/// </summary>
public class BinaryManager : BaseManager<BinaryManager>
{
    private BinaryManager() { InitData(); }
    /// <summary>
    /// 文件默认存储路径
    /// </summary>
    private static readonly string SavePath = Application.persistentDataPath + "/Data/";

    /// <summary>
    /// 二进制数据
    /// </summary>
    public static string BinaryDataPath = Application.streamingAssetsPath + "/BinaryData/";

    /// <summary>
    /// 用于存储所有表
    /// </summary>
    private readonly Dictionary<string, object> tableDic = new Dictionary<string, object>();

    /// <summary>
    /// 加载二进制配置表数据
    /// </summary>
    public void InitData()
    {
        LoadTable<TB_SchemeContainer, TB_Scheme>();
        LoadTable<TB_ProcessContainer, TB_Process>();
        LoadTable<TB_SubProcessContainer, TB_SubProcess>();
        LoadTable<TB_SubProcessStepContainer, TB_SubProcessStep>();
        LoadTable<TB_SystemContainer, TB_System>();
        LoadTable<TB_DeviceTypeContainer, TB_DeviceType>();
        LoadTable<TB_ToolAndMaterialContainer, TB_ToolAndMaterial>();
    }

    /// <summary>
    /// 加载Excel表的2进制数据到内存中
    /// </summary>
    /// <typeparam name="T">容器类名</typeparam>
    /// <typeparam name="K">数据结构类名</typeparam>
    private void LoadTable<T, K>()
    {
        //读取二进制文件 解析
        using FileStream fs = File.Open(BinaryManager.BinaryDataPath + typeof(K).Name + ".binary", FileMode.Open, FileAccess.Read);
        byte[] data = new byte[fs.Length];
        fs.Read(data, 0, data.Length);
        fs.Close();

        int index = 0;
        //读取多少行
        int count = BitConverter.ToInt32(data, index);
        index += 4;

        //读取主键变量名 字符串长度
        int keyNameLength = BitConverter.ToInt32(data, index);
        index += 4;

        //读取主键变量名
        string keyName = Encoding.UTF8.GetString(data, index, keyNameLength);
        index += keyNameLength;

        Type contaninerType = typeof(T);
        object contaninerObj = Activator.CreateInstance(contaninerType);

        //得到数据结构类的Type
        Type classType = typeof(K);

        //通过反射得到数据结构类 所有字段的信息
        FieldInfo[] infos = classType.GetFields();

        //读取每一行的信息
        for (int i = 0; i < count; i++)
        {
            object dataObj = Activator.CreateInstance(classType);
            foreach (FieldInfo fi in infos)
            {
                if (fi.FieldType == typeof(int))
                {
                    //将2进制数据转为int 然后给对应字段赋值
                    fi.SetValue(dataObj, BitConverter.ToInt32(data, index));
                    index += 4;
                }
                else if (fi.FieldType == typeof(bool))
                {
                    //将2进制数据转为int 然后给对应字段赋值
                    fi.SetValue(dataObj, BitConverter.ToBoolean(data, index));
                    index += 1;
                }
                else if (fi.FieldType == typeof(float))
                {
                    //将2进制数据转为int 然后给对应字段赋值
                    fi.SetValue(dataObj, BitConverter.ToSingle(data, index));
                    index += 4;
                }
                else if (fi.FieldType == typeof(string))
                {
                    int length = BitConverter.ToInt32(data, index);
                    index += 4;
                    fi.SetValue(dataObj, Encoding.UTF8.GetString(data, index, length));
                    index += length;
                }
            }
            //读取完一行的数据了 应该把这个数据添加到容器对象中
            //得到容器对象中的 字典对象
            object dicObj = contaninerType.GetField("dataDic").GetValue(contaninerObj);

            //通过字典对象得到其中的Add方法
            MethodInfo methodInfo = dicObj.GetType().GetMethod("Add");
            //
            object keyValue = classType.GetField(keyName).GetValue(dataObj);
            //Debug.Log(keyValue);
            methodInfo.Invoke(dicObj, new object[] { keyValue, dataObj });
        }
        //把读取完的表记录下来
        tableDic.Add(typeof(T).Name, contaninerObj);
    }

    /// <summary>
    /// 得到一张表的数据
    /// </summary>
    /// <typeparam name="T">容器</typeparam>
    /// <returns></returns>
    public T GetTable<T>()where T : class
    {
        string tableName =typeof(T).Name;

        if (tableDic.ContainsKey(tableName))
            return tableDic[tableName] as T;
        return null;
    }

    /// <summary>
    /// 保存数据
    /// </summary>
    /// <param name="data"></param>
    /// <param name="fileName"></param>
    public void SaveData(object data, string fileName)
    {
        if (!Directory.Exists(SavePath))
            Directory.CreateDirectory(SavePath);
        using FileStream fs = new FileStream(SavePath + fileName + ".binary", FileMode.OpenOrCreate, FileAccess.Write);
        BinaryFormatter binaryFormatter = new BinaryFormatter();
        binaryFormatter.Serialize(fs, data);
        fs.Flush();
        fs.Close();
        fs.Dispose();
    }

    /// <summary>
    /// 读取数据 返回
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="fileName"></param>
    /// <returns></returns>
    public T LoadData<T>(string fileName)where T : class,new()
    {
        
        if (!File.Exists(SavePath + fileName + ".binary"))
        {
            return default;
        }
        T data = new T();
        using (FileStream fs = File.Open(SavePath + fileName + ".binary",FileMode.Open,FileAccess.Read))
        {
            BinaryFormatter binaryFormatter =new BinaryFormatter();
            data = binaryFormatter.Deserialize(fs) as T;
            fs.Close();
            fs.Dispose();
        }
        return data;
    }

}