using System.IO;
using DSPRE.ROMFiles;
using System;
using System.Windows.Forms;
using System.Text;
using System.Collections.Generic;
using static DSPRE.RomInfo;

namespace DSPRE {
    public class MoveData : RomFile {
        public enum AttackRange : ushort {
            SELECTION = (0),
            VARIABLE = (1 << 0),
            RANDOM = (1 << 1),
            BOTH = (1 << 2),
            FOES_AND_ALLY = (1 << 3),
            USER = (1 << 4),
            USER_SIDE = (1 << 5),
            ACTIVE_FIELD = (1 << 6),
            OPPONENTS_FIELD = (1 << 7),
            ALLY = (1 << 8),
            ACUPRESSURE = (1 << 9),
            ME_FIRST = (1 << 10)
        };
        public enum MoveSplit : byte {
            PHYSICAL = 0,
            SPECIAL,
            STATUS,
        };
        public enum ContestCondition : byte {
            COOL = 0,
            BEAUTIFUL,
            CUTE,
            SMART,
            TOUGH,
        };

        public enum MoveFlags : byte {
            CONTACT = (1 << 0),
            PROTECT = (1 << 1),
            MAGIC_COAT = (1 << 2),
            SNATCH = (1 << 3),
            MIRROR_MOVE = (1 << 4),
            KINGSROCK = (1 << 5),
            KEEP_HP_BAR = (1 << 6),
            DEL_SHADOW = (1 << 7)
        }


        public ushort battleeffect;
        public MoveSplit split;
        public byte damage;

        public PokemonType movetype;
        public byte accuracy;
        public byte pp;
        public byte sideEffectProbability;

        public ushort target;
        public sbyte priority;
        public byte flagField;

        public byte contestAppeal;
        public ContestCondition contestConditionType;

        public MoveData(Stream stream) {
            using (BinaryReader reader = new BinaryReader(stream)) {
                this.battleeffect = reader.ReadUInt16();
                this.split = (MoveSplit)reader.ReadByte();
                this.damage = reader.ReadByte();

                this.movetype = (PokemonType)reader.ReadByte();
                this.accuracy = reader.ReadByte();
                this.pp = reader.ReadByte();
                this.sideEffectProbability = reader.ReadByte();
                this.target = reader.ReadUInt16(); //bitfield
                this.priority = reader.ReadSByte();
                this.flagField = reader.ReadByte();

                this.contestAppeal = reader.ReadByte();
                this.contestConditionType = (ContestCondition)reader.ReadByte();
            }
        }

        public MoveData(int ID) : this(new FileStream(RomInfo.gameDirs[DirNames.moveData].unpackedDir + "\\" + ID.ToString("D4"), FileMode.Open)) { }

        public override byte[] ToByteArray() {
            using (MemoryStream memoryStream = new MemoryStream()) {
                using (BinaryWriter writer = new BinaryWriter(memoryStream)) {
                    writer.Write(this.battleeffect);
                    writer.Write((byte)this.split);
                    writer.Write(this.damage);

                    writer.Write((byte)this.movetype);
                    writer.Write(this.accuracy);
                    writer.Write(this.pp);
                    writer.Write(this.sideEffectProbability);
                    writer.Write((ushort)this.target);
                    writer.Write(this.priority);
                    writer.Write(this.flagField);

                    writer.Write(this.contestAppeal);
                    writer.Write((byte)this.contestConditionType);

                    writer.Write((ushort)0); //Filler
                }

                return memoryStream.ToArray();
            }
        }


        public void SaveToFileDefaultDir(int IDtoReplace, bool showSuccessMessage = true) {
            SaveToFileDefaultDir(DirNames.moveData, IDtoReplace, showSuccessMessage);
        }
        public void SaveToFileExplorePath(string suggestedFileName, bool showSuccessMessage = true) {
            SaveToFileExplorePath("Gen IV Move Data", "bin", suggestedFileName, showSuccessMessage);
        }

