package nl.joozey.games.asmallworld;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.Camera;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;

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

/**
 * Created by mint on 22-4-17.
 */

public class MainGame implements Scene {

    interface Particle {
        public void act(float deltaTime);

        public boolean isFinished();

        public void draw(SpriteBatch batch);
    }

    private class MoveParticle extends Sprite implements Particle {
        private float angle;
        private float speed;
        private float alpha;
        private boolean finished;

        public MoveParticle(TextureRegion textureRegion, float x, float y, float angle, float speed) {
            super(textureRegion);
            this.angle = angle;
            this.speed = speed;
            this.alpha = 1f;
            this.setPosition(x, y);
        }

        public void draw(SpriteBatch batch) {
            super.draw(batch);
        }

        public void act(float deltaTime) {
            if (!finished) {
                translate(
                        Gdx.graphics.getDeltaTime() * (float) Math.sin(this.angle) * speed,
                        Gdx.graphics.getDeltaTime() * speed
                );

                alpha -= Gdx.graphics.getDeltaTime() * 1f;
                alpha = Math.max(0, alpha);
                setAlpha(alpha);

                if (alpha <= 0) {
                    finished = true;
                }
            }
        }

        public boolean isFinished() {
            return finished;
        }
    }

    private class AnimationParticle extends Sprite implements Particle {
        private int animX, animY, frameWidth, frameHeight;
        private int frameCount;
        private float interFrameCount;
        private boolean finished;

        public AnimationParticle(TextureRegion textureRegion, int animX, int animY, int frameWidth, int frameHeight, float x, float y) {
            super(textureRegion);
            this.animX = animX;
            this.animY = animY;
            this.frameWidth = frameWidth;
            this.frameHeight = frameHeight;
            setPosition(x, y);
        }

        public void draw(SpriteBatch batch) {
            super.draw(batch);
        }

        public void act(float deltaTime) {

            if (frameCount < animX * animY) {
                interFrameCount += deltaTime * 10f;
                frameCount = (int) interFrameCount;

                setRegion(
                        (frameCount % animX) * frameWidth,
                        (int) (frameCount / animY) * frameHeight,
                        frameWidth,
                        frameHeight
                );
            } else {
                finished = true;
            }
        }

        public boolean isFinished() {
            return finished;
        }
    }

    private SceneControl sceneControl;

    private Texture levelDummyTex;
    private Texture craneTex;
    private Texture craneGateTex;
    private TextureRegion craneGateTexRegion;
    private Texture dirtParticleTex;
    private float digLevel;
    private float gateAnim;
    private float horPosition;
    private float sceneTimer;
    private float failedTimer;
    private float pixPerMeter;

    private boolean act1Finished, act2Finished, act3Finished, act4Finished;

    private Sprite craneSprite;
    private Sprite craneGateSprite;
    private Sprite groundSprite;
    private Sprite digSprite;
    private Texture explosionTexture;
    private Sprite explosionSprite;
    private Pixmap digPixmap;

    private Texture gemTex;
    private Sprite gemSprite;

    private Texture prepareExtractTextTex;
    private Texture extractCompleteTextTex;
    private Texture missionFailedTextTex;
    private Sprite infoTextSprite;

    private boolean showInfoText;
    private float infoBlink;

    private Texture coreTex;
    private Texture coreGlowTex;
    private Sprite coreSprite;
    private Sprite coreGlowSprite;

    private Texture hookRocketTex;
    private Sprite hookRocketSprite;

    private DrillBearSprite drillBearSprite;
    private boolean drillDown;
    private boolean drillLeft;
    private boolean drillRight;
    private float startDigLevel;

    private boolean fail;

    private Sound landingSound;
    private Sound drillSound;
    private Sound mechdoorSound;
    private Sound birdSound;
    private Sound explosionSound;
    private boolean craneLanding;
    private boolean drillBearReady;
    private boolean mechdoorOpening;
    private boolean mechdoorOpen;
    private long drillSoundId;

    private Music music1;

    private SpriteTextWriter spriteTextWriter;
    private Texture scoreTex;
    private Sprite scoreSprite;

    private List<Sprite> gemList;

    private List<Particle> particleList = new ArrayList<Particle>();

