using AxibugEmuOnline.Client.ClientCore;
using AxibugEmuOnline.Client.Tools;
using AxibugProtobuf;
using MAME.Core;
using System;
using System.IO;
using System.Runtime.InteropServices;

namespace AxibugEmuOnline.Client
{
    /// <summary> 存档文件管理类 </summary>
    public class SaveFile
    {
        public SavCloudApi CloudAPI => App.SavMgr.CloudApi;

        /// <summary> 指示该存档是否是自动存档 </summary>
        public bool AutoSave => SlotIndex == 0;
        /// <summary> 指示该存档所在槽位 </summary>
        public int SlotIndex { get; private set; }
        /// <summary> 指示该存档所属Rom的ID </summary>
        public int RomID { get; private set; }
        /// <summary> 指示该存档所属模拟器平台 </summary>
        public RomPlatformType EmuPlatform { get; private set; }

        /// <summary> 指示该存档是否为空 </summary>
        public bool IsEmpty { get; }
        /// <summary> 存档文件路径 </summary>
        public string FilePath
        {
            get
            {
                var path = App.PersistentDataPath(EmuPlatform);
                path = $"{path}/Slot/{EmuPlatform}/{RomID}";

                Directory.CreateDirectory(path);

                var filePath = $"{path}/slot{SlotIndex}.SlotSav";
                return filePath;
            }
        }

        /// <summary> 存档顺序号,用于判断云存档和本地存档的同步状态,是否存在冲突 </summary>
        public uint Sequecen { get; private set; }
        SimpleFSM<SaveFile> FSM;

        public SaveFile(int romID, RomPlatformType platform, int slotIndex)
        {
            RomID = romID;
            EmuPlatform = platform;
            SlotIndex = slotIndex;
            FSM = new SimpleFSM<SaveFile>(this);
            FSM.AddState<UnkownState>();
            FSM.AddState<CheckingState>();
            FSM.AddState<DownloadingState>();
            FSM.AddState<UploadingState>();
            FSM.AddState<SyncedState>();

            IsEmpty = File.Exists(FilePath);

            if (IsEmpty) Sequecen = 0;
            else //从文件头读取存储序号
            {
                byte[] saveOrderData = new byte[4];
                var streaming = File.OpenRead(FilePath);
                int res = streaming.Read(saveOrderData, 0, 4);
                if (res != 4) //无效的存档文件
                {
                    IsEmpty = true;
                    File.Delete(FilePath);
                }
                else
                {
                    Sequecen = BitConverter.ToUInt32(saveOrderData, 0);
                }

                streaming.Dispose();
            }

            FSM.ChangeState<UnkownState>();
        }

        public void Update()
        {
            FSM.Update();
        }
        public unsafe void GetSavData(out byte[] savData, out byte[] screenShotData)
        {
            savData = null;
            screenShotData = null;

            if (!File.Exists(FilePath)) return;

            var raw = File.ReadAllBytes(FilePath);
            int headerSize = Marshal.SizeOf(typeof(Header));

            if (raw.Length < headerSize)
            {
                App.log.Warning("无效存档");
                return;
            }

            var header = new Header();
            IntPtr ptr = Marshal.AllocHGlobal(headerSize);
            Marshal.StructureToPtr(header, ptr, false);
            Marshal.Copy(raw, 0, ptr, headerSize);
            Marshal.FreeHGlobal(ptr);

            savData = new byte[header.DataLength];
            Array.Copy(raw, headerSize, savData, 0, savData.Length);
            screenShotData = new byte[header.ScreenShotLength];
            Array.Copy(raw, headerSize + savData.Length, screenShotData, 0, screenShotData.Length);

            return;
        }

        public unsafe void Save(uint sequence, byte[] savData, byte[] screenShotData)
        {
            var filePath = FilePath;

            var header = new Header
            {
                Sequence = sequence,
                RomID = RomID,
                DataLength = savData.Length,
                ScreenShotLength = screenShotData.Length
            };
            int headerSize = Marshal.SizeOf(typeof(Header));
            IntPtr ptr = Marshal.AllocHGlobal(headerSize);

            var totalSize = headerSize + savData.Length + screenShotData.Length;
            byte[] raw = new byte[totalSize];

            try
            {
                Marshal.StructureToPtr(header, ptr, false);
                Marshal.Copy(ptr, raw, 0, headerSize);
            }
            finally
            {
                Marshal.FreeHGlobal(ptr);
            }
            Array.Copy(savData, 0, raw, headerSize, savData.Length);
            Array.Copy(screenShotData, 0, raw, headerSize + savData.Length, screenShotData.Length);

            File.WriteAllBytes(filePath, raw);
            Sequecen = sequence;
        }

        /// <summary>
        /// 尝试同步存档
        /// </summary>
        public void TrySync()
        {
            if (FSM.CurrentState is not UnkownState && FSM.CurrentState is not SyncedState) return;

            FSM.ChangeState<CheckingState>();
        }


        [StructLayout(LayoutKind.Explicit, Size = 16)]
        struct Header
        {
            [FieldOffset(0)]
            public uint Sequence;
            [FieldOffset(4)]
            public int RomID;
            [FieldOffset(8)]
            public int DataLength;
            [FieldOffset(12)]
            public int ScreenShotLength;
        }

        public enum EnumState
        {
            Unkown,
            Checking,
            Downloading,
            Uploading,
            Synced
        }
    }
}