package ch.asynk.rustanddust.screens;

import com.badlogic.gdx.Gdx;

import com.badlogic.gdx.graphics.OrthographicCamera;

import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.math.MathUtils;

public class GameCamera extends OrthographicCamera
{
    private static final float ZEROF = 0.01f;

    private int screenWidth;
    private int screenHeight;
    private float zoomOut;
    private float zoomIn;
    private float viewportAspect;
    private float widthFactor;
    private float heightFactor;
    private Rectangle virtual;
    private Rectangle window;
    private Rectangle hud;
    private Matrix4 hudMatrix;
    private Matrix4 hudInvProjMatrix;
    private int hudCorrection;
    private int hudLeft;
    private int hudBottom;
    private boolean fixedHud;

    public GameCamera(float virtualWidth, float virtualHeight, float zoomOut, float zoomIn, int hudCorrection, boolean fixedHud)
    {
        super(virtualWidth, virtualHeight);
        this.zoomOut = zoomOut;
        this.zoomIn = zoomIn;
        this.viewportAspect = (viewportWidth / viewportHeight);
        this.virtual = new Rectangle();
        this.virtual.set(0, 0, virtualWidth, virtualHeight);
        this.window = new Rectangle();
        this.hud = new Rectangle();
        this.hudMatrix = new Matrix4();
        this.hudInvProjMatrix = new Matrix4();
        this.hudCorrection = hudCorrection;
        this.fixedHud = fixedHud;
    }

    public void updateViewport(int screenWidth, int screenHeight)
    {
        this.screenWidth = screenWidth;
        this.screenHeight = screenHeight;

        float aspect = (screenWidth / (float) screenHeight);
        float diff = (viewportAspect - aspect);

        if (diff < -ZEROF) {
            // wider than tall
            window.width = java.lang.Math.min((screenHeight * viewportAspect / zoom), screenWidth);
            window.height = screenHeight;
            window.x = ((screenWidth - window.width) / 2f);
            window.y = 0f;
            viewportWidth = (viewportHeight * (window.width / window.height));
            hud.y = hudCorrection;
            hud.x = (hud.y * viewportWidth / viewportHeight);
        } else if (diff > ZEROF) {
            // taller than wide
            window.width = screenWidth;
            window.height = java.lang.Math.min((screenWidth * viewportAspect / zoom), screenHeight);
            window.x = 0f;
            window.y = ((screenHeight - window.height) / 2f);
            viewportHeight = (viewportWidth * (window.height / window.width));
            hud.x = hudCorrection;
            hud.y = (hud.x * viewportHeight / viewportWidth);
        }

        if (fixedHud) {
            hud.x = 0;
            hud.y = 0;
            hud.width = screenWidth;
            hud.height = screenHeight;
        } else {
            hud.width = (window.width - (2 * hud.x));
            hud.height = (window.height - (2 * hud.y));
        }

        widthFactor = (viewportWidth / screenWidth);
        heightFactor = (viewportHeight / screenHeight);

        clampPosition();
        update(true);
        hudMatrix.setToOrtho2D(hud.x, hud.y, hud.width, hud.height);
        hudInvProjMatrix.set(hudMatrix);
        Matrix4.inv(hudInvProjMatrix.val);
    }

    public void applyMapViewport()
    {
        Gdx.gl.glViewport((int)window.x, (int)window.y, (int)window.width, (int)window.height);
    }

    public void applyHudViewport()
    {
        if (fixedHud)
            Gdx.gl.glViewport(0, 0, screenWidth, screenHeight);
    }

    public Matrix4 getHudMatrix()
    {
        return hudMatrix;
    }

    public int getScreenWidth()
    {
        return screenWidth;
    }

    public int getScreenHeight()
    {
        return screenHeight;
    }

    public int getHudLeft()
    {
        return (int) hud.x;
    }

    public int getHudBottom()
    {
        return (int) hud.y;
    }

    public int getHudWidth()
    {
        return (int) hud.width;
    }

    public int getHudHeight()
    {
        return (int) hud.height;
    }

    public void centerOnWorld()
    {
        position.set((viewportWidth / 2f), (viewportHeight / 2f), 0f);
    }

    public void zoom(float dz)
    {
        zoom += dz;
        clampZoom();
        updateViewport(screenWidth, screenHeight);
    }

    public void translate(float dx, float dy)
    {
        float deltaX = (dx * zoom * widthFactor);
        float deltaY = (dy * zoom * heightFactor);
        translate(deltaX, -deltaY, 0);
        clampPosition();
        update(true);
    }

    public void clampZoom()
    {
        zoom = MathUtils.clamp(zoom, zoomIn, zoomOut);
    }

    public void clampPosition()
    {
        float cx = (viewportWidth * zoom);
        float cy = (viewportHeight * zoom);

        if ((virtual.width - cx) > ZEROF) {
            cx /= 2f;
            position.x = MathUtils.clamp(position.x, cx, (virtual.width - cx));
        } else
            position.x = (virtual.width / 2f);

        if ((virtual.height - cy) > ZEROF) {
            cy /= 2f;
            position.y = MathUtils.clamp(position.y, cy, (virtual.height - cy));
        } else
            position.y = (virtual.height / 2f);
    }

    public void debug()
    {
        System.err.println(String.format(" VIEWPORT: %dx%d * %.2f -> %dx%d", (int)viewportWidth, (int)viewportHeight,
                zoom, (int)(viewportWidth * zoom), (int)(viewportHeight * zoom)));
        System.err.println(String.format("   WINDOW: %d;%d %dx%d", (int)window.x, (int)window.y, (int)window.width, (int)window.height));
        System.err.println("MATRIX:" + combined.toString());
    }

    public void unproject(int x, int y, Vector3 v)
    {
        unproject(v.set(x, y, 0), window.x, window.y, window.width, window.height);
    }

    public void unprojectHud(float x, float y, Vector3 v)
    {
        Rectangle r = (fixedHud ? hud : window);
        x = x - r.x;
        y = Gdx.graphics.getHeight() - y - 1;
        y = y - r.y;
        v.x = (2 * x) / r.width - 1;
        v.y = (2 * y) / r.height - 1;
        v.z = 2 * v.z - 1;
        v.prj(hudInvProjMatrix);
    }
}