    public MainGame() {
        pixPerMeter = 10f;
        levelDummyTex = new Texture("level_dummy.png");
        groundSprite = new Sprite(levelDummyTex);
        groundSprite.setPosition(0, -levelDummyTex.getHeight() + Gdx.graphics.getHeight());

        scoreTex = new Texture("score.png");
        scoreSprite = new Sprite(scoreTex);

        String scoreString = String.valueOf(Statics.score);
        spriteTextWriter = new SpriteTextWriter(
                scoreString, 0, 0);

        digPixmap = new Pixmap((int) levelDummyTex.getWidth(), (int) levelDummyTex.getHeight(), Pixmap.Format.RGBA8888);
        digSprite = new Sprite(new Texture(digPixmap));
        digSprite.setPosition(0, -levelDummyTex.getHeight() + Gdx.graphics.getHeight());

        coreTex = new Texture("core.png");
        coreSprite = new Sprite(coreTex);
        coreSprite.setPosition(
                groundSprite.getX() + groundSprite.getWidth() * .5f - coreTex.getWidth() * .5f,
                groundSprite.getY() + 400);
        coreGlowTex = new Texture("core_glow.png");
        coreGlowSprite = new Sprite(coreGlowTex);
        coreGlowSprite.setPosition(coreSprite.getX(), coreSprite.getY());

        craneTex = new Texture("crane.png");
        craneSprite = new Sprite(craneTex);
        craneSprite.setPosition(Gdx.graphics.getWidth() * .5f - craneTex.getWidth() * .5f, Gdx.graphics.getHeight());

        craneGateTex = new Texture("crane_gate.png");
        craneGateTexRegion = new TextureRegion(craneGateTex, 0, 0, 104, 29);
        craneGateSprite = new Sprite(craneGateTexRegion);
        craneGateSprite.setPosition(craneSprite.getX() + 145, craneSprite.getY() + 158);

        drillBearSprite = new DrillBearSprite();
        drillBearSprite.getSprite().setPosition(-drillBearSprite.getSprite().getWidth(), 0);
        dirtParticleTex = new Texture("dirtparticles.png");

        prepareExtractTextTex = new Texture("prep_extraction_text.png");
        extractCompleteTextTex = new Texture("extraction_complete_text.png");
        missionFailedTextTex = new Texture("mission_failed_text.png");

        infoTextSprite = new Sprite(prepareExtractTextTex);
        infoTextSprite.setAlpha(0);

        hookRocketTex = new Texture("hook_rocket.png");
        hookRocketSprite = new Sprite(hookRocketTex);
        hookRocketSprite.setPosition(
                craneGateSprite.getX() + craneGateSprite.getWidth() * .5f - hookRocketTex.getWidth() * .5f,
                craneGateSprite.getY());

        sceneTimer = 0;

        landingSound = Gdx.audio.newSound(Gdx.files.internal("crane_landing.ogg"));
        drillSound = Gdx.audio.newSound(Gdx.files.internal("drillsound.ogg"));
        music1 = Gdx.audio.newMusic(Gdx.files.internal("music1.ogg"));
        birdSound = Gdx.audio.newSound(Gdx.files.internal("bird.ogg"));
        mechdoorSound = Gdx.audio.newSound(Gdx.files.internal("mech_door.ogg"));
        explosionSound = Gdx.audio.newSound(Gdx.files.internal("explosion.ogg"));

        explosionTexture = new Texture("explosion1.png");
        explosionSprite = new Sprite(new TextureRegion(explosionTexture, 0, 0, 62, 62));

        gemTex = new Texture("gem.png");
        gemList = new ArrayList<Sprite>();

        for (int i = 0; i < 100; i++) {
            Sprite gem = new Sprite(gemTex);
            gem.setPosition(
                    20 + (float) Math.random() * Gdx.graphics.getWidth() - 20 - gemTex.getWidth(),
                    -800 -i * 100 + (float) Math.random() * 50
            );
            gemList.add(gem);
        }

        setInfoText(null);
    }

    @Override
    public void render(SpriteBatch batch) {
        groundSprite.draw(batch);
        digSprite.draw(batch);
        craneSprite.draw(batch);
        craneGateSprite.draw(batch);
        drillBearSprite.draw(batch);

        coreGlowSprite.draw(batch);
        coreSprite.draw(batch);

        if (act3Finished) {
            hookRocketSprite.draw(batch);
        }

        for (Sprite sprite : gemList) {
            sprite.draw(batch);
        }

        drawParticles(batch);

        drawInfoText(batch);

        Camera camera = sceneControl.getCamera();
        scoreSprite.setPosition(
                camera.position.x - scoreSprite.getWidth() * .5f,
                camera.position.y + 200);
        scoreSprite.draw(batch);

        String scoreString = String.valueOf(Statics.score);
        spriteTextWriter.setText(scoreString,
                camera.position.x - scoreString.length() * 7,
                camera.position.y + 180);
        spriteTextWriter.draw(batch);

    }

