package games.rednblack.editor.plugin.ninepatch;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Circle;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.utils.ScissorStack;

/**
 * Created by azakhary on 8/18/2015.
 */
public class EditingZone extends Actor {

    private ShapeRenderer shapeRenderer;
    private TextureRegion texture;

    private static final Color BG = new Color(43f / 255f, 43f / 255f, 43f / 255f, 1f);
    private static final Color GUIDE_COLOR = new Color(255f/255f, 94f/255f, 0f/255f, 0.5f);
    private static final Color OVER_GUIDE_COLOR = new Color(255f/255f, 173f/255f, 125f/255f, 1f);

    private float currZoom = 1f;
    private Vector2 shift = new Vector2(0, 0);

    private int mouseOverSplit = -1;

    private float[] splitPositions = new float[4];
    private int[] splits = new int[4];

    public interface PatchChangeListener {
        void changed(int[] splits);
    }

    private PatchChangeListener listener;

    public EditingZone() {
        shapeRenderer = new ShapeRenderer();

        addListener(new InputListener() {
            private Vector2 lastPoint;
            private int selectedSplit = -1;

            public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
                selectedSplit = splitCollision(x, y);
                if(selectedSplit >= 0) {

                } else {
                    lastPoint = new Vector2(x, y);
                }
                return true;
            }

            public void touchDragged(InputEvent event, float x, float y, int pointer) {
                if (selectedSplit >= 0) {
                    if(selectedSplit == 0) { //left
                        splits[0] = (int) ((x-shift.x-getWidth()/2f)/currZoom+texture.getRegionWidth()/2f);
                        if(splits[0] > texture.getRegionWidth()-splits[1]) {
                            int tmp = splits[1]; splits[1] = texture.getRegionWidth()-splits[0]; splits[0] = texture.getRegionWidth() - tmp;
                            selectedSplit = 1;
                            mouseOverSplit = selectedSplit;
                        }
                        splitUpdate();
                        return;
                    }
                    if(selectedSplit == 1) {
                        splits[1] = -(int) ((x-shift.x-getWidth()/2f)/currZoom-texture.getRegionWidth()/2f);
                        if(texture.getRegionWidth()-splits[1] < splits[0]) {
                            int tmp = splits[0]; splits[0] = texture.getRegionWidth() - splits[1]; splits[1] = texture.getRegionWidth() - tmp;
                            selectedSplit = 0;
                            mouseOverSplit = selectedSplit;
                        }
                        splitUpdate();
                        return;
                    }
                    if(selectedSplit == 2) { // top
                        splits[2] = -(int) ((y-shift.y-getHeight()/2f)/currZoom-texture.getRegionHeight()/2f);
                        if(texture.getRegionHeight()-splits[2] < splits[3]) {
                            int tmp = splits[2]; splits[2] = texture.getRegionHeight()-splits[3]; splits[3] = texture.getRegionHeight()-tmp;
                            selectedSplit = 3;
                            mouseOverSplit = selectedSplit;
                        }
                        splitUpdate();
                        return;
                    }
                    if(selectedSplit == 3) {
                        splits[3] = (int) ((y-shift.y-getHeight()/2f)/currZoom+texture.getRegionHeight()/2f);
                        if(splits[3] > texture.getRegionHeight()-splits[2]) {
                            int tmp = splits[3]; splits[3] = texture.getRegionHeight()-splits[2]; splits[2] = texture.getRegionHeight()-tmp;
                            selectedSplit = 2;
                            mouseOverSplit = selectedSplit;
                        }
                        splitUpdate();
                        return;
                    }
                } else {
                    Vector2 diff = new Vector2(x - lastPoint.x, y - lastPoint.y);
                    shiftBy(diff);
                    lastPoint = new Vector2(x, y);
                }
            }

            public void touchUp(InputEvent event, float x, float y, int pointer, int button) {
                selectedSplit = -1;

                if(listener != null) {
                    listener.changed(splits.clone());
                }
            }

            public boolean mouseMoved(InputEvent event, float x, float y) {
                mouseOverSplit = splitCollision(x, y);
                return false;
            }

            @Override
            public boolean scrolled(InputEvent event, float x, float y, float amountX, float amountY) {
                zoomBy(amountY);
                return true;
            }
        });
    }

    public void setListener(PatchChangeListener listener) {
        this.listener = listener;
    }

    public void splitUpdate() {
        if(splits[0] < 0) splits[0] = 0;
        if(splits[1] < 0) splits[1] = 0;
        if(splits[2] < 0) splits[2] = 0;
        if(splits[3] < 0) splits[3] = 0;
    }

    public int[] getSplits() {
        return splits.clone();
    }

    public void setTexture(TextureRegion texture) {
        this.texture = texture;

        int[] s = ((TextureAtlas.AtlasRegion)texture).findValue("split");

        splits[0] = s[0];
        splits[1] = s[1];
        splits[2] = s[2];
        splits[3] = s[3];
    }

    @Override
    public void draw (Batch batch, float parentAlpha) {
        Rectangle scissors = new Rectangle();
        Rectangle clipBounds = new Rectangle(getX(),getY(),getWidth(),getHeight());
        batch.flush();
        ScissorStack.calculateScissors(getStage().getCamera(), batch.getTransformMatrix(), clipBounds, scissors);
        ScissorStack.pushScissors(scissors);

        drawBg(batch, parentAlpha);

        batch.draw(texture,
                getX() + getWidth() / 2 - texture.getRegionWidth() / 2f + shift.x,
                getY() + getHeight() / 2 - texture.getRegionHeight() / 2f + shift.y,
                texture.getRegionWidth() / 2f,
                texture.getRegionHeight() / 2f,
                texture.getRegionWidth(), texture.getRegionHeight(),
                currZoom, currZoom, 0);

        drawSplits(batch, parentAlpha);

        batch.flush();
        ScissorStack.popScissors();
    }

    public void drawBg(Batch batch, float parentAlpha) {
        batch.end();
        Gdx.gl.glLineWidth(1.0f);
        Gdx.gl.glEnable(GL20.GL_BLEND);
        Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);

        shapeRenderer.setProjectionMatrix(getStage().getCamera().combined);
        Matrix4 matrix = batch.getTransformMatrix();
        shapeRenderer.setTransformMatrix(matrix);

        shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
        BG.a = parentAlpha;
        shapeRenderer.setColor(BG);
        shapeRenderer.rect(getX(), getY(), getWidth(), getHeight());
        shapeRenderer.end();

        Gdx.gl.glDisable(GL20.GL_BLEND);
        batch.begin();
        batch.setColor(Color.WHITE.r, Color.WHITE.g, Color.WHITE.b, Color.WHITE.a * parentAlpha);
    }

    public void drawSplits(Batch batch, float parentAlpha) {
        batch.end();
        Gdx.gl.glLineWidth(1.0f);
        Gdx.gl.glEnable(GL20.GL_BLEND);
        Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);

        shapeRenderer.setProjectionMatrix(getStage().getCamera().combined);
        Matrix4 matrix = batch.getTransformMatrix();
        shapeRenderer.setTransformMatrix(matrix);

        shapeRenderer.begin(ShapeRenderer.ShapeType.Line);

        // left, right, top , bottom
        Color guideColor = new Color(GUIDE_COLOR);
        guideColor.a*=parentAlpha;
        Color overColor = new Color(OVER_GUIDE_COLOR);
        overColor.a*=parentAlpha;

        splitPositions[0] = shift.x + getWidth() / 2f + (-texture.getRegionWidth() / 2f + splits[0]) * currZoom;
        splitPositions[1] = shift.x + getWidth() / 2f + (texture.getRegionWidth() / 2f - splits[1]) * currZoom;
        splitPositions[2] = shift.y + getHeight()/2f + (texture.getRegionHeight() / 2f - splits[2])*currZoom;
        splitPositions[3] = shift.y + getHeight() / 2f + (-texture.getRegionHeight() / 2f + splits[3]) * currZoom;


        if(mouseOverSplit == 0) shapeRenderer.setColor(overColor); else shapeRenderer.setColor(guideColor);
        shapeRenderer.line(getX() + splitPositions[0], getY(), getX() + splitPositions[0], getY() + getHeight());

        if(mouseOverSplit == 1) shapeRenderer.setColor(overColor); else shapeRenderer.setColor(guideColor);
        shapeRenderer.line(getX() + splitPositions[1], getY(), getX() + splitPositions[1], getY() + getHeight());

        if(mouseOverSplit == 2) shapeRenderer.setColor(overColor); else shapeRenderer.setColor(guideColor);
        shapeRenderer.line(getX(), getY() + splitPositions[2], getX() + getWidth(), getY() + splitPositions[2]);

        if(mouseOverSplit == 3) shapeRenderer.setColor(overColor); else shapeRenderer.setColor(guideColor);
        shapeRenderer.line(getX(), getY() + splitPositions[3], getX() + getWidth(), getY() + splitPositions[3]);

        shapeRenderer.end();
        Gdx.gl.glDisable(GL20.GL_BLEND);
        batch.begin();
        batch.setColor(Color.WHITE.r, Color.WHITE.g, Color.WHITE.b, Color.WHITE.a * parentAlpha);
    }

    public void zoomBy(float amount) {
        float defaultSize = 1.0f - amount*0.12f;
        currZoom*=defaultSize;
    }

    public void shiftBy(Vector2 diff) {
        shift.add(diff);
    }

    public int splitCollision(float x, float y) {
        Circle touchCircle =  new Circle();
        touchCircle.radius = 5f;
        touchCircle.setPosition(x, y);

        if(touchCircle.contains(splitPositions[0], touchCircle.y)) {
            return 0;
        }
        if(touchCircle.contains(splitPositions[1], touchCircle.y)) {
            return 1;
        }
        if(touchCircle.contains(touchCircle.x, splitPositions[2])) {
            return 2;
        }
        if(touchCircle.contains(touchCircle.x, splitPositions[3])) {
            return 3;
        }

        return -1;
    }
}