        public static void UpdateFromText(MoveData m, string[] split) {
            int target = 1;
            if (split.Length < target + 1) {
                return;
            } else {
                if (!Enum.TryParse(split[target], true, out m.movetype)) {
                    MessageBox.Show($"Malformed entry: \"{string.Join(" ", split)}\".\nMove type is unreadable: \"{split[target]}\"", "Parser error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
                target++;

                if (split.Length < target + 1) {
                    return;
                } else {
                    if (!Enum.TryParse(split[target], true, out m.split)) {
                        MessageBox.Show($"Malformed entry: \"{string.Join(" ", split)}\".\nMove split is unreadable: \"{split[target]}\"", "Parser error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        return;
                    }
                    target++;

                    if (split.Length < target + 1) {
                        return;
                    } else {
                        if (!byte.TryParse(split[target], out m.damage)) {
                            if (split[target].StartsWith("-") || split[target].StartsWith("—")) {
                                m.damage = 0;
                            } else {
                                MessageBox.Show($"Malformed entry: \"{string.Join(" ", split)}\".\nMove power is unreadable: \"{split[target]}\"", "Parser error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                                return;
                            }

                        }
                        target++;

                        if (split.Length < target + 1) {
                            return;
                        } else {
                            if (!byte.TryParse(split[target].Replace('%', ' '), out m.accuracy)) {
                                if (split[target].StartsWith("-") || split[target].StartsWith("—")) {
                                    m.accuracy = 0;
                                } else {
                                    MessageBox.Show($"Malformed entry: \"{string.Join(" ", split)}\".\nMove accuracy is unreadable: \"{split[target]}\"", "Parser error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                                    return;
                                }
                            }
                            target++;

                            if (split.Length < target + 1) {
                                return;
                            } else {
                                if (!byte.TryParse(split[target].Replace('%', ' '), out m.pp)) {
                                    MessageBox.Show($"Malformed entry: \"{string.Join(" ", split)}\".\nMove PP count is unreadable: \"{split[target]}\"", "Parser error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                                    return;
                                }
                            }
                        }
                    }
                }
            }

            return;
        }

        public override string ToString() {
            StringBuilder sb = new StringBuilder();
            sb.Append(movetype.ToString());
            sb.Append('\t');

            sb.Append(split.ToString());
            sb.Append('\t');

            if (damage == 0) {
                sb.Append("-");
            } else {
                sb.Append(damage);
            }
            sb.Append('\t');

            if (accuracy == 0) {
                sb.Append("-");
            } else {
                sb.Append(accuracy);
                sb.Append('%');
            }
            sb.Append('\t');

            sb.Append(pp);

            return sb.ToString();
        }

        public override bool Equals(object obj) {
            return obj is MoveData data &&
                   battleeffect == data.battleeffect &&
                   split == data.split &&
                   damage == data.damage &&
                   movetype == data.movetype &&
                   accuracy == data.accuracy &&
                   pp == data.pp &&
                   sideEffectProbability == data.sideEffectProbability &&
                   target == data.target &&
                   priority == data.priority &&
                   flagField == data.flagField &&
                   contestAppeal == data.contestAppeal &&
                   contestConditionType == data.contestConditionType;
        }

        public override int GetHashCode() {
            int hashCode = -1756630415;
            hashCode = hashCode * -1521134295 + battleeffect.GetHashCode();
            hashCode = hashCode * -1521134295 + split.GetHashCode();
            hashCode = hashCode * -1521134295 + damage.GetHashCode();
            hashCode = hashCode * -1521134295 + movetype.GetHashCode();
            hashCode = hashCode * -1521134295 + accuracy.GetHashCode();
            hashCode = hashCode * -1521134295 + pp.GetHashCode();
            hashCode = hashCode * -1521134295 + sideEffectProbability.GetHashCode();
            hashCode = hashCode * -1521134295 + target.GetHashCode();
            hashCode = hashCode * -1521134295 + priority.GetHashCode();
            hashCode = hashCode * -1521134295 + flagField.GetHashCode();
            hashCode = hashCode * -1521134295 + contestAppeal.GetHashCode();
            hashCode = hashCode * -1521134295 + contestConditionType.GetHashCode();
            return hashCode;
        }
    }
}