/*!
 * Built by Revolist OU ❤️
 */
import { getRange } from "../../store/index";
import { codesLetter, isAll, isClear, isCopy, isCut, isEnterKeyValue, isPaste, isTab, timeout, RESIZE_INTERVAL, } from "../../utils";
import { getCoordinate, isAfterLast, isBeforeFirst, } from "./selection.utils";
const DIRECTION_CODES = [
    codesLetter.TAB,
    codesLetter.ARROW_UP,
    codesLetter.ARROW_DOWN,
    codesLetter.ARROW_LEFT,
    codesLetter.ARROW_RIGHT,
];
export class KeyboardService {
    constructor(sv) {
        this.sv = sv;
    }
    async keyDown(e, canRange, isEditMode, { range, focus }) {
        // IF EDIT MODE
        if (isEditMode) {
            switch (e.code) {
                case codesLetter.ESCAPE:
                    this.sv.cancel();
                    break;
                case codesLetter.TAB:
                    this.keyChangeSelection(e, canRange);
                    break;
            }
            return;
        }
        // IF NOT EDIT MODE
        // pressed clear key
        if (range && isClear(e.code)) {
            this.sv.clearCell();
            return;
        }
        // below works with focus only
        if (!focus) {
            return;
        }
        // tab key means same as arrow right
        if (isTab(e.code)) {
            this.keyChangeSelection(e, canRange);
            return;
        }
        // pressed enter
        if (isEnterKeyValue(e.key)) {
            this.sv.change();
            return;
        }
        // copy operation
        if (isCopy(e)) {
            return;
        }
        // cut operation
        if (isCut(e)) {
            return;
        }
        // paste operation
        if (isPaste(e)) {
            this.sv.internalPaste();
            return;
        }
        // select all
        if (isAll(e)) {
            if (canRange) {
                this.selectAll(e);
            }
            return;
        }
        // pressed letter key
        if (e.key.length === 1) {
            this.sv.change(e.key);
            return;
        }
        // pressed arrow, change selection position
        if (await this.keyChangeSelection(e, canRange)) {
            return;
        }
    }
    selectAll(e) {
        const range = this.sv.selectionStore.get('range');
        const focus = this.sv.selectionStore.get('focus');
        // if no range or focus - do nothing
        if (!range || !focus) {
            return;
        }
        e.preventDefault();
        this.sv.selectAll();
    }
    async keyChangeSelection(e, canRange) {
        const data = this.changeDirectionKey(e, canRange);
        if (!data) {
            return false;
        }
        // this interval needed for several cases
        // grid could be resized before next click
        // at this case to avoid screen jump we use this interval
        await timeout(RESIZE_INTERVAL + 30);
        const range = this.sv.selectionStore.get('range');
        const focus = this.sv.selectionStore.get('focus');
        return this.keyPositionChange(data.changes, range, focus, data.isMulti);
    }
    keyPositionChange(changes, range, focus, isMulti = false) {
        if (!range || !focus) {
            return false;
        }
        const data = getCoordinate(range, focus, changes, isMulti);
        if (!data) {
            return false;
        }
        const eData = this.sv.getData();
        if (isMulti) {
            if (isAfterLast(data.end, eData.lastCell) || isBeforeFirst(data.start)) {
                return false;
            }
            const range = getRange(data.start, data.end);
            return this.sv.range(range);
        }
        return this.sv.focus(data.start, changes, isAfterLast(data.start, eData.lastCell)
            ? 1
            : isBeforeFirst(data.start)
                ? -1
                : 0);
    }
    /** Monitor key direction changes */
    changeDirectionKey(e, canRange) {
        const isMulti = canRange && e.shiftKey;
        if (DIRECTION_CODES.includes(e.code)) {
            e.preventDefault();
        }
        if (e.shiftKey) {
            switch (e.code) {
                case codesLetter.TAB:
                    return { changes: { x: -1 }, isMulti: false };
            }
        }
        switch (e.code) {
            case codesLetter.ARROW_UP:
                return { changes: { y: -1 }, isMulti };
            case codesLetter.ARROW_DOWN:
                return { changes: { y: 1 }, isMulti };
            case codesLetter.ARROW_LEFT:
                return { changes: { x: -1 }, isMulti };
            case codesLetter.TAB:
            case codesLetter.ARROW_RIGHT:
                return { changes: { x: 1 }, isMulti };
        }
    }
}
