package com.ygames.ysoccer.match;

import com.ygames.ysoccer.events.BallKickEvent;
import com.ygames.ysoccer.framework.EventManager;
import com.ygames.ysoccer.framework.SoundManager;

import static com.ygames.ysoccer.match.Const.PASSING_SPEED_FACTOR;
import static com.ygames.ysoccer.match.Const.SECOND;
import static com.ygames.ysoccer.match.PlayerFsm.Id.STATE_KICK;

class PlayerStateKick extends PlayerState {

    enum Mode {UNKNOWN, PASSING, KICKING, SHOOTING}

    private Mode mode;
    private boolean startedShooting;
    private boolean forcedPass;
    private boolean shotCrit;
    private boolean critDisabled;
    private float shootingSkill;

    PlayerStateKick(PlayerFsm fsm) {
        super(STATE_KICK, fsm);
    }

    @Override
    void entryActions() {
        super.entryActions();

        mode = Mode.UNKNOWN;
        startedShooting = false;
        forcedPass = false;
        shotCrit = false;
        critDisabled = player.kickCritDisabled;
        player.kickCritDisabled = false;
        shootingSkill = player.skills.shooting;

        ball.a = player.kickAngle;
    }

    @Override
    void doActions() {
        super.doActions();

        float angle;
        if (player.inputDevice.value) {
            angle = player.inputDevice.angle;
        } else {
            angle = player.kickAngle;
        }
        float angle_diff = ((angle - player.kickAngle + 540.0f) % 360.0f) - 180.0f;

        switch (mode) {
            case UNKNOWN:
                if (timer > 0.15 * Const.SECOND) {
                    if (player.kickAction == Player.KickAction.SHOOT) {
                        applyShotCrit();
                        if (ball.isInsideDirectShotArea(-player.side) && player.seesTheGoal()) {
                            mode = Mode.SHOOTING;

                            EventManager.publish(new BallKickEvent(1f));

                        } else {
                            mode = Mode.KICKING;
                            ball.v = shotPower(250.0f);

                            EventManager.publish(new BallKickEvent(0.8f));
                        }

                    } else {
                        mode = Mode.PASSING;
                        ball.v = Const.passPower(240f);

                        // automatic angle correction
                        Player forcedReceiver = player.consumeForcedPassingMate();
                        if (forcedReceiver != null) {
                            forcedPass = true;
                            ball.setForcedPass(player, forcedReceiver);
                            ball.a = player.forcedPassAngleTo(forcedReceiver);
                            ball.v = Math.max(ball.v, player.forcedPassSpeedTo(forcedReceiver));
                            ball.vz = 0;
                            ball.s = 0;
                        }
                        if (!forcedPass && angle_diff == 0) {
                            player.searchPassingMate();
                            if (player.passingMate != null) {
                                ball.a += player.passingMateAngleCorrection;
                                ball.v += Const.passPower(PASSING_SPEED_FACTOR * player.passingMate.distanceFrom(player));
                            }
                        }

                        EventManager.publish(new BallKickEvent(0.6f));
                    }
                }
                break;

            case PASSING:
                if (timer < 0.25 * Const.SECOND) {
                    if (forcedPass) {
                        ball.vz = 0;
                        ball.s = 0;
                        break;
                    }

                    // vertical
                    float f = 1.0f; // medium shoots
                    if (player.inputDevice.value) {
                        // low shoots
                        if (Math.abs(angle_diff) < 67.5f) {
                            f = 0;
                        }
                        // high shoots
                        else if (Math.abs(angle_diff) > 112.5f) {
                            f = 1.33f;
                        }
                    }
                    ball.vz = f * (120.0f + ball.v * (1 - 0.05f * shootingSkill) * timer / Const.SECOND);

                    // spin
                    if (player.inputDevice.value) {
                        if ((Math.abs(angle_diff) > 22.5f) && (Math.abs(angle_diff) < 157.5f)) {
                            ball.s += spinPower(130.0f) / Const.SECOND * Math.signum(angle_diff);
                        }
                    }
                }
                break;

            case KICKING:
                if (timer < 0.25 * Const.SECOND) {
                    // horizontal speed
                    if (player.kickButtonHeld()) {
                        ball.v += shotPower(960.0f + 2 * shootingSkill) / Const.SECOND;
                    }

                    // vertical speed
                    // medium shots
                    float base = 120f;
                    float factor = 0.8f;
                    if (player.inputDevice.value) {
                        // low shots
                        if (Math.abs(angle_diff) < 67.5f) {
                            factor = 0.4f;
                        }
                        // high shots
                        else if (Math.abs(angle_diff) > 112.5f) {
                            factor = 1.2f;
                        }
                    }
                    ball.vz = factor * (base + ball.v * (1 - 0.05f * shootingSkill) * timer / Const.SECOND);

                    // spin
                    if (player.inputDevice.value) {
                        if ((Math.abs(angle_diff) > 22.5f) && (Math.abs(angle_diff) < 157.5f)) {
                            ball.s += spinPower(130.0f) / Const.SECOND * Math.signum(angle_diff);
                        }
                    }
                }
                break;

            case SHOOTING:
                if (timer > 0.20f * SECOND && timer < 0.30f * SECOND) {
                    // horizontal speed
                    if (!startedShooting) {
                        ball.v = shotPower(270f);
                        startedShooting = true;
                    }

                    // no shooting skill
                    // 270 + (0 : 51) * 650 / 512 = 270 + 0 : 64 = 270 : 334

                    // top shooting skill
                    // 270 + (0 : 51) * 1000 / 512 = 270 + 0 : 97 = 270 : 367
                    if (player.kickButtonHeld()) {
                        ball.v += shotPower(650.0f + 50 * shootingSkill) / SECOND;
                    }

                    // vertical speed
                    // medium shots
                    float base = 110.0f;
                    float factor = 0.8f;
                    if (player.inputDevice.value) {
                        // low shots
                        if (Math.abs(angle_diff) < 67.5f) {
                            base = 120.0f;
                            factor = 0.35f;
                        }
                        // high shots
                        else if (Math.abs(angle_diff) > 112.5f) {
                            base = 100.0f;
                            factor = 1.2f;
                        }
                    }
                    ball.vz = factor * (base + ball.v * (1 - 0.05f * shootingSkill) * timer / SECOND);

                    // spin
                    if (player.inputDevice.value) {
                        if ((Math.abs(angle_diff) > 22.5f) && (Math.abs(angle_diff) < 157.5f)) {
                            ball.s += spinPower(130.0f) / SECOND * Math.signum(angle_diff);
                        }
                    }
                }
                break;
        }

        player.animationStandRun();
    }

    private void applyShotCrit() {
        if (!critDisabled && player.role != Player.Role.GOALKEEPER && Const.rollShotCrit()) {
            shootingSkill = Math.max(shootingSkill, Const.CRIT_SHOOTING_STAT);
            shotCrit = true;
            ball.markCriticalShot();
            SoundManager.playPlayerCrit();
        }
    }

    private float shotPower(float value) {
        float power = Const.kickPower(value);
        return shotCrit ? power * Const.CRIT_SHOT_POWER_FACTOR : power;
    }

    private float spinPower(float value) {
        return shotCrit ? value * Const.CRIT_SPIN_FACTOR : value;
    }

    @Override
    State checkConditions() {
        if (timer > 0.35 * Const.SECOND) {
            return fsm.stateStandRun;
        }

        if (ballLost()) {
            return fsm.stateStandRun;
        }

        return null;
    }

    private boolean ballLost() {
        return ball.owner != null && ball.owner.team.side != player.team.side;
    }
}
