// Modules
using ModMenu;
using System.Net.Http;
using System.Threading.Tasks;
using System.Xml.Linq;
using System.Text;
using System.Drawing;
using DrawingColor = System.Drawing.Color;
using System.Net;
using System.Dynamic;
using System.Text.Json;
using System.IO;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Tga;
using NativeFileDialogSharp;

namespace Func {
    public class Player {
        public string nnidHex { get; set; }
        public uint PID { get; set; }
        public string NNID { get; set; }
        public string Name { get; set; }
    }

    public class SessionData {
        public uint SessionID { get; set; }
        public List<Player> Players { get; set; } = new List<Player>();
    }

    public class ExtFunc {
        public static async Task<string> GetNNID(int pid, bool isPretendo = true) {
            using (HttpClient client = new HttpClient()) {
                client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0");
                // Thanks @tombun2 for pointing this out
                // src: https://github.com/Milk-Cool/PretendoLookup/blob/932263e2c5e7f2e7527ff5dc690a8701dbf39885/miidata.js
                client.DefaultRequestHeaders.Add("X-Nintendo-Client-ID", "a2efa818a34fa16b8afbc8a74eba3eda");
                client.DefaultRequestHeaders.Add("X-Nintendo-Client-Secret", "c91cdb5658bd4954ade78533a339cf9a");

                try {
                    // src: https://github.com/kinnay/NintendoClients/wiki/Account-Server
                    if (isPretendo) {
                        HttpResponseMessage response = await client.GetAsync($"http://account.pretendo.cc/v1/api/miis?pids={pid}");
                        if (response.IsSuccessStatusCode) {
                            string content = await response.Content.ReadAsStringAsync();
                            XDocument doc = XDocument.Parse(content);
                            string userId = doc.Root?.Element("mii")?.Element("user_id")?.Value;
                            return userId;
                        } else {
                            return "0";
                        }   
                    } else {
                        HttpResponseMessage response = await client.GetAsync($"https://account.spfn.net/v1/api/admin/mapped_ids?input_type=pid&output_type=user_id&input={pid}");
                        if (response.IsSuccessStatusCode) {
                            string content = await response.Content.ReadAsStringAsync();
                            XDocument doc = XDocument.Parse(content);
                            string userId = doc.Root?.Element("mapped_id")?.Element("out_id")?.Value;
                            return userId;
                        } else {
                            return "0";
                        }
                    }
                }
                catch (Exception ex) {
                    Console.WriteLine(ex.Message);
                    return "0";
                }
            }
        }
        public static int FindPattern(byte[] data, byte[] pattern) {
            for (int i = 0; i <= data.Length - pattern.Length; i++) {
                bool match = true;
                for (int j = 0; j < pattern.Length; j++) {
                    if (data[i + j] != pattern[j]) {
                        match = false;
                        break;
                    }
                }

                if (match)
                    return i;
            }

            return -1;
        }
    }

