package lc.dnd.cardmaker;

import lc.mecha.json.JSONArray;
import lc.mecha.json.JSONObject;
import lc.mecha.log.MechaLogger;
import lc.mecha.log.MechaLoggerFactory;
import lc.mecha.util.StringAccumulatorV2;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;

import java.io.*;
import java.util.Properties;

/**
 * This note generator works based on the 5ETools database. It generates markdown formatted notes/cards for use
 * by players or a DM.
 *
 * @author Alex Leigh
 */
public class FifthEdSpellNoteMakerCmd {
    private static final MechaLogger logger = MechaLoggerFactory.getLogger(FifthEdSpellNoteMakerCmd.class);

    public static void main(String[] args) throws IOException {
        makeCards("/spells-phb.json");
        makeCards("/spells-scag.json");
        makeCards("/spells-xge.json");
    }

    public static void makeCards(String filename) throws IOException {
        var is = logger.getClass().getResourceAsStream(filename);
        var doc = new JSONObject(is);

        File output = new File("output");
        //noinspection ResultOfMethodCallIgnored
        output.mkdirs();

        Properties props = new Properties();
        props.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
        props.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
        var velocityEngine = new VelocityEngine(props);
        velocityEngine.init();
        var t = velocityEngine.getTemplate("/5e_spell.vm");

        var spells = doc.getJSONArray("spell");
        for (int i = 0; i < spells.length(); i++) {
            JSONObject spell = spells.getJSONObject(i);
            var context = new VelocityContext();

            for (String objKey : spell.keySet()) {
                context.put(objKey, spell.get(objKey));
            }

            var duration = spell.optJSONArray("duration");
            if (duration != null)
                context.put("duration", toStringTime(duration));


            var entries = spell.optJSONArray("entries");
            if (entries != null)
                context.put("description", toStringEntry(entries));

            var classes = spell.getJSONObject("classes");
            context.put("classes", toStringClasses(classes.getJSONArray("fromClassList")));
            context.put("class_tags", toClassTags(classes.getJSONArray("fromClassList")));

            String safeName = spell.getString("name").replace("/", "_");
            String outputFilename = "output" + File.separator + safeName + ".md";
            try (OutputStream os = new FileOutputStream(outputFilename)) {
                try (OutputStreamWriter osw = new OutputStreamWriter(os)) {
                    t.merge(context, osw);
                }
            }
        }
    }

    public static String toStringEntry(JSONArray arr) {
        var sa = new StringAccumulatorV2("\n\n");
        for (int idx = 0; idx < arr.length(); idx++) {
            if (arr.isString(idx))
                sa.add(arr.getString(idx));
        }
        return sa.asString();
    }

    public static String toStringTime(JSONArray arr) {
        StringAccumulatorV2 sa = new StringAccumulatorV2(",");
        for (int i = 0; i < arr.length(); i++) {
            JSONObject duration = arr.getJSONObject(i);
            String durationType = duration.getString("type");
            switch (durationType) {
                case "instant":
                    sa.add("Instant");
                    break;
                case "timed":
                    logger.debug("Timed: {}", duration);
                    var dur = duration.getJSONObject("duration");
                    sa.add("Timed (" + dur.getInt("amount") + " " + dur.getString("type") + ")");
                    break;
                case "permanent":
                    sa.add("Permanent");
                    break;
                case "special":
                    sa.add("Special");
                    break;
                default:
                    logger.warn("Unknown time type: {}", durationType);
                    break;
            }
        }
        return sa.asString();
    }

    public static String toStringClasses(JSONArray arr) {
        StringAccumulatorV2 sa = new StringAccumulatorV2(",");
        for (int i = 0; i < arr.length(); i++) {
            JSONObject c = arr.getJSONObject(i);
            sa.add(c.getString("name") + " (" + c.getString("source") + ")");
        }
        return sa.asString();
    }

    public static String toClassTags(JSONArray arr) {
        StringAccumulatorV2 sa = new StringAccumulatorV2(" ");
        for (int i = 0; i < arr.length(); i++) {
            JSONObject c = arr.getJSONObject(i);
            sa.add("#" + c.getString("name").toLowerCase());
        }
        return sa.asString();
    }
}
