/*
 * Shell.java
 *
 * This is a passive model, and this model does very little by itself. All of
 * its work is done by the CollisionController or the GameplayController.
 *
 * This separation is very important for this class because it has a lot of
 * interactions with other classes. When a shell dies, it emits stars. If we
 * did not move that behavior to the CollisionController, then we would have to
 * have a reference to the GameScene in this class. Tight coupling with the
 * GameScene is a very bad idea, so we have separated this out.
 *
 * Based on the original Optimization Lab by Don Holden, 2007
 *
 * @author: Walker M. White
 * @version: 2/2/2025
 */
package edu.cornell.cis3152.optimization.entity;

import com.badlogic.gdx.math.*;
import com.badlogic.gdx.utils.JsonValue;
import edu.cornell.gdiac.util.*;
import edu.cornell.gdiac.graphics.*;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;

/**
 * Model class for enemy shells.
 */
public class Shell extends GameObject {
    /// CONSTANTS (defined by the JSON file)
    /** Maximum friction multiplier for collisions */
    private static float maxFriction;
    /** Minimum friction multiplier for collisions */
    private static float minFriction;
    /** Maximum y-velocity on the bounce */
    private static float maxVY;
    /** Minimum y-velocity on the bounce */
    private static float minVY;
    /** The minimum x-velocity of a newly generated shell */
    private static float minVX;
    /** The maximum y-velocity of a newly generated shell */
    private static float maxVX;
    /** The effects of gravity on this shell */
    private static float gravity;
    /** Rescale the size of a shell */
    private static float imageScale;
    /** How fast we change frames (one frame per 4 calls to update) */
    private static float animationSpeed;

    // ATTRIBUTES (per object)
    /** Friction multiplier for this shell */
    private float friction;
    /** Minimum Y velocity for this shell */
    private float minSpeed;
    /** Current animation frame for this shell */
    private float animeframe;

    /** To measure if we are damaged */
    private boolean damaged;
    /** The backup sprite to use if we are damaged */
    private SpriteSheet dmgSprite;

    /**
     * Returns the type of this object.
     *
     * We use this instead of runtime-typing for performance reasons.
     *
     * @return the type of this object.
     */
    public ObjectType getType() {
        return ObjectType.SHELL;
    }

    /**
     * Returns the friction value for collisions
     *
     * @return the friction value for collisions
     */
    public float getFriction() {
        return friction;
    }

    /**
     * Returns the minimum y-velocity on a bounce
     *
     * @return the minimum y-velocity on a bounce
     */
    public float getMinVY() {
        return minSpeed;
    }

    /**
     * Sets whether this shell is destroyed.
     *
     * Shells have to be shot twice to be destroyed.  This getter checks whether
     * this shell should be destroyed or it should just change colors.
     *
     * @param value whether this shell is destroyed
     */
    public void setDestroyed(boolean value) {
        if (value) {
            if (!damaged && dmgSprite != null) {
                // If it's a red shell, just turn green
                animator = dmgSprite;
                dmgSprite = null;
            } else {
                destroyed = value;
            }
        } else {
            destroyed = value;
        }
    }

    /**
     * Defines the constants for this class.
     *
     * Constants are values shared across all instances. Instead of defining
     * them above, we define them a JSON file. This static method initializes
     * the value from this file.
     *
     * @param constants The JSON value with constants
     */
    public static void setConstants(JsonValue constants) {
        JsonValue frictionRange = constants.get("friction range");
        maxFriction = frictionRange.getFloat(1);
        minFriction = frictionRange.getFloat(0);

        // Only define vx. Gravity takes care of vy.
        JsonValue velRange = constants.get("x-velocity range");
        minVX = velRange.getFloat(0);
        maxVX = velRange.getFloat(1);

        velRange = constants.get("y-velocity range");
        minVY = velRange.getFloat(0);
        maxVY = velRange.getFloat(1);

        gravity = constants.getFloat("gravity", 0);
        imageScale = constants.getFloat("image scale", 1);
        animationSpeed = constants.getFloat("animation speed", 0);
    }

    /**
    * Creates a shell with the given starting position.
     *
     * Any parameters other than position should be derived from the JSON
     * value of constants. See {@link #setConstants}.
     *
     * @param x The x-coordinate of the object
     * @param y The y-coordinate of the object
     */
    public Shell(float x, float y) {
        super(x,y);

        // Set friction multiplier for this shell
        friction = RandomGenerator.getFloat(minFriction, maxFriction);
        // Set minimum Y velocity for this shell
        minSpeed = RandomGenerator.getFloat(minVY, maxVY);

        float vx = RandomGenerator.getFloat(minVX,maxVX);
        // Coin flip positive or negative
        velocity.x = vx*((float)RandomGenerator.getFloat(0, 1) * 2 - 1);

        animeframe = 0.0f;
    }

    /**
     * Sets the sprite sheet for this game object
     *
     * This value can change over time.
     *
     * @param sheet The sprite sheet
     */
    @Override
    public void setSpriteSheet(SpriteSheet sheet) {
        super.setSpriteSheet( sheet );
        radius *= imageScale;
    }

    /**
     * Sets the sprite sheet for when this shell is damaged
     *
     * Setting this to null means that the shell will be destroyed
     * with one shot.
     *
     * @param sheet The sprite sheet for a damaged shell
     */
    public void setDamagedSprite(SpriteSheet sheet) {
        dmgSprite = sheet;
    }

    /**
     * Returns the sprite sheet for when this shell is damaged
     *
     * If this value is null then the shell will be destroyed with
     * one shot.
     *
     * @return the sprite sheet for when this shell is damaged
     */
    public SpriteSheet getDamagedSprite() {
        return dmgSprite;
    }


    /**
     * Updates the animation frame and velocity of this shell.
     *
     * @param delta Number of seconds since last animation frame
     */
    public void update(float delta) {
        // Call superclass's run
        super.update(delta);

        // Increase animation frame
        if (animator != null) {
            animeframe += animationSpeed;
            if (animeframe >= animator.getSize()) {
            animeframe -= animator.getSize();
            }
        }

        // Gravity affects the shell
        velocity.y -= gravity;

    }

    /**
     * Draws this shell to the sprite batch
     *
     * There is only one drawing pass in this application, so you can draw the
     * objects in any order.
     *
     * @param batch The sprite batch
     */
    public void draw(SpriteBatch batch) {
        animator.setFrame((int)animeframe);
        SpriteBatch.computeTransform( transform, origin.x, origin.y,
            position.x, position.y, 0.0f, imageScale, imageScale);

        batch.setColor( Color.WHITE );
        batch.draw( animator, transform );
    }

}
