/*
 * Ship.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.graphics.*;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Color;

/**
 * Model class for the player ship.
 */
public class Ship extends GameObject {
    /// CONSTANTS (defined by the JSON file)
    /** Horizontal speed **/
    private static float beetleSpeed;
    /** How long between shots */
    private static int cooldownTime;
    /** Cooldown bonus if we don't fire */
    private static int cooldownBonus;
    /** Rescale the size of a ship */
    private static float imageScale;
    /** How fast we change frames (one frame per 4 calls to update) */
    private static float animationSpeed;

    // ATTRIBUTES (per object)
    /** The left/right movement of the player this turn */
    private float movement = 0.0f;
    /** Whether this ship is currently firing */
    private boolean firing = false;
    /** How long before ship can fire again */
    private int cooldown;
    /** Current animation frame for this ship */
    private float animeframe;

    /**
     * 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.SHIP;
    }

    /**
     * Returns the current player (left/right) movement input.
     *
     * @return the current player movement input.
     */
    public float getMovement() {
        return movement;
    }

    /**
     * Sets the current player (left/right) movement input.
     *
     * @param value the current player movement input.
     */
    public void setMovement(float value) {
        movement = value;
    }

    /**
     * Returns true if the ship is actively firing.
     *
     * @return true if the ship is actively firing.
     */
    public boolean isFiring() {
        return firing && cooldown <= 0;
    }

    /**
     * Sets whether the ship is actively firing.
     *
     * @param value whether the ship is actively firing.
     */
    public void setFiring(boolean value) {
        firing = value;
    }

    /**
     * Resets the cooldown so that the weapon can fire again.
     *
     * Since weapon fire is managed externally, we need this method to
     * reset the weapon after use. Otherwise, the player can fire
     * a continuous stream of bullets.
     */
    public void resetCooldown() {
        cooldown = cooldownTime;
    }

    /**
     * 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) {
        beetleSpeed = constants.getFloat("beetle speed",0);
        cooldownTime  = constants.getInt("cooldown time",0);
        cooldownBonus = constants.getInt("cooldown bonus",0);
        imageScale = constants.getFloat("image scale", 1);
        animationSpeed = constants.getFloat("animation speed",0);
    }

    /**
    * Creates a ship 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 Ship(float x, float y) {
        super(x, y);
        cooldown   = 0;
        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;
    }

    /**
     * Updates the animation frame and position of this ship.
     *
     * Notice how little this method does. It does not actively fire the weapon.
     * It only manages the cooldown and indicates whether the weapon is
     * currently firing. The result of weapon fire is managed by the
     * GameplayController.
     *
     * @param delta Number of seconds since last animation frame
     */
    public void update(float delta) {
        // Call superclass's update
        super.update(delta);

        // Increase animation frame, but only if trying to move
        if (movement != 0.0f && animator != null) {
            animeframe += animationSpeed;
            if (animeframe >= animator.getSize()) {
                animeframe -= animator.getSize();
            }
            position.x += movement * beetleSpeed;
        }

        // Decrease time until ship can fire again
        if (cooldown > 0) {
            cooldown--;
        }

        if (!firing) {
            // Cool down faster when not holding space
            cooldown -= cooldownBonus;
        }
    }

    /**
     * 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) {
        float x = animator.getRegionWidth()/2.0f;
        float y = animator.getRegionHeight()/2.0f;
        SpriteBatch.computeTransform(transform, x, y, position.x, position.y*imageScale,
                                     0, imageScale, imageScale);

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

}
