"use strict";
/* Copyright 2015 Bloomberg Finance L.P.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.HandDraw = void 0;
const d3 = __importStar(require("d3"));
const utils = __importStar(require("./utils"));
const Interaction_1 = require("./Interaction");
class HandDraw extends Interaction_1.Interaction {
    render() {
        super.render();
        this.d3el.style('cursor', 'crosshair');
        this.active = false;
        // Register the mouse callback when the mark view promises are
        // resolved.
        this.setLinesView().then(() => {
            this.d3el.on('mousedown', () => {
                return this.mousedown();
            });
            this.setLimits();
        });
        this.model.on_some_change(['min_x', 'max_x'], this.setLimits, this);
    }
    setLinesView() {
        return __awaiter(this, void 0, void 0, function* () {
            const fig = this.parent;
            const views = yield Promise.all(fig.mark_views.views);
            const fig_mark_ids = fig.mark_views._models.map((markModel) => {
                return markModel.model_id; // Model ids of the marks in the figure
            });
            const mark_index = fig_mark_ids.indexOf(this.lines.model_id);
            this.linesView = views[mark_index];
        });
    }
    mousedown() {
        this.active = true;
        this.mouseEntry(false);
        this.d3el.on('mousemove', this.mousemove.bind(this));
        this.d3el.on('mouseleave', this.mouseup.bind(this));
        this.d3el.on('mouseup', this.mouseup.bind(this));
    }
    mouseup() {
        if (this.active) {
            this.mouseEntry(true);
            this.lines.set('y', utils.convert_dates(utils.deepCopy(this.lines.y_data)));
            this.linesView.touch();
            this.active = false;
            this.d3el.on('mousemove', null);
            this.d3el.on('mouseleave', null);
            this.d3el.on('mouseup', null);
        }
    }
    mousemove() {
        this.mouseEntry(true);
    }
    mouseEntry(memory) {
        // If memory is set to true, itermediate positions between the last
        // position of the mouse and the current one will be interpolated.
        if (this.active) {
            const xindex = Math.min(this.lineIndex, this.lines.x_data.length - 1);
            const mousePos = d3.mouse(this.el);
            if (!memory || !('previousPos' in this)) {
                this.previousPos = mousePos;
            }
            const scaleX = this.linesView.scales.x.scale;
            const scaleY = this.linesView.scales.y.scale;
            const newx = scaleX.invert(mousePos[0]);
            const newy = scaleY.invert(mousePos[1]);
            const oldx = scaleX.invert(this.previousPos[0]);
            scaleY.invert(this.previousPos[1]);
            const old_index = this.nearestNeighbourSearch(this.lines.x_data[xindex], oldx);
            const new_index = this.nearestNeighbourSearch(this.lines.x_data[xindex], newx);
            const min = Math.min(old_index, new_index);
            const max = Math.max(old_index, new_index);
            for (let i = min; i <= max; ++i) {
                if ((!this.validMin || this.lines.x_data[xindex][i] >= this.minX) &&
                    (!this.validMax || this.lines.x_data[xindex][i] <= this.maxX)) {
                    this.lines.y_data[this.lineIndex][i] = newy;
                }
            }
            // since x_data may be a TypedArray, explicitly use Array.map
            const xy_data = Array.prototype.map.call(this.lines.x_data[xindex], (d, i) => {
                return {
                    x: d,
                    y: this.lines.y_data[this.lineIndex][i],
                };
            });
            this.linesView.d3el
                .select('#curve' + (this.lineIndex + 1))
                .attr('d', (d) => {
                return this.linesView.line(xy_data);
            });
            this.previousPos = mousePos;
        }
    }
    setLimits() {
        const is_date = this.linesView.scales.x.model.type == 'date';
        if (is_date) {
            this.minX = utils.getDate(this.model.get('min_x'));
            this.validMin = !(this.minX === null ||
                this.minX === undefined ||
                isNaN(this.minX.getTime()));
            this.maxX = utils.getDate(this.model.get('max_x'));
            this.validMax = !(this.maxX === null ||
                this.maxX === undefined ||
                isNaN(this.maxX.getTime()));
        }
        else {
            this.minX = this.model.get('min_x');
            this.maxX = this.model.get('max_x');
            this.validMin = !(this.minX === null || this.minX === undefined);
            this.validMax = !(this.maxX === null || this.maxX === undefined);
        }
    }
    nearestNeighbourSearch(x_data, x) {
        const idx = this.linesView.bisect(x_data, x);
        if (x - x_data[idx - 1] > x_data[idx] - x) {
            return idx;
        }
        else {
            return idx - 1;
        }
    }
    get lineIndex() {
        return this.model.get('line_index');
    }
    get lines() {
        return this.model.get('lines');
    }
}
exports.HandDraw = HandDraw;
//# sourceMappingURL=HandDraw.js.map