/*globals _:false */
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const lodash_1 = require("lodash");
const connection_1 = require("./connection");
const darge_1 = require("./darge");
const model_1 = require("./model");
const timer_1 = require("./timer");
const utils_1 = require("./utils");
const vector_1 = require("./vector");
const WIDTH = 100;
const HEIGHT = 120;
class Canvas {
    constructor(canvasid, datamodel) {
        this.init_rand = (model) => {
            model.pos.x = utils_1.my_rand(WIDTH, this.canvas.width - model.width);
            model.pos.y = utils_1.my_rand(HEIGHT, this.canvas.height - model.height);
            model.velocity.x = utils_1.my_rand(-1, 1);
            model.velocity.y = utils_1.my_rand(1, 2);
        };
        this.init_world = () => {
            this.connections = this.loadSprings();
            for (let i = 0; i < this.datamodel.models.length; i++) {
                const prismaModel = this.datamodel.models[i];
                const ctx = this.canvas.getContext("2d");
                let p = new model_1.Model(ctx, prismaModel);
                this.init_rand(p);
                this.models.push(p);
            }
            darge_1.autolayout(this.models, this.connections, false);
        };
        this.get_dragged_indexes = () => {
            return lodash_1.chain(this.ongoingTouches)
                .map("dragged_model")
                .push(this.dragged_model)
                .without(-1)
                .value();
        };
        this.find_model = (point) => {
            const x = point.x;
            const y = point.y;
            const idx = this.models.findIndex(m => {
                const center = m.center();
                const dx = Math.abs(x - center.x);
                const dy = Math.abs(y - center.y);
                return dx < m.width / 2 && dy < m.height / 2;
            });
            return idx;
        };
        this.point_from_event = (event, scaled = false) => {
            let rect = this.canvas.getBoundingClientRect();
            const x = scaled
                ? (1 / this.scaleFactor) * (event.clientX - rect.left - this.panX)
                : event.clientX - rect.left;
            const y = scaled
                ? (1 / this.scaleFactor) * (event.clientY - rect.top - this.panY)
                : event.clientY - rect.top;
            const point = new vector_1.Vec(x, y);
            return point;
        };
        this.mouseup = () => {
            var _a;
            this.dragged_model = -1;
            const domMatrix = (_a = this.ctx) === null || _a === void 0 ? void 0 : _a.getTransform();
            this.mouseDown = false;
            this.mouseStart = null;
        };
        this.mousedown = (event) => {
            let mousedown_point = this.point_from_event(event, true);
            this.mouseDown = true;
            this.dragged_model = this.find_model(mousedown_point);
            if (this.dragged_model == -1) {
                this.mouseStart = mousedown_point;
            }
            else {
                this.dragged_model_offset = this.models[this.dragged_model].pos.sub(mousedown_point);
            }
        };
        this.mousemove = (event) => {
            var _a;
            if (this.mouseStart && this.dragged_model === -1) {
                let mouse_point = this.point_from_event(event, true);
                // This Only Renders when the mouse is being moved
                const panX = mouse_point.x - this.mouseStart.x;
                const panY = mouse_point.y - this.mouseStart.y;
                (_a = this.ctx) === null || _a === void 0 ? void 0 : _a.translate(panX, panY);
                // this.draw();
                this.mouseStart = mouse_point;
                return;
            }
            if (this.dragged_model !== -1) {
                let mouse_speed = new vector_1.Vec(event.movementX, event.movementY);
                let newpoint = this.point_from_event(event, true);
                this.models[this.dragged_model].pos = newpoint.add(this.dragged_model_offset);
                this.models[this.dragged_model].velocity = mouse_speed.mult(20);
                return;
            }
        };
        this.draw = () => {
            this.timer.mark_time();
            this.origin += 10;
            if (this.origin > 500)
                this.origin = 10;
            let ctx = this.ctx;
            if (ctx) {
                // ctx.canvas.width = window.innerWidth;
                // ctx.canvas.height = window.innerHeight;
                // Store the current transformation matrix
                ctx.save();
                // Use the identity matrix while clearing the canvas
                ctx.setTransform(1, 0, 0, 1, 0, 0);
                ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
                // Restore the transform
                ctx.restore();
                const domMatrix = ctx.getTransform();
                this.scaleFactor = domMatrix.a;
                this.panX = domMatrix.e;
                this.panY = domMatrix.f;
                ctx.fillStyle = "rgb(244, 248, 250)";
                ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
                // Render Spring Connections
                ctx.strokeStyle = "#808080";
                // ctx.setLineDash([4, 4]);
                this.connections.forEach((spring, i) => {
                    spring.draw(ctx, this.models);
                });
                // Render Models
                this.models.forEach((model, i) => {
                    model.draw(ctx, this.hover_model === i, this.dragged_model === i);
                });
            }
            else {
                console.error("Canvas Context Not Found");
            }
        };
        this.handleKeyDown = (ev) => {
            console.log(ev.key);
            switch (ev.key) {
                case "a":
                    darge_1.autolayout(this.models, this.connections, true);
                    break;
                default:
                    break;
            }
        };
        this.handleKeyUp = (ev) => { };
        this.handleScroll = (ev) => {
            const amount = ev.deltaY > 0 ? 0.95 : 1.05;
            this.ctx && this.ctx.scale(amount, amount);
        };
        this.attach_handlers = () => {
            const container = document.getElementById("canvas-container");
            if (container) {
                container.addEventListener("mouseup", this.mouseup, false);
                container.addEventListener("mousemove", this.mousemove, false);
                container.addEventListener("mousedown", this.mousedown, false);
                container.addEventListener("wheel", this.handleScroll, false);
                container.addEventListener("keydown", this.handleKeyDown, false);
                container.addEventListener("keyup", this.handleKeyUp, false);
            }
            setInterval(this.draw, 30);
        };
        this.origin = 0;
        this.timer = new timer_1.Timer();
        this.timer.mark_time();
        this.models = [];
        this.radius = 40;
        this.connections = [];
        this.datamodel = datamodel;
        this.canvas = document.getElementById(canvasid);
        this.canvas.width = window.innerWidth;
        this.canvas.height = window.innerHeight;
        this.ctx = this.canvas.getContext("2d");
        this.simulate = true;
        this.hover_model = -1;
        this.dragged_model = -1;
        this.dragged_model_offset = new vector_1.Vec();
        this.ongoingTouches = [];
        this.mouseStart = null;
        this.selection = [];
        this.num_touch_start = 0;
        this.scaleFactor = 1.0;
        this.panX = 0;
        this.panY = 0;
        this.shiftDown = false;
        this.mouseDown = false;
        this.init_world();
        this.attach_handlers();
    }
    updateDatamodel(datamodel) {
        this.models = [];
        this.connections = [];
        this.datamodel = datamodel;
        this.init_world();
    }
    loadSprings() {
        const loadedRelations = [];
        const connections = [];
        this.datamodel.models.forEach((model, i) => {
            model.fields.forEach((field, fieldIdx) => {
                if (field.kind === "object" &&
                    field.relationName &&
                    !loadedRelations.includes(field.relationName)) {
                    loadedRelations.push(field.relationName);
                    const from_model_idx = i;
                    const from_field_idx = fieldIdx;
                    const to_model_idx = this.datamodel.models.findIndex(m => m.name === field.type);
                    const to_model = this.datamodel.models[to_model_idx];
                    const to_field_idx = to_model.fields.findIndex(f => field.relationName === f.relationName && f.name !== field.name);
                    if (to_model_idx !== -1)
                        connections.push(new connection_1.Connection(from_model_idx, to_model_idx, from_field_idx, to_field_idx));
                }
            });
        });
        return connections;
    }
}
exports.Canvas = Canvas;