    public class Codes {
        public static void DisconnectFromLobby() {
            Int16 timer = 0;
            var client_time_ptr = Program.readUInt32((uint)(Program.readUInt32((uint)(Program.readUInt32((uint)(Program.readUInt32(0x101E5660) + 0x28)) + 0x24)) + 0x40));
            Program.writeInt16((uint)(client_time_ptr + 0x1F8), timer);
        }
        public static void NameChanger(string name) {
            Console.WriteLine($"Changing Name to: {name}...");
            var name_pointer = Program.readUInt32(0x101E80A4);
            Program.writeBytes((uint)(name_pointer + 0x8C), new byte[32]);
            Program.writeBytes((uint)(name_pointer + 0x8C), Encoding.BigEndianUnicode.GetBytes(name));
        }
        public static string GetSessionData() {
            bool isPretendo = true;
            bool isSpfnTried = false;
            string nnidStr = null;
            httpMenu.session.SessionID = 0;
            httpMenu.session.Players.Clear();
            httpMenu.session = new SessionData();

            for (var i = 0; i < 8; i++) {
                var ptrToPlayerInfo = Program.readUInt32((uint)(Program.readUInt32((uint)(Program.readUInt32(0x101DD330) + 0x10)) + (uint)(i * 4))); // PlayerInfo Pointer
                var name = Encoding.BigEndianUnicode.GetString(Program.readBytes(ptrToPlayerInfo + 0x6, 32)).Replace("\n", "").Replace("\r", "").TrimEnd('\0');
                var pidRaw = Program.readUInt32(ptrToPlayerInfo + 0xd0);
                if (pidRaw != 0) {
                    nnidStr = ExtFunc.GetNNID((int)pidRaw, isPretendo).GetAwaiter().GetResult();
                } else {
                    nnidStr = "0";
                }
                if (nnidStr == "0" && !isSpfnTried && pidRaw != 0) {
                    nnidStr = ExtFunc.GetNNID((int)pidRaw, false).GetAwaiter().GetResult();
                    if (nnidStr != "0") {
                        isPretendo = false;
                    } else {
                        isSpfnTried = true;
                    }
                }
                string nnidHex = BitConverter.ToString(BitConverter.GetBytes(pidRaw)).Replace("-", "");

                httpMenu.session.Players.Add(new Player {
                    nnidHex = nnidHex,
                    PID = pidRaw,
                    NNID = nnidStr,
                    Name = name
                });
            }
            var ptr = Program.readUInt32(0x101E8980);
            uint sessionID;
            if (ptr != 0) {
                var index = Program.readBytes(ptr + 0xBD, 1)[0];
                sessionID = Program.readUInt32(ptr + index + 0xCC);
            } else {
                sessionID = 0;
            }
            httpMenu.session.SessionID = sessionID;

            string json = JsonSerializer.Serialize(httpMenu.session, new JsonSerializerOptions { WriteIndented = true });
            return json;
        }
        public static void forceSessiondId(uint sessionId) {
            // Console.WriteLine(sessionId);
            var p = Program.readUInt32(0x101DD330);
            Program.writeUInt32((uint)(p + 0x260), (uint)sessionId);
        }
        public static void faceimgtga(int format) {
            var p = Program.readUInt32(0x101DCDB0) + 0x150;
            var localPlayer = Program.readBytes(p, 5376);
            byte[] pattern = new byte[]{ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00 };
            int faceimg_offset = ExtFunc.FindPattern(localPlayer, pattern);
            if (faceimg_offset == -1) {
                Console.WriteLine("Could not find FaceImg.tga pattern!");
            } else {
                var faceimg = Program.readBytes((uint)(p + faceimg_offset), 65580);
                string path = null;
                string defaultPath = null;
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
                    defaultPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                } else {
                    defaultPath = Path.GetDirectoryName(Environment.ProcessPath);
                }

                if (format == 0) {
                    path = Dialog.FileSave("tga", defaultPath).Path;
                    if (string.IsNullOrEmpty(path)) return;
                    File.WriteAllBytes(path, faceimg);
                } else if (format == 1) {
                    path = Dialog.FileSave("png", defaultPath).Path;
                    if (string.IsNullOrEmpty(path)) return;
                    using (Image image = Image.Load(faceimg)) {
                        image.SaveAsPng(path);
                    }
                }
                if (string.IsNullOrEmpty(path)) return;
                // on *nix systems set file to be owned by everyone.
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
                    File.SetUnixFileMode(path, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute | UnixFileMode.GroupRead | UnixFileMode.GroupWrite | UnixFileMode.GroupExecute | UnixFileMode.OtherRead | UnixFileMode.OtherWrite | UnixFileMode.OtherExecute);
                }
            }
        }
        public static string TotalWins() {
            try {
                var p_SaveDataMgr = Program.readUInt32((uint)(Program.readUInt32(SaveEditor.SAVEDATAMGR_ROOT_PTR) + 0xC));
                return $"{Program.readUInt32((uint)(p_SaveDataMgr + 0xCB38))}";
            } catch {
                return "Game not fully loaded yet...";
            }
        }
        public static string TotalLoss() {
            try {
                var p_SaveDataMgr = Program.readUInt32((uint)(Program.readUInt32(SaveEditor.SAVEDATAMGR_ROOT_PTR) + 0xC));
                return $"{Program.readUInt32((uint)(p_SaveDataMgr + 0xCB3C))}";
            } catch {
                return "Game not fully loaded yet...";
            }
        }


        public static string EquippedHeadgear() {
            try {
                var p_SaveDataMgr = Program.readUInt32((uint)(Program.readUInt32(SaveEditor.SAVEDATAMGR_ROOT_PTR) + 0xC));
                return $"{Program.readUInt32((uint)(p_SaveDataMgr + 0x188))}";
            } catch {
                return "Game not fully loaded yet...";
            }
        }
        public static string EquippedClothes() {
            try {
                var p_SaveDataMgr = Program.readUInt32((uint)(Program.readUInt32(SaveEditor.SAVEDATAMGR_ROOT_PTR) + 0xC));
                return $"{Program.readUInt32((uint)(p_SaveDataMgr + 0x180))}";
            } catch {
                return "Game not fully loaded yet...";
            }
        }
        public static string EquippedShoes() {
            try {
                var p_SaveDataMgr = Program.readUInt32((uint)(Program.readUInt32(SaveEditor.SAVEDATAMGR_ROOT_PTR) + 0xC));
                return $"{Program.readUInt32((uint)(p_SaveDataMgr + 0x184))}";
            } catch {
                return "Game not fully loaded yet...";
            }
        }
        public static string EquippedWep() {
            try {
                var p_SaveDataMgr = Program.readUInt32((uint)(Program.readUInt32(SaveEditor.SAVEDATAMGR_ROOT_PTR) + 0xC));
                return $"{Program.readUInt32((uint)(p_SaveDataMgr + 0x18C))}";
            } catch {
                return "Game not fully loaded yet...";
            }
        }
    }
}
