package com.almasb.fxglgames.geowars.component;

import com.almasb.fxgl.core.math.FXGLMath;
import com.almasb.fxgl.dsl.components.ExpireCleanComponent;
import com.almasb.fxgl.entity.Entity;
import com.almasb.fxgl.entity.SpawnData;
import com.almasb.fxgl.entity.component.Component;
import com.almasb.fxgl.particle.ParticleComponent;
import com.almasb.fxgl.particle.ParticleEmitters;
import com.almasb.fxgl.time.LocalTimer;
import com.almasb.fxglgames.geowars.factory.GeoWarsFactory;
import com.almasb.fxglgames.geowars.WeaponType;
import javafx.geometry.Point2D;
import javafx.scene.effect.BlendMode;
import javafx.scene.paint.Color;
import javafx.util.Duration;

import java.util.ArrayList;
import java.util.List;

import static com.almasb.fxgl.dsl.FXGL.*;
import static com.almasb.fxgl.dsl.FXGLForKtKt.geto;
import static com.almasb.fxglgames.geowars.Config.MAX_CHARGES_SECONDARY;
import static com.almasb.fxglgames.geowars.Config.WEAPON_DELAY;
import static com.almasb.fxglgames.geowars.GeoWarsType.GRID;

/**
 * @author Almas Baimagambetov (almaslvl@gmail.com)
 */
public class PlayerComponent extends Component {

    private Point2D oldPosition;

    private int playerSpeed;
    private double speed;

    private boolean isShockwaveReady = true;

    private LocalTimer weaponTimer = newLocalTimer();

    public PlayerComponent(int playerSpeed) {
        this.playerSpeed = playerSpeed;
    }

    @Override
    public void onAdded() {
        oldPosition = entity.getPosition();
    }

    @Override
    public void onUpdate(double tpf) {
        speed = tpf * playerSpeed;

        var dir = entity.getPosition().subtract(oldPosition);

        if (!entity.getPosition().equals(oldPosition))
            entity.rotateToVector(dir);

        oldPosition = entity.getPosition();
    }

    public double getSpeed() {
        return speed;
    }

    public void shoot(Point2D shootPoint) {
        Point2D position = entity.getCenter();
        Point2D vectorToMouse = shootPoint.subtract(position);

        shootDirection(vectorToMouse);
    }

    public void shootDirection(Point2D direction) {
        if (weaponTimer.elapsed(WEAPON_DELAY)) {
            Point2D position = entity.getCenter();
            Point2D vectorToMouse = direction;

            WeaponType type = geto("weaponType");

            List<Entity> bullets = new ArrayList<>();

            switch (type) {
                case TRIPLE:

                    // spawn extra bullet
                    bullets.add(spawnBullet(position.subtract(
                            new Point2D(vectorToMouse.getY(), -vectorToMouse.getX()).normalize().multiply(15)
                    ), vectorToMouse));

                case DOUBLE:

                    // spawn extra bullet
                    bullets.add(spawnBullet(position.add(
                            new Point2D(vectorToMouse.getY(), -vectorToMouse.getX()).normalize().multiply(15)
                    ), vectorToMouse));

                case SINGLE:
                default:
                    bullets.add(spawnBullet(position, vectorToMouse));
                    break;
            }

            if (getb("isRicochet")) {
                bullets.forEach(bullet -> {
                    bullet.addComponent(new RicochetComponent());
                });
            }

            weaponTimer.capture();
        }
    }

    /**
     * @param position typically player center
     * @param direction bullet direction
     * @return bullet
     */
    private Entity spawnBullet(Point2D position, Point2D direction) {
        var data = new SpawnData(position.getX(), position.getY())
                .put("direction", direction);
        var e =  spawn("Bullet", data);

        GeoWarsFactory.respawnBullet(e, data);

        return e;
    }

    public boolean isShockwaveReady() {
        return isShockwaveReady;
    }

    public void setShockwaveReady(boolean shockwaveReady) {
        isShockwaveReady = shockwaveReady;
    }

    public void releaseShockwave() {
        if (isShockwaveReady) {
            isShockwaveReady = false;
            spawn("Shockwave", entity.getCenter());
        }
    }

    public void left() {
        entity.translateX(-speed);
    }

    public void right() {
        entity.translateX(speed);
    }

    public void up() {
        entity.translateY(-speed);
    }

    public void down() {
        entity.translateY(speed);
    }

    public void playSpawnAnimation() {
        for (int i = 0; i < 6; i++) {
            final int j = i;

            runOnce(() -> {
                byType(GRID).get(0).getComponent(GridComponent.class)
                        .applyExplosiveForce(1500 + j*100, new Point2D(getAppWidth() / 2.0, getAppHeight() / 2.0), j*50 + 50);
            }, Duration.seconds(i * 0.4));
        }

        var emitter = ParticleEmitters.newExplosionEmitter(450);
        emitter.setSize(1, 16);
        emitter.setBlendMode(BlendMode.SRC_OVER);
        emitter.setStartColor(Color.color(1.0, 1.0, 1.0, 0.5));
        emitter.setEndColor(Color.BLUE);
        emitter.setMaxEmissions(20);
        emitter.setEmissionRate(0.5);

        entityBuilder()
                .at(entity.getCenter().subtract(8, 8))
                .with(new ParticleComponent(emitter))
                .with(new ExpireCleanComponent(Duration.seconds(3)))
                .buildAndAttach();

        animationBuilder()
                .fadeIn(entity)
                .buildAndPlay();
    }

    public void shootSecondary(Point2D shootPoint) {
        if (geti("secondaryCharge") < MAX_CHARGES_SECONDARY)
            return;

        set("secondaryCharge", 0);

        getGameScene().getViewport().shakeRotational(2);

        for (int i = -35; i <= 35; i += 5) {
            var p = FXGLMath.rotate(shootPoint, entity.getCenter(), i);
            var e = spawnBullet(entity.getCenter(), p.subtract(entity.getCenter()));
            e.setVisible(false);
        }
    }
}
