using UnityEngine;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;

public enum AdRewardType {
    GachaCoin   = 1,
    Avatar      = 2,
    Count,
}

[DataContract(Name="KeyValueOfintint")]
public sealed class KeyValueOfintint {
    [DataMember]
    public int Key;
    [DataMember]
    public int Value;

    public KeyValueOfintint(KeyValuePair<int, int> value){
        Key = value.Key;
        Value = value.Value;
    }
}

[DataContract]
public sealed class GameData {
    [DataMember(Name = "Data0")]
    public long beginPlayTime = DateTime.UtcNow.ToBinary();
    [DataMember(Name = "Data1")]
    public string lastPlayedVersion = Const.DefaultAppVersion;
    [DataMember(Name = "Data2")]
    public long lastPlayTime = DateTime.UtcNow.ToBinary(); // 最終プレイ日時
    [DataMember(Name = "Data3")]
    public int dailyLoginCount = 0;
    [DataMember(Name = "Data4")]
    public int adCount;
    [DataMember(Name = "Data5")]
    public int stage = 1;
    // [DataMember(Name = "Data6")]
    // public int avatarId = Const.DefaultAvatarId;
    [DataMember(Name = "Data7")]
    public int coin;
    // [DataMember(Name = "Data8")]
    // private int[] avatarIdArray;
    // public List<int> avatarIdList;
    // [DataMember(Name = "Data9")]
    // public int gachaCount;
    // [DataMember(Name = "Data10")]
    // private int[] newAvatarIdArray;
    // public List<int> newAvatarIdList;
    // [DataMember(Name = "Data11")]
    // public bool isRandomAvatar;
    // CornField
    [DataMember(Name = "Data12")] 
    public List<PlantLineData> PlantLines = new List<PlantLineData>();
    // [DataMember(Name = "Data13")] 
    [DataMember(Name = "Data14")] 
    public int MachineLevel = 1;
    [DataMember(Name = "Data15")] 
    public List<SeedlingProgressData> SeedlingDataList = new List<SeedlingProgressData>();
    [DataMember(Name = "Data16")] 
    public int cornSeed;
    // Main
    // 所持レシピ
    [DataMember(Name = "Data17")] 
    public int[] MyRecipes;
    // 所持素材
    [DataMember(Name = "Data18")] 
    public List<MaterialData> Material = new List<MaterialData>();
    // 店頭ポップコーン在庫
    [DataMember(Name = "Data19")]
    private int[] shopStock;
    public List<int> ShopStock;
    // タンクポップコーン在庫
    [DataMember(Name = "Data20")]
    public List<StorageTank> StorageTanks;
    
    

    // public void ChangeAvatar(AvatarData avatarData){
    //     newAvatarIdList.Remove(avatarData.id);
    // }
    // public void AddAvatar(AvatarData avatarData){
    //     if(HasAvatar(avatarData))    return ;
    //     avatarIdList.Add(avatarData.id);
    //     newAvatarIdList.Add(avatarData.id);
    // }
    // public bool HasAvatar(AvatarData avatarData){
    //     return avatarIdList.Exists(id => id == avatarData.id);
    // }
    // public void SetRandom(bool isRandom, AvatarCategory category){
    // }
    // public bool GetRandom(AvatarCategory category){
    //     return false;
    // }



//    [OnDeserialized] Unityだと機能しない
    public void OnDeserialized(){
        PreDeserialize();

        // if(avatarIdList.Count < 1){
        //     avatarIdList.Add(Const.DefaultAvatarId);
        // }
        // 追加した要素の初期化用
        // TODO Release前にまっさらにする
    }
    public void PreDeserialize(){
        // avatarIdList = avatarIdArray == null ? new List<int>() : avatarIdArray.ToList();
        // newAvatarIdList = newAvatarIdArray == null ? new List<int>() : newAvatarIdArray.ToList();
        // lastAdRewardTimeList = lastAdRewardTimeArray == null ? new List<long>() : lastAdRewardTimeArray.ToList();
        ShopStock = shopStock?.ToList() ?? new List<int>();
    }
    private Dictionary<int, int> ArrayToDictionary(KeyValueOfintint[] array){
        var dictionary = new Dictionary<int, int>();
        if(array != null && array.Length > 0){
            foreach(var kv in array){
                dictionary.Add(kv.Key, kv.Value);
            }
        }
        return dictionary;
    }
    public void PreSerialize(){
        // avatarIdArray = avatarIdList.ToArray();
        // newAvatarIdArray = newAvatarIdList.ToArray();
        // lastAdRewardTimeArray = lastAdRewardTimeList.ToArray();
        shopStock = ShopStock.ToArray();
    }
    private KeyValueOfintint[] DictionaryToArray(Dictionary<int, int> dictionary){
        var array = new KeyValueOfintint[dictionary.Count];
        int index = 0;
        foreach(var kvp in dictionary){
            array[index++] = new KeyValueOfintint(kvp);
        }
        return array;
    }
}

public static class GameDataManager {

    public enum LoadState {
        NoFile,
        ExistsClashed,
        DeserializeFailed,
        Old,
        Succeeded,
    }

//    private static readonly int DiskFullTextId = 201;
//    private static readonly int UnauthorizedAccessExceptionTextId = 202;

