package ru.m.puzzlez.render;

import haxework.net.ImageLoader;
import ru.m.puzzlez.core.ImageSource;
import haxework.signal.Signal;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.geom.Point;
import haxework.view.SpriteView;
import openfl.Assets;
import ru.m.puzzlez.core.GameEvent;
import ru.m.puzzlez.core.GameState;

class Render extends SpriteView implements IRender {

    public var signal(default, null):Signal<GameEvent>;
    public var scale(get, set):Float;

    private function get_scale():Float {
        return table.scaleX;
    }

    private function set_scale(value:Float):Float {
        var result = table.scaleX = table.scaleY = value;
        setSize(table.width, table.height, 'table');
        return result;
    }

    private var state:GameState;
    private var image:BitmapData;

    private var table:Sprite;
    private var parts:Map<Int, PartView>;
    private var activePart:PartView;
    private var activePoint:Point;

    public function new() {
        super();
        signal = new Signal();
        table = new Sprite();
        table.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
        content.addChild(table);
        parts = new Map();
    }

    public function onGameEvent(event:GameEvent):Void {
        switch event {
            case START(state):
                onStart(state);
            case PART_MOVED(id, point):
                var part:PartView = parts[id];
                part.position = point;
            case _:
        }
    }

    private function onStart(state:GameState):Void {
        clean();
        this.state = state;
        RenderUtil.resolveImage(state.preset.image).then(start);
    }

    private function start(image:BitmapData):Void {
        this.image = RenderUtil.cropImage(image, state.preset.imageRect);
        for (part in state.parts) {
            var partImage = RenderUtil.cropImagePart(this.image, part);
            var partView = new PartView(part.id, partImage, part.rect.size);
            partView.position = part.rect.topLeft;
            parts.set(part.id, partView);
            table.addChild(partView);
        }
        scale = scale;
        table.graphics.lineStyle(2, 0xCCCCCC);
        table.graphics.beginFill(0x555555);
        table.graphics.drawRect(state.preset.tableRect.x, state.preset.tableRect.y, state.preset.tableRect.width, state.preset.tableRect.height);
        table.graphics.endFill();
        table.graphics.lineStyle();
        toUpdate();
    }

    private function onMouseDown(event:MouseEvent):Void {
        if (Std.is(event.target, PartView)) {
            activePart = event.target;
            table.setChildIndex(activePart, table.numChildren - 1);
            activePoint = table.globalToLocal(new Point(event.stageX, event.stageY));
            table.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
            table.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
        }
    }

    private function onMouseMove(event:MouseEvent):Void {
        var newPoint = table.globalToLocal(new Point(event.stageX, event.stageY));
        var partPosition = activePart.position.add(newPoint).subtract(activePoint);
        signal.emit(GameEvent.PART_MOVE(activePart.id, partPosition.clone()));
        activePoint = newPoint;
    }

    private function onMouseUp(event:MouseEvent):Void {
        var newPoint = table.globalToLocal(new Point(event.stageX, event.stageY));
        var partPosition = activePart.position.add(newPoint).subtract(activePoint);
        signal.emit(GameEvent.PART_PUT(activePart.id, partPosition.clone()));
        table.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
        table.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
        activePart = null;
        activePoint = null;
    }

    private function clean() {
        for (partView in parts) {
            table.removeChild(partView);
        }
        parts = new Map();
    }
}