    @Override
    public void act(float deltaTime) {
        if (fail) {
            failed(Gdx.graphics.getDeltaTime());

        } else {

            if (!act1Finished) {
                if (sceneTimer >= 0 && sceneTimer < 100) {
                    act1(deltaTime);

                } else {
                    act1Finished = true;
                }
            }

            if (act1Finished) {
                if (!act2Finished && digLevel < 508) {
                    act2(deltaTime);

                } else {
                    act2Finished = true;
                }
            }

            if (act2Finished) {
                if (!act3Finished) {
                    act3(deltaTime);
                }
            }

            if (act3Finished) {
                if (!act4Finished) {
                    act4(deltaTime);
                }
            }

            if (act4Finished) {
                sceneControl.showOutro();
            }
        }

        coreGlowSprite.rotate(50f * Gdx.graphics.getDeltaTime());
    }

    private void act1(float deltaTime) {
        if (sceneTimer == 0) {
            birdSound.play();
        }

        sceneTimer += deltaTime * 10f;

        if (sceneTimer < 60) {
            digLevel = sceneTimer;
            startDigLevel = digLevel;
        }

        if (sceneTimer > 36) {
            if (!craneLanding) {
                landingSound.play();
                craneLanding = true;
            }
        }

        if (sceneTimer >= 45 && sceneTimer < 88) {
            craneSprite.setPosition(craneSprite.getX(), -digLevel * pixPerMeter + Gdx.graphics.getHeight() - (sceneTimer - 45) * 10);
            craneGateSprite.setPosition(craneSprite.getX() + 145, craneSprite.getY() + 158);
            hookRocketSprite.setPosition(
                    craneGateSprite.getX() + craneGateSprite.getWidth() * .5f - hookRocketTex.getWidth() * .5f,
                    craneGateSprite.getY());
        }

        sceneControl.getCamera().position.y = Gdx.graphics.getHeight() * .25f - digLevel * pixPerMeter;
    }

    private void act2(float deltaTime) {
        if (!drillBearReady) {
            drillSoundId = drillSound.loop();
            drillBearReady = true;
        }
        drillBearSprite.getSprite().setPosition(
                craneSprite.getX() + craneSprite.getWidth() * .5f - drillBearSprite.getSprite().getWidth() * .5f + horPosition,
                craneSprite.getY() - (digLevel - startDigLevel) * pixPerMeter);

        if (drillDown) {
            if (digLevel == startDigLevel) {
                music1.setLooping(true);
                music1.setVolume(0.5f);
                music1.play();
            }
            doDrillDown();

        } else {
            dontDrillDown();
        }

        if (fail) {
            return;
        }

        sceneControl.getCamera().position.y = Gdx.graphics.getHeight() * .25f - digLevel * pixPerMeter;
    }

    private void act3(float deltaTime) {
        if(drillBearSprite.getSprite().getX() + drillBearSprite.getSprite().getWidth() > coreSprite.getX() &&
                drillBearSprite.getSprite().getX() < coreSprite.getX() + coreSprite.getWidth()) {
            drillSound.stop(drillSoundId);
            sceneTimer += 10f * deltaTime;

            if (sceneTimer > 110 && digLevel > 60) {
                digLevel -= 100f * deltaTime;
                sceneControl.getCamera().position.y = Gdx.graphics.getHeight() * .25f - digLevel * pixPerMeter;
            }

            if (digLevel <= 60 && sceneTimer > 160) {

                if (!mechdoorOpening) {
                    mechdoorSound.play(1f);
                    mechdoorOpening = true;
                }

                gateAnim += deltaTime * 2f;
                gateAnim = Math.min(gateAnim, 4);
                craneGateSprite.setRegion(new TextureRegion(craneGateTex, 0, (int) gateAnim * 29, 104, 29));

                if (gateAnim >= 4) {
                    mechdoorOpen = true;
                }
            }

            if (!mechdoorOpen) {
                setInfoText(prepareExtractTextTex);

            } else {
                setInfoText(null);
                act3Finished = true;
            }
        } else {
            fail = true;
        }
    }

    private void act4(float deltaTime) {

        sceneControl.getCamera().position.y = hookRocketSprite.getY();
        sceneControl.getCamera().position.x = hookRocketSprite.getX();

        System.out.println(hookRocketSprite.getY());

        if (hookRocketSprite.getY() < -5020) {
            setInfoText(extractCompleteTextTex);

            sceneTimer += deltaTime * 10f;
            if (sceneTimer > 200) {
                act4Finished = true;
            }

        } else {
            doFlyDown();

            if (fail) {
                return;
            }
        }
    }