    private static GameData dataCache = null;
    public static GameData GameData {
        get{ return dataCache ?? (dataCache = LoadGameData()); }
    }
    public static bool IsLoaded {
        get{ return dataCache != null; }
    }
    public static bool ExistsGameData(){
        return UsayaStorageManager.Exists(UsayaStorageFilename.Main_Data, Const.GameDataTag) || UsayaStorageManager.ExistsBackup(UsayaStorageFilename.Main_Data, Const.GameDataTag);
    }
    private static GameData LoadGameData(){
        GameData data = null;
        var loadState = LoadState.ExistsClashed;
        try{
            if(UsayaStorageManager.Exists(UsayaStorageFilename.Main_Data, Const.GameDataTag)){
                loadState = LoadState.DeserializeFailed;
                data = StringToGameData(UsayaStorageManager.Load<string>(UsayaStorageFilename.Main_Data, Const.GameDataTag));
                loadState = LoadState.Succeeded;
            }else{
                loadState = LoadState.NoFile;
            }
        }catch(Exception){
        }finally{
            var backup = LoadGameDataBackup(loadState);
            if(loadState != LoadState.Succeeded || (data.stage < backup.stage)){
                data = backup;
                if(loadState == LoadState.Succeeded){
//                    AnalyticsManager.DataLoadWarning(LoadState.Old);
                }
            }
        }
        data.OnDeserialized();

        if(Application.version.CompareVersion(data.lastPlayedVersion) != 0){
            data.lastPlayedVersion = Application.version;
        }
        return data;
    }
    private static GameData LoadGameDataBackup(LoadState mainLoadState){
        GameData result = null;
        var loadState = LoadState.ExistsClashed;
        try{
            if(UsayaStorageManager.ExistsBackup(UsayaStorageFilename.Main_Data, Const.GameDataTag)){
                loadState = LoadState.DeserializeFailed;
                result = StringToGameData(UsayaStorageManager.LoadBackup(UsayaStorageFilename.Main_Data, Const.GameDataTag));
                loadState = LoadState.Succeeded;
                if(mainLoadState != LoadState.Succeeded){
//                    AnalyticsManager.DataLoadWarning(mainLoadState);
                }
            }else{
                loadState = LoadState.NoFile;
            }
        }catch(Exception){
        }finally{
            if(mainLoadState != LoadState.Succeeded && loadState != LoadState.NoFile){
//                AnalyticsManager.DataLoadWarning(mainLoadState, loadState);
            }
            if(loadState != LoadState.Succeeded){
                result = CreateData();
            }
        }
        return result;
    }

    private static GameData CreateData(){
        var data = new GameData();
        data.lastPlayedVersion = Application.version;
        return data;
    }
    public static bool OverWriteGameData(string dataString){
        try{
            dataCache = StringToGameData(dataString);
            return true;
        }catch{
            return false;
        }
    }
    public static void SaveGameData(){
        string data = GameDataToString(GameData);
        bool isMainSaved = false;
        Exception exceptionMain = null;
        try{
            UsayaStorageManager.Save<string>(UsayaStorageFilename.Main_Data, Const.GameDataTag, data);
            isMainSaved = true;
        }catch(Exception e){
            // TODO ログサーバが出来たらExceptionを送りたい
            exceptionMain = e;
        }finally{
            bool isBackupSaved = false;
            Exception exceptionBackup = null;
            try{
                UsayaStorageManager.SaveBackup(UsayaStorageFilename.Main_Data, Const.GameDataTag, data);
                isBackupSaved = true;
            }catch(Exception e){
                // TODO ログサーバが出来たらExceptionを送りたい
                exceptionBackup = e;
            }finally{
                if(!isMainSaved && !isBackupSaved){
//                    AnalyticsManager.DataSaveWarning("GameData", SaveState.MainAndBackupFailed);
//                    SaveFailedDialogManager.ShowDialog(exceptionMain, exceptionBackup);
                }else if(!isMainSaved){
//                    AnalyticsManager.DataSaveWarning("GameData", SaveState.MainFailed);
                }else if(!isBackupSaved){
//                    AnalyticsManager.DataSaveWarning("GameData", SaveState.BackupFailed);
                }
            }
        }
    }
    public static string GetGameDataString(){
        return GameDataToString(GameData);
    }
    private static string GameDataToString(GameData data){
        data.PreSerialize();
        return AvoEx.AesEncryptor.Encrypt(
            StringExtensions.DataToXML<GameData>(data)
            .Replace("KeyValueOfintint", "KVOii")
            .Replace("KeyValueOfintlong", "KVOil")
            .Replace("KeyValueOfintInfiniteFloat", "KVOiIF")
//            .Compression()
        );
    }
    private static GameData StringToGameData(string str){
        str = AvoEx.AesEncryptor.DecryptString(str)
//            .DeCompression()
            .Replace("KVOii", "KeyValueOfintint")
            .Replace("KVOil", "KeyValueOfintlong")
            .Replace("KVOiIF", "KeyValueOfintInfiniteFloat");
        return StringExtensions.XMLToData<GameData>(str);
    }

#if UNITY_EDITOR || DEVELOPMENT_BUILD
    public static void ResetData(){
        dataCache = CreateData();
        dataCache.OnDeserialized();
    }
#endif
}
