<template>
    <div>
        <el-button @click="addCircle">Circle</el-button>
        <el-button @click="addRect">Rect</el-button>
        <el-button @click="deleteItem">Delete</el-button>
        <el-button @click="undo">Undo</el-button>
        <el-button @click="redo">Redo</el-button>
        <canvas id="undoFrame" width="800" height="400" />
    </div>
</template>

<script lang="ts">
    import { SMouseEvent, SUndoStack } from "@saga-web/base";
    import { SColor, SPainter, SRect } from "@saga-web/draw";
    import { SGraphItem, SGraphScene, SGraphView } from "@saga-web/graph";
    import {SGraphCommand} from "@saga-web/graph/lib";
    import {SUndoCommand} from "@saga-web/base/lib";
    import {SPoint} from "@saga-web/draw/lib";

    class RectItem extends SGraphItem {
        width = 100;
        height = 100;

        constructor(parent: SGraphItem | null) {
            super(parent);
            this.moveable = true;
            this.selectable = true;
            this.selected = true;
        }

        boundingRect(): SRect {
            return new SRect(0, 0, this.width, this.height);
        }

        onDraw(canvas: SPainter): void {
            canvas.pen.color = SColor.Blue;
            canvas.pen.lineWidth = 5;
            canvas.brush.color = this.selected ? SColor.Red : SColor.Green;
            canvas.drawRect(0, 0, this.width, this.height)
        }
    }

    class CircleItem extends SGraphItem {
        r = 50;

        constructor(parent: SGraphItem | null) {
            super(parent);
            this.moveable = true;
            this.selectable = true;
        }

        boundingRect(): SRect {
            return new SRect(0, 0, this.r * 2, this.r * 2);
        }

        onDraw(canvas: SPainter): void {
            canvas.pen.color = SColor.Blue;
            canvas.pen.lineWidth = 5;
            canvas.brush.color = this.selected ? SColor.Red : SColor.Green;
            canvas.drawCircle(this.r, this.r, this.r);
        }
    }

    class SScene extends SGraphScene {
        undoStack = new SUndoStack();
        /** 定义命令 */
        cmd = 0;

        constructor() {
            super();
        }


        onMouseUp(event: SMouseEvent): boolean {
            switch(this.cmd) {
                case 1:
                    this.addCircle(event.x, event.y);
                    break;
                case 2:
                    this.addRect(event.x, event.y);
                    break;
                default:
                    super.onMouseUp(event);
            }
            this.cmd = 0;
            return false
        }

        private addCircle(x: number, y: number): void {
            let item = new CircleItem(null);
            item.moveTo(x - 50, y - 50);
            this.addItem(item);
            this.undoStack.push(new SGraphAddCommand(this, item));
            item.connect("onMove", this, this.onItemMove.bind(this));
        }

        private addRect(x: number, y: number): void {
            let item = new RectItem(null);
            item.moveTo(x - 50, y - 50);
            this.addItem(item);
            this.undoStack.push(new SGraphAddCommand(this, item));
            item.connect("onMove", this, this.onItemMove.bind(this));
        }

        onItemMove(item: SGraphItem, ...arg: any): void {
            this.undoStack.push(new SGraphMoveCommand(this, item, arg[0][0] as SPoint, arg[0][1] as SPoint));
        }
    }

    class TestView extends SGraphView {
        constructor() {
            super("undoFrame");
        }
    }

    class SGraphAddCommand extends SGraphCommand {
        item: SGraphItem;
        parent: SGraphItem | null;

        constructor(scene: SGraphScene, item: SGraphItem) {
            super(scene);
            this.item = item;
            this.parent = item.parent;
        }
        mergeWith(command: SUndoCommand): boolean {
            return false;
        }

        redo(): void {
            this.item.parent = this.parent;
            this.parent.update();
        }

        undo(): void {
            this.item.parent = null;
            this.parent.update();
        }
    }

    class SGraphDeleteCommand extends SGraphCommand {
        mergeWith(command: SUndoCommand): boolean {
            return false;
        }

        redo(): void {
        }

        undo(): void {
        }
    }

    class SGraphMoveCommand extends SGraphCommand {
        item: SGraphItem;
        old: SPoint;
        pos: SPoint;

        constructor(scene: SGraphScene, item: SGraphItem, old: SPoint, pos: SPoint) {
            super(scene);
            this.item = item;
            this.old = old;
            this.pos = pos;
        }
        mergeWith(command: SUndoCommand): boolean {
            if (command instanceof  SGraphMoveCommand && command.item == this.item) {
                command.pos = this.pos;
                return true;
            }
            return false;
        }

        redo(): void {
            this.item.pos = new SPoint(this.pos.x, this.pos.y);
            this.item.update();
        }

        undo(): void {
            this.item.pos = new SPoint(this.old.x, this.old.y);
            this.item.update();
        }
    }

    export default {
        data() {
            return {
                scene: new SScene(),
            }
        },
        mounted(): void {
            let view = new TestView();
            view.scene = this.scene;//new SScene(); //this.data.scene;
        },
        methods: {
            addCircle() {
                this.scene.cmd = 1;
            },
            addRect() {
                this.scene.cmd = 2;
            },
            deleteItem() {

            },
            undo() {
                console.log("undo");
                this.scene.undoStack.undo();
            },
            redo() {
                this.scene.undoStack.redo();
            }
        }
    }
</script>

<style scoped>

</style>