    private void setInfoText(Texture infoText) {
        if (infoText != null) {
            infoTextSprite.setTexture(infoText);
            showInfoText = true;
        } else {
            showInfoText = false;
        }
    }

    private void drawInfoText(SpriteBatch batch) {
        if (showInfoText) {
            infoBlink += Gdx.graphics.getDeltaTime() * 2f;

            OrthographicCamera camera = sceneControl.getCamera();
            infoTextSprite.setPosition(
                    camera.position.x - Gdx.graphics.getWidth() * .5f + 50,
                    camera.position.y + Gdx.graphics.getHeight() * .5f - infoTextSprite.getHeight() - 100);

            if ((int) infoBlink % 2 == 0) {
                infoTextSprite.setAlpha(1);

            } else {
                infoTextSprite.setAlpha(0);
            }

            infoTextSprite.draw(batch);
        }
    }

    private void failed(float deltaTime) {
        if (fail) {
            act4Finished = true;
            failedTimer += deltaTime * 10f;
            setInfoText(missionFailedTextTex);

            if (failedTimer > 50) {
                sceneControl.showIntro();
            }
        }
    }

    private void doDrillDown() {
        drillBearSprite.drill(true);
        drillSound.setPitch(drillSoundId, 1.2f);
        digLevel += Gdx.graphics.getDeltaTime() * 10f;

        if (drillLeft) {
            horPosition -= 55f * Gdx.graphics.getDeltaTime();
        }

        if (drillRight) {
            horPosition += 55f * Gdx.graphics.getDeltaTime();
        }

        Sprite bearSprite = drillBearSprite.getSprite();
        bearSprite.translate((float) Math.random() * 10f - 5f, (float) Math.random() * 10f - 5f);

        for (int i = gemList.size() - 1; i >= 0; i--) {
            Sprite sprite = gemList.get(i);
            if (Vector2.dst2(
                    sprite.getX() + sprite.getWidth() * .5f, sprite.getY() + sprite.getHeight() * .5f,
                    bearSprite.getX() + bearSprite.getWidth() * .5f, bearSprite.getY() + bearSprite.getHeight() * .5f
            ) < bearSprite.getWidth() * bearSprite.getWidth()) {
                gemList.remove(i);
                Statics.score += 5000;

                String scoreString = String.valueOf(Statics.score);
                spriteTextWriter.setText(scoreString,
                        Gdx.graphics.getWidth() * .5f - scoreString.length() * 7,
                        Gdx.graphics.getHeight() - 120);
            }
        }

        float drawYPos = (-bearSprite.getY() + Gdx.graphics.getHeight() - bearSprite.getHeight() * .5f);

        digPixmap.setColor(new Color(.42f, .39f, .39f, .4f));
        digPixmap.fillCircle(
                (int) (drillBearSprite.getSprite().getX() + drillBearSprite.getSprite().getWidth() * .5f),
                (int) drawYPos,
                (int) (drillBearSprite.getSprite().getWidth() * .5f)
        );
        digSprite.getTexture().draw(digPixmap, 0, 0);

        sprayParticles();
    }

    private void dontDrillDown() {
        drillBearSprite.drill(false);
        drillSound.setPitch(drillSoundId, 0.8f);
    }

    private void doFlyDown() {
        if (drillLeft) {
            hookRocketSprite.rotate(-150f * Gdx.graphics.getDeltaTime());
        }

        if (drillRight) {
            hookRocketSprite.rotate(150f * Gdx.graphics.getDeltaTime());
        }

        hookRocketSprite.translateX((float) Math.cos(Math.toRadians(hookRocketSprite.getRotation() + 90)) * -200f * Gdx.graphics.getDeltaTime());
        hookRocketSprite.translateY((float) Math.sin(Math.toRadians(hookRocketSprite.getRotation() + 90)) * -200f * Gdx.graphics.getDeltaTime());

        float drawYPos = (-hookRocketSprite.getY() + Gdx.graphics.getHeight() - hookRocketSprite.getHeight() * .5f);

        if (hookRocketSprite.getY() < -600 &&
                digPixmap.getPixel(
                        (int) (hookRocketSprite.getX() + hookRocketSprite.getWidth() * .5f),
                        (int) drawYPos) == 0
                ) {
            explode(hookRocketSprite.getX(), hookRocketSprite.getY());
            explosionSound.play(1f);
            fail = true;
        }

        digPixmap.setColor(new Color(.2f, .2f, .2f, 1f));
        digPixmap.fillCircle(
                (int) (hookRocketSprite.getX() + hookRocketSprite.getWidth() * .5f),
                (int) drawYPos,
                2
        );
        digSprite.getTexture().draw(digPixmap, 0, 0);

    }

