using McCrypt;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace PremiumPackInstaller
{
    public partial class MainWindow : Form
    {
        public Dictionary<String, String> SkinpacksList = new Dictionary<String, String>();
        public Random rng = new Random();

        string LocalAppdata;     
        string MinecraftFolder;  
        string LocalState;       
        string PremiumCache;     
        string PremiumSkins;     

        public MainWindow()
        {
            LocalAppdata = Environment.GetEnvironmentVariable("LOCALAPPDATA");
            MinecraftFolder = Path.Combine(LocalAppdata, "Packages", "Microsoft.MinecraftUWP_8wekyb3d8bbwe");
            LocalState = Path.Combine(MinecraftFolder, "LocalState");
            PremiumCache = Path.Combine(LocalState, "premium_cache");
            PremiumSkins = Path.Combine(PremiumCache, "skin_packs");


            InitializeComponent();
        }

        private void LoginPlayfab()
        {
            Task.Run(() =>
            {
                try
                {
                    PlayFab.PlayFab.PullEntityTokenOutOfMyAss();
                    RefreshItemList();
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            });
        }

        private string FindInPremiumCache(string uuid)
        {
            foreach(string premiumSkinFolder in Directory.GetDirectories(PremiumSkins, "*", SearchOption.TopDirectoryOnly))
            {
                try
                {
                    string manifestJson = Path.Combine(premiumSkinFolder, "manifest.json");
                    if (Manifest.ReadUUID(manifestJson) == uuid)
                        return premiumSkinFolder;
                }
                catch (Exception)
                {
                    continue;
                }
            }

            byte[] randomBytes = new byte[8];
            rng.NextBytes(randomBytes);
            string name = Convert.ToBase64String(randomBytes);
            
            if (Directory.Exists(name))
                return FindInPremiumCache(uuid);

            return Path.Combine(PremiumSkins, name);
        }

        private void RefreshPlayfab()
        {
            SkinpacksList.Clear();

            dynamic results = PlayFab.PlayFab.Search("", "((contentType eq 'MarketplaceDurableCatalog_V1.2' and contents/any(c: c/minClientVersion le '" + clientVersion.Text + "' and c/maxClientVersion gt '" + clientVersion.Text + "'))) and platforms/any(tp: tp eq 'uwp.store' and tp eq 'title.bedrockvanilla') and (tags/any(t: t eq 'skinpack' or t eq 'mashup' or t eq 'has_skinpack')) and not tags/any(t: t eq 'hidden_offer' or t eq 'realms_plus')", 0, "startDate desc");
            foreach (dynamic result in results.Items)
            {

                JObject resultTitle = result.Title;
                if (resultTitle.First == null)
                    continue;

                if (resultTitle.First.First == null)
                    continue;

                string packTitle = resultTitle.First.First.ToString();

                if (result.DisplayProperties != null)
                    if (result.DisplayProperties.packIdentity != null)
                        foreach (dynamic identity in result.DisplayProperties.packIdentity)
                        {
                            if (identity.type.ToString() == "skinpack" || identity.type.ToString() == "skin_pack")
                            {
                                string packUuid = identity.uuid.ToString();
                                if(!SkinpacksList.ContainsKey(packUuid))
                                    SkinpacksList.Add(packUuid, packTitle);
                            }
                        }
            }
        }

        private void RefreshItemList()
        {
            Task.Run(() =>
            {
                try
                {

                    this.Invoke((Action)delegate { replacePack.Enabled = false; });
                    this.Invoke((Action)delegate { refreshButton.Enabled = false; });
                    this.Invoke((Action)delegate { getMoreSkinsList.Items.Clear(); });
                    RefreshPlayfab();
                    foreach(KeyValuePair<String, String> SkinPack in SkinpacksList)
                    {
                        string skinsListEntry = SkinPack.Value;

                        string manifestFile = Path.Combine(FindInPremiumCache(SkinPack.Key), "manifest.json");
                        if (File.Exists(manifestFile))
                        {
                            try
                            {
                                string installedName = Manifest.ReadName(manifestFile);
                                if (installedName.Trim() != SkinPack.Value.Trim())
                                    skinsListEntry += " => " + installedName;
                            }
                            catch (Exception) { };
                        }

                        this.Invoke((Action)delegate { getMoreSkinsList.Items.Add(skinsListEntry); });
                        this.Invoke((Action)delegate { getMoreSkinsList.SelectedIndex = 0; });
                    }
                    this.Invoke((Action)delegate { refreshButton.Enabled = true; });
                    this.Invoke((Action)delegate { replacePack.Enabled = true; });
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            });
        }
        private void MainWindow_Load(object sender, EventArgs e)
        {
            string verStr = PlayFab.Config.GetConfValue("ClientVersion");
            if(verStr == null)
            {
                verStr = "1.19.20";
                PlayFab.Config.WriteConfValue("ClientVersion", verStr);
            }
            
            clientVersion.Text = verStr;

            LoginPlayfab();
        }

        private void refreshButton_Click(object sender, EventArgs e)
        {
            RefreshItemList();
        }

        private void replacePack_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = "Minecraft Skin Pack|*.mcpack";
            ofd.Title = "Select a MCPack File";
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    string mcpack = ofd.FileName;
                    string uuid = SkinpacksList.ElementAt(getMoreSkinsList.SelectedIndex).Key;
                    string contentKey = "s5s5ejuDru4uchuF2drUFuthaspAbepE";

                    string skinPackPath = FindInPremiumCache(uuid);
                    if (Directory.Exists(skinPackPath))
                        Directory.Delete(skinPackPath, true);
                    Directory.CreateDirectory(skinPackPath);

                    ZipFile.ExtractToDirectory(mcpack, skinPackPath);
                    string manifestJson = Path.Combine(skinPackPath, "manifest.json");

                    Manifest.ChangeUUID(manifestJson, uuid);
                    Manifest.SignManifest(skinPackPath);
                    Marketplace.EncryptContents(skinPackPath, uuid, contentKey);
                    RefreshItemList();
                    MessageBox.Show("Pack replaced!", "Done", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                catch(Exception ex)
                {
                    MessageBox.Show("Pack replace fail: "+ex.Message, "Fail", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }

            }
        }

        private void clientVersion_TextChanged(object sender, EventArgs e)
        {
            while (true)
            {
                try
                {
                    PlayFab.Config.WriteConfValue("ClientVersion", clientVersion.Text);
                    break;
                }
                catch (Exception) { };
            }
        }
    }
}
