using System;

namespace PKHeX
{
    internal static class PKMConverter
    {
        internal static int Country = 31;
        internal static int Region = 7;
        internal static int ConsoleRegion = 1;
        internal static string OT_Name = "PKHeX";
        internal static int OT_Gender;
        
        internal static void updateConfig(int SUBREGION, int COUNTRY, int _3DSREGION, string TRAINERNAME, int TRAINERGENDER)
        {
            Region = SUBREGION;
            Country = COUNTRY;
            ConsoleRegion = _3DSREGION;
            OT_Name = TRAINERNAME;
            OT_Gender = TRAINERGENDER;
        }

        private static int getPKMDataFormat(byte[] data)
        {
            if (!PKX.getIsPKM(data.Length))
                return -1;

            switch (data.Length)
            {
                case PKX.SIZE_3PARTY:
                case PKX.SIZE_3STORED:
                    return 3;
                case PKX.SIZE_4PARTY:
                case PKX.SIZE_4STORED:
                case PKX.SIZE_5PARTY:
                    if ((BitConverter.ToUInt16(data, 0x80) >= 0x3333 || data[0x5F] >= 0x10) && BitConverter.ToUInt16(data, 0x46) == 0) // PK5
                        return 5;
                    return 4;
                case PKX.SIZE_6STORED:
                    return 6;
                case PKX.SIZE_6PARTY: // collision with PGT, same size.
                    if (BitConverter.ToUInt16(data, 0x4) != 0) // Bad Sanity?
                        return -1;
                    if (BitConverter.ToUInt32(data, 0x06) == PKX.getCHK(data))
                        return 6;
                    if (BitConverter.ToUInt16(data, 0x58) != 0) // Encrypted?
                    {
                        for (int i = data.Length - 0x10; i < data.Length; i++) // 0x10 of 00's at the end != PK6
                            if (data[i] != 0)
                                return 6;
                        return -1;
                    }
                    return 6;
            }
            return -1;
        }
        internal static PKM getPKMfromBytes(byte[] data, string ident = null)
        {
            checkEncrypted(ref data);
            switch (getPKMDataFormat(data))
            {
                case 3:
                    return new PK3(data, ident);
                case 4:
                    return new PK4(data, ident);
                case 5:
                    return new PK5(data, ident);
                case 6:
                    return new PK6(data, ident);
                default:
                    return null;
            }
        }
        internal static PKM convertToFormat(PKM pk, int Format, out string comment)
        {
            if (pk == null)
            {
                comment = "Null input. Aborting.";
                return null;
            }
            if (pk.Format == Format)
            {
                comment = "No need to convert, current format matches requested format.";
                return pk;
            }
            if (pk.Format > Format)
            {
                comment = "Cannot convert a PKM backwards." + Environment.NewLine
                          + "Current Format: " + pk.Format + Environment.NewLine
                          + "Desired Format: " + Format;
                return null;
            }
            string currentFormat = pk.Format.ToString();
            PKM pkm = pk.Clone();
            if (pkm.IsEgg) // force hatch
            {
                pkm.IsEgg = false;
                if (pkm.AO)
                    pkm.Met_Location = 318; // Battle Resort
                else if (pkm.XY)
                    pkm.Met_Location = 38; // Route 7
                else if (pkm.Gen5)
                    pkm.Met_Location = 16; // Route 16
                else
                    pkm.Met_Location = 30001; // Pokétransfer
            }
            if (pkm.Format == 3 && Format > 3)
                pkm = (pkm as PK3).convertToPK4();
            if (pkm.Format == 4 && Format > 4)
                pkm = (pkm as PK4).convertToPK5();
            if (pkm.Format == 5 && Format > 5)
                pkm = (pkm as PK5).convertToPK6();
            comment = $"Converted from pk{currentFormat} to pk{Format}";
            return pkm;
        }
        internal static void checkEncrypted(ref byte[] pkm)
        {
            int format = getPKMDataFormat(pkm);
            ushort chk = 0;
            switch (format)
            {
                case 3: // TOneverDO, nobody exports encrypted pk3s
                    return;
                case 4:
                case 5:
                    for (int i = 8; i < PKX.SIZE_4STORED; i += 2)
                        chk += BitConverter.ToUInt16(pkm, i);
                    if (chk != BitConverter.ToUInt16(pkm, 0x06))
                        pkm = PKX.decryptArray45(pkm);
                    return;
                case 6:
                    if (BitConverter.ToUInt16(pkm, 0xC8) != 0 && BitConverter.ToUInt16(pkm, 0x58) != 0)
                        pkm = PKX.decryptArray(pkm);
                    return;
                default:
                    return; // bad!
            }
        }
    }
}