    private void explode(float x, float y) {
        AnimationParticle animationParticle = new AnimationParticle(new TextureRegion(explosionTexture),
                4, 4, 62, 62, x - explosionTexture.getWidth() * .5f, y - explosionTexture.getHeight() * .5f);
        particleList.add(animationParticle);
    }

    private void sprayParticles() {
        MoveParticle dustParticle = new MoveParticle(
                new TextureRegion(
                        dirtParticleTex,
                        ((int) (Math.random() * 4) * 16),
                        0, 16, 16
                ),
                drillBearSprite.getSprite().getX() + (float) Math.random() * drillBearSprite.getSprite().getWidth() * .5f,
                drillBearSprite.getSprite().getY(),
                (float) Math.random() * 180,
                75f);

        particleList.add(dustParticle);
    }

    private void drawParticles(SpriteBatch batch) {
        for (int i = particleList.size() - 1; i >= 0; i--) {
            Particle particle = particleList.get(i);

            if (particle.isFinished()) {
                particleList.remove(i);
            }

            particle.act(Gdx.graphics.getDeltaTime());
            particle.draw(batch);
        }
    }

    @Override
    public void dispose() {
        levelDummyTex.dispose();
        drillBearSprite.dispose();
        craneTex.dispose();
        dirtParticleTex.dispose();
        digPixmap.dispose();

        birdSound.dispose();
        drillSound.dispose();
        landingSound.dispose();
        music1.dispose();
        mechdoorSound.dispose();

        spriteTextWriter.dispose();
        scoreTex.dispose();
        coreTex.dispose();
        coreGlowTex.dispose();
        explosionTexture.dispose();
    }

    @Override
    public void setSceneControl(SceneControl sceneControl) {
        this.sceneControl = sceneControl;
    }

    @Override
    public void restart() {
        craneLanding = false;
        mechdoorOpening = false;
        drillBearReady = false;
        act1Finished = false;
        act2Finished = false;
        act3Finished = false;
        act4Finished = false;
        mechdoorOpen = false;

        failedTimer = 0;
        gateAnim = 0;
        digLevel = 0;
        sceneTimer = 0;

        drillBearSprite.getSprite().setPosition(-drillBearSprite.getSprite().getWidth(), 0);
        craneSprite.setPosition(Gdx.graphics.getWidth() * .5f - craneTex.getWidth() * .5f, Gdx.graphics.getHeight());
        craneGateSprite.setPosition(craneSprite.getX() + 145, craneSprite.getY() + 158);
        hookRocketSprite.setPosition(
                craneGateSprite.getX() + craneGateSprite.getWidth() * .5f - hookRocketTex.getWidth() * .5f,
                craneGateSprite.getY());

        particleList.clear();
    }

    @Override
    public boolean keyDown(int keycode) {
        if (!drillBearReady) return false;

        if (keycode == Input.Keys.S || keycode == Input.Keys.DOWN) {
            drillDown = true;
        }

        if (keycode == Input.Keys.A || keycode == Input.Keys.LEFT) {
            drillLeft = true;
        }

        if (keycode == Input.Keys.D || keycode == Input.Keys.RIGHT) {
            drillRight = true;
        }

        return false;
    }

    @Override
    public boolean keyUp(int keycode) {
        if (!drillBearReady) return false;

        if (keycode == Input.Keys.S || keycode == Input.Keys.DOWN) {
            drillDown = false;
        }

        if (keycode == Input.Keys.A || keycode == Input.Keys.LEFT) {
            drillLeft = false;
        }

        if (keycode == Input.Keys.D || keycode == Input.Keys.RIGHT) {
            drillRight = false;
        }

        return false;
    }

    @Override
    public boolean keyTyped(char character) {
        return false;
    }

    @Override
    public boolean touchDown(int screenX, int screenY, int pointer, int button) {
        return false;
    }

    @Override
    public boolean touchUp(int screenX, int screenY, int pointer, int button) {
        return false;
    }

    @Override
    public boolean touchDragged(int screenX, int screenY, int pointer) {
        return false;
    }

    @Override
    public boolean mouseMoved(int screenX, int screenY) {
        return false;
    }

    @Override
    public boolean scrolled(int amount) {
        return false;
    }
}
