/*
 * TextureRegionParser.java
 *
 * This is an interface for parsing a JSON entry into a texture region. It gives you a
 * very simple way of breaking up a single texture into a texture atlas.
 *
 * @author Walker M. White
 * @data   04/20/2020
 */
package edu.cornell.gdiac.assets;

import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.JsonValue;
import com.badlogic.gdx.utils.ObjectMap;

/**
 * This class parses a JSON entry into a {@link TextureRegion}.
 *
 * Film strips are defined inside of a texture entry. That texture entry is the source
 * of the texture region. Texture regions are defined inside of a subobject called "atlas".
 * Each entry is an array of four numbers defining the rectangle [left,bottom,right,top]
 * of the texture region.
 */
public class TextureRegionParser implements AssetParser<TextureRegion> {
    /** The parent texture of the current texture region */
    private JsonValue root;
    /** The current texture region entry in the JSON directory */
    private JsonValue atlas;

    /** The separating character between the file name and the alias */
    public static char ALIAS_SEP = ':';
    /** The substitution character for when an alias separation is in the file */
    private static char ALIAS_MASK = '∀';

    /**
     * Returns the asset type generated by this parser
     *
     * @return the asset type generated by this parser
     */
    public Class<TextureRegion> getType() {
        return TextureRegion.class;
    }

    /**
     * Resets the parser iterator for the given directory.
     *
     * The value directory is assumed to be the root of a larger JSON structure.
     * The individual assets are defined by subtrees in this structure.
     *
     * @param directory    The JSON representation of the asset directory
     */
    public void reset(JsonValue directory) {
        root = directory;
        root = root.getChild( "textures" );
        atlas = null;
        advance();
    }

    /**
     * Returns true if there are still assets left to generate
     *
     * @return true if there are still assets left to generate
     */
    public boolean hasNext() {
        return atlas != null;
    }

    /**
     * Processes the next available texture region, loading it into the asset manager
     *
     * The parser converts JSON entries into {@link FilmStripLoader.FilmStripParameters}
     * values of the same name. The keys match the field names of that object.
     *
     * This method fails silently if there are no available assets to process.
     *
     * @param manager   The asset manager to load an asset
     * @param keymap    The mapping of JSON keys to asset file names
     */
    public void processNext(AssetManager manager, ObjectMap<String,String> keymap) {
        String file = root.getString( "file", null );
        if (file == null) {
            advance();
            return;
        }
        TextureRegionLoader.TextureRegionParameters params = new TextureRegionLoader.TextureRegionParameters(file);
        if (atlas.size < 4) {
            throw new GdxRuntimeException( "Rectangle "+atlas+" is not valid");
        }

        params.x = atlas.getInt(0);
        params.y = atlas.getInt(1);
        params.width  = atlas.getInt(2);
        params.height = atlas.getInt(3);
        params.width = params.width == -1 ? -1 : params.width-params.x;
        params.height = params.height == -1 ? -1 : params.height-params.y;
        String region = ParserUtils.safeConcatenate(file,atlas.name(),ALIAS_SEP,ALIAS_MASK);
        keymap.put(root.name()+"."+atlas.name(),region);
        manager.load( region,TextureRegion.class, params );
        advance();
    }

    /**
     * Returns true if o is another TextureRegionParser
     *
     * @return true if o is another TextureRegionParser
     */
    public boolean equals(Object o) {
        return o instanceof TextureRegionParser;
    }

    /**
     * Advances the read position forward to find the next texture region
     *
     * Texture regions are defined inside of a parent texture. As not all textures
     * have a region, we need to scan forward to find the next film strip
     */
    private void advance() {
        if (atlas != null) {
            atlas = atlas.next();
            if (atlas == null) {
                root = root.next();
            }
        }
        while (atlas == null && root != null) {
            if (root.hasChild( "atlas" )) {
                atlas = root.getChild( "atlas" );
            } else {
                root = root.next();
            }
        }
    }

}