// • ▌ ▄ ·.  ▄▄▄·  ▄▄ • ▪   ▄▄· ▄▄▄▄·  ▄▄▄·  ▐▄▄▄  ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀  █▪▀▀▀ ▀  ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀  ▀  ▀ ▀▀  █▪ ▀▀▀
//      Magicbane Emulator Project © 2013 - 2024
//                www.magicbane.com

package engine.wpak;

import engine.gameManager.ConfigManager;
import engine.mbEnums;
import engine.wpak.data.EffectDescription;
import engine.wpak.data.PowerActionEntry;
import engine.wpak.data.StatTransfer;
import org.pmw.tinylog.Logger;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class PowerActionParser {
    private static final Pattern STRSPLIT_REGEX = Pattern.compile("([^\"]\\S*|\"[^\"]*\")\\s*");
    private static final Pattern POWER_ACTION_REGEX = Pattern.compile("(?<=POWERACTIONBEGIN)(.+?)(?=POWERACTIONEND)", Pattern.DOTALL);
    private static final String powerActionPath = ConfigManager.DEFAULT_DATA_DIR + "wpak/PowerActions.cfg";

    public static void parseWpakFile() {

        // Read .wpak file from disk

        byte[] fileData;

        try {
            fileData = Files.readAllBytes(Paths.get(powerActionPath));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        String fileContents = new String(fileData);

        // Iterate over power entries from .wpak data

        Matcher matcher = POWER_ACTION_REGEX.matcher(fileContents);

        while (matcher.find()) {

            PowerActionEntry powerActionEntry = parsePowerActionEntry(matcher.group().trim());

        }
    }

    private static PowerActionEntry parsePowerActionEntry(String powerActionData) {

        PowerActionEntry powerActionEntry = new PowerActionEntry();
        EffectDescription effectDescription;
        StatTransfer statTransfer;

        try {
            // Remove all lines that contain a # and leading/trailing blank lines

            powerActionData = powerActionData.replaceAll("(?m)^(\\s*#.*|\\s*)\r?\n?", "").trim();

            String[] lineData = powerActionData.split("\n");

            // Parse effect entry header

            Iterator<String> entryIterator = Arrays.stream(lineData).iterator();

            String headerLine = entryIterator.next();
            ArrayList<String> headerData = new ArrayList<>();

            Matcher matcher = STRSPLIT_REGEX.matcher(headerLine.trim());

            while (matcher.find())
                headerData.add(matcher.group().trim());

            Iterator<String> headerIterator = headerData.iterator();
            powerActionEntry.action_id = headerIterator.next();
            powerActionEntry.action_type = headerIterator.next();

            switch (powerActionEntry.action_type) {
                case "RemoveEffect":
                    effectDescription = new EffectDescription();
                    effectDescription.effect_id = headerIterator.next();
                    powerActionEntry.effects.add(effectDescription);
                    break;
                case "CreateMob":
                    powerActionEntry.petLevel = Integer.parseInt(headerIterator.next());
                    powerActionEntry.petRace = Integer.parseInt(headerIterator.next());
                    break;
                case "DamageOverTime":
                    effectDescription = new EffectDescription();
                    effectDescription.effect_id = headerIterator.next();
                    effectDescription.cycleDuration = Integer.parseInt(headerIterator.next());
                    effectDescription.cycleDelay = Integer.parseInt(headerIterator.next());
                    powerActionEntry.effects.add(effectDescription);
                    break;
                case "Transform":
                case "Invis":
                case "ApplyEffect":
                case "ApplyEffects":
                case "DeferredPower":
                case "DirectDamage":
                    while (headerIterator.hasNext()) {
                        effectDescription = new EffectDescription();
                        effectDescription.effect_id = headerIterator.next();
                        effectDescription.level = Integer.parseInt(headerIterator.next());
                        powerActionEntry.effects.add(effectDescription);
                    }
                    break;
                case "TransferStat":
                    statTransfer = new StatTransfer();
                    statTransfer.fromStat = mbEnums.CostType.valueOf(headerIterator.next());
                    statTransfer.toStat = mbEnums.CostType.valueOf(headerIterator.next());
                    statTransfer.fromStatValue = Float.parseFloat(headerIterator.next());
                    statTransfer.fromCurve = mbEnums.CompoundCurveType.valueOf(headerIterator.next());
                    statTransfer.toStatValue = Float.parseFloat(headerIterator.next());
                    statTransfer.toCurve = mbEnums.CompoundCurveType.valueOf(headerIterator.next());
                    statTransfer.fromStatBool = Boolean.parseBoolean(headerIterator.next());
                    statTransfer.toStatBool = Boolean.parseBoolean(headerIterator.next());
                    powerActionEntry.statTransfer = statTransfer;
                    break;
                case "TransferStatOT":
                    statTransfer = new StatTransfer();
                    statTransfer.fromStat = mbEnums.CostType.valueOf(headerIterator.next());
                    statTransfer.toStat = mbEnums.CostType.valueOf(headerIterator.next());
                    statTransfer.fromStatValue = Float.parseFloat(headerIterator.next());
                    statTransfer.fromCurve = mbEnums.CompoundCurveType.valueOf(headerIterator.next());
                    statTransfer.toStatValue = Float.parseFloat(headerIterator.next());
                    statTransfer.toCurve = mbEnums.CompoundCurveType.valueOf(headerIterator.next());
                    statTransfer.fromStatBool = Boolean.parseBoolean(headerIterator.next());
                    statTransfer.toStatBool = Boolean.parseBoolean(headerIterator.next());
                    statTransfer.transfer_action = headerIterator.next();
                    statTransfer.transfer_ticks = Integer.parseInt(headerIterator.next());
                    powerActionEntry.statTransfer = statTransfer;
                    break;
                case "Charm":
                    effectDescription = new EffectDescription();
                    effectDescription.effect_id = headerIterator.next();
                    effectDescription.level = Integer.parseInt(headerIterator.next());
                    effectDescription.type = headerIterator.next();
                    powerActionEntry.effects.add(effectDescription);
                    break;
                case "Block":
                    effectDescription = new EffectDescription();
                    effectDescription.effect_id = headerIterator.next();
                    effectDescription.level = Integer.parseInt(headerIterator.next());
                    powerActionEntry.effects.add(effectDescription);
                    break;
                case "Resurrect":
                    powerActionEntry.levelCap = Integer.parseInt(headerIterator.next());
                    break;
                case "Recall": // No arguments for these tags or not parsed
                case "Teleport":
                case "TreeChoke":
                case "SimpleDamage":
                    break;
                default:
                    Logger.error("Unhandled type " + powerActionEntry.action_type + " for Pow4erAction: " + powerActionEntry.action_id);
                    break;
            }

            // Process key value pairs after header

            while (entryIterator.hasNext()) {
                String lineValue = entryIterator.next();
                String[] lineValues = lineValue.split("=");
                String key = lineValues[0].trim();
                String[] arguments;

                switch (key) {
                    case "BODYPARTS":
                        arguments = lineValues[1].trim().split("\\s+");

                        for (String bodyPart : arguments)
                            powerActionEntry.bodyparts.add(Integer.parseInt(bodyPart));
                        break;
                    case "FEMALEBODYPARTS":
                        arguments = lineValues[1].trim().split("\\s+");

                        for (String bodyPart : arguments)
                            powerActionEntry.femaleBodyParts.add(Integer.parseInt(bodyPart));
                        break;
                    case "SCALEFACTOR":
                        arguments = lineValues[1].trim().split("\\s+");

                        for (String scaleFactor : arguments)
                            powerActionEntry.scaleFactor.add(Float.parseFloat(scaleFactor));
                        break;
                    case "ISRESISTABLE":
                        powerActionEntry.isResistible = Boolean.parseBoolean(lineValues[1].trim());
                        break;
                    case "ISAGGRESSIVE":
                        powerActionEntry.isAggressive = Boolean.parseBoolean(lineValues[1].trim());
                        break;
                    case "BLADETRAILS":
                        powerActionEntry.bladeTrails = Boolean.parseBoolean(lineValues[1].trim());
                        break;
                    case "SHOULDSHOWWEAPONS":
                        powerActionEntry.shouldShowWeapons = Boolean.parseBoolean(lineValues[1].trim());
                        break;
                    case "SHOULDSHOWARMOR":
                        powerActionEntry.shouldShowArmor = Boolean.parseBoolean(lineValues[1].trim());
                        break;
                    case "APPLYEFFECTBLANK":
                        powerActionEntry.applyEffectBlank = Boolean.parseBoolean(lineValues[1].trim());
                        break;
                    case "WEAROFFEFFECTBLANK":
                        powerActionEntry.wearOffEffectBlank = Boolean.parseBoolean(lineValues[1].trim());
                        break;
                    case "ATTACKANIMS":
                        arguments = lineValues[1].trim().split("\\s+");

                        for (String animation : arguments)
                            powerActionEntry.attackAnimations.add(Integer.parseInt(animation));
                        break;
                    case "REMOVEALL":
                        powerActionEntry.removeAll = Boolean.parseBoolean(lineValues[1].trim());
                        break;
                    case "EFFECTID":
                        effectDescription = new EffectDescription();
                        effectDescription.effect_id = lineValues[1].trim();
                        powerActionEntry.effects.add(effectDescription);
                        break;
                    case "LEVELCAP":
                        arguments = lineValues[1].trim().split("\\s+");
                        powerActionEntry.levelCap = Integer.parseInt(arguments[0]);

                        if (arguments.length > 1)  // Not all level caps have a curve
                            powerActionEntry.levelCurve = mbEnums.CompoundCurveType.valueOf(arguments[1]);
                        break;
                    case "CLEARAGGRO":
                        powerActionEntry.clearAggro = Boolean.parseBoolean(lineValues[1].trim());
                        break;
                    case "TARGETBECOMESPET":
                        powerActionEntry.targetBecomesPet = Boolean.parseBoolean(lineValues[1].trim());
                        break;
                    case "DESTROYOLDPET":
                        powerActionEntry.destroyOldPet = Boolean.parseBoolean(lineValues[1].trim());
                        break;
                    case "APPLYEFFECTOTHER":
                    case "APPLYEFFECTSELF":
                    case "WEAROFFEFFECTOTHER": // Keys not parsed go here.
                    case "WEAROFFEFFECTSELF":
                        break;
                    default:
                        Logger.error("Unhandled variable type:" + key + " for powerAction: " + powerActionEntry.action_id);
                }

            }
        } catch (Exception e) {
            Logger.error(powerActionEntry.action_id + " " + e);
        }
        return powerActionEntry;
    }
}