/*!
 * Built by Revolist OU ❤️
 */
import debounce from "lodash/debounce";
import { h } from "@stencil/core";
import { CELL_HANDLER_CLASS, MOBILE_CLASS } from "../../utils/consts";
import { collectModelsOfRange, getCell, getCurrentCell, isAfterLast, } from "./selection.utils";
import { getRange } from "../../store/index";
import { getPropertyFromEvent } from "../../utils/events";
export class AutoFillService {
    constructor(sv) {
        this.sv = sv;
        this.autoFillType = null;
        this.autoFillInitial = null;
        this.autoFillStart = null;
        this.autoFillLast = null;
    }
    /**
     * Render autofill box
     * @param range
     * @param selectionFocus
     */
    renderAutofill(range, selectionFocus, isMobile = false) {
        let handlerStyle;
        if (range) {
            handlerStyle = getCell(range, this.sv.dimensionRow.state, this.sv.dimensionCol.state);
        }
        else {
            handlerStyle = getCell(Object.assign(Object.assign({}, selectionFocus), { x1: selectionFocus.x, y1: selectionFocus.y }), this.sv.dimensionRow.state, this.sv.dimensionCol.state);
        }
        return (h("div", { class: {
                [CELL_HANDLER_CLASS]: true,
                [MOBILE_CLASS]: isMobile,
            }, style: {
                left: `${handlerStyle.right}px`,
                top: `${handlerStyle.bottom}px`,
            }, onMouseDown: (e) => this.autoFillHandler(e), onTouchStart: (e) => this.autoFillHandler(e) }));
    }
    autoFillHandler(e, type = "AutoFill" /* AutoFillType.autoFill */) {
        let target = null;
        if (e.target instanceof Element) {
            target = e.target;
        }
        if (!target) {
            return;
        }
        this.selectionStart(target, this.sv.getData(), type);
        e.preventDefault();
    }
    get isAutoFill() {
        return !!this.autoFillType;
    }
    /**
     * Process mouse move events
     */
    selectionMouseMove(e) {
        // initiate mouse move debounce if not present
        if (!this.onMouseMoveAutofill) {
            this.onMouseMoveAutofill = debounce((e, data) => this.doAutofillMouseMove(e, data), 5);
        }
        if (this.isAutoFill) {
            this.onMouseMoveAutofill(e, this.sv.getData());
        }
    }
    getFocus(focus, range) {
        // there was an issue that it was taking last cell from range but focus was out
        if (!focus && range) {
            focus = { x: range.x, y: range.y };
        }
        return focus || null;
    }
    /**
     * Autofill logic:
     * on mouse move apply based on previous direction (if present)
     */
    doAutofillMouseMove(event, data) {
        // if no initial - not started
        if (!this.autoFillInitial) {
            return;
        }
        const x = getPropertyFromEvent(event, 'clientX', MOBILE_CLASS);
        const y = getPropertyFromEvent(event, 'clientY', MOBILE_CLASS);
        // skip touch
        if (x === null || y === null) {
            return;
        }
        const current = getCurrentCell({ x, y }, data);
        // first time or direction equal to start(same as first time)
        if (!this.autoFillLast) {
            if (!this.autoFillLast) {
                this.autoFillLast = this.autoFillStart;
            }
        }
        // check if not the latest, if latest - do nothing
        if (isAfterLast(current, data.lastCell)) {
            return;
        }
        this.autoFillLast = current;
        const isSame = current.x === this.autoFillInitial.x &&
            current.y === this.autoFillInitial.y;
        // if same as initial - clear
        if (isSame) {
            this.sv.setTempRange(null);
        }
        else {
            const area = getRange(this.autoFillInitial, this.autoFillLast);
            this.sv.setTempRange({
                area,
                type: this.autoFillType,
            });
        }
    }
    /**
     * Range selection started
     * Mode @param type:
     * Can be triggered from MouseDown selection on element
     * Or can be triggered on corner square drag
     */
    selectionStart(target, data, type = "Selection" /* AutoFillType.selection */) {
        /** Get cell by autofill element */
        const { top, left } = target.getBoundingClientRect();
        this.autoFillInitial = this.getFocus(data.focus, data.range);
        this.autoFillType = type;
        this.autoFillStart = getCurrentCell({ x: left, y: top }, data);
    }
    /**
     * Clear current range selection on mouse up and mouse leave events
     */
    clearAutoFillSelection(focus, oldRange) {
        // If autofill was active, apply autofill values
        if (this.autoFillInitial) {
            // Fetch latest focus
            this.autoFillInitial = this.getFocus(focus, oldRange);
            // Apply range data if autofill mode is active
            if (this.autoFillType === "AutoFill" /* AutoFillType.autoFill */) {
                const range = getRange(this.autoFillInitial, this.autoFillLast);
                // If range is present, apply data
                if (range) {
                    const { defaultPrevented: stopApply, detail: { range: newRange }, } = this.sv.clearRangeDataApply({
                        range,
                    });
                    // If data apply was not prevented, apply new range
                    if (!stopApply && oldRange) {
                        this.applyRangeWithData(newRange, oldRange);
                    }
                    else {
                        // If data apply was prevented, clear temporary range
                        this.sv.setTempRange(null);
                    }
                }
            }
            else {
                // If not autofill mode, apply range only
                this.applyRangeOnly(this.autoFillInitial, this.autoFillLast);
            }
        }
        // Reset autofill state
        this.resetAutoFillState();
    }
    /**
     * Reset autofill state
     */
    resetAutoFillState() {
        this.autoFillType = null;
        this.autoFillInitial = null;
        this.autoFillLast = null;
        this.autoFillStart = null;
    }
    /**
     * Trigger range apply events and handle responses
     */
    onRangeApply(newData, newRange, oldRange) {
        this.sv.rangeDataApply({
            data: newData,
            models: collectModelsOfRange(newData, this.sv.dataStore),
            type: this.sv.dataStore.get('type'),
            oldRange,
            newRange
        });
        this.sv.setRange(newRange);
    }
    /** Apply range and copy data during range application */
    applyRangeWithData(newRange, rangeToCopy) {
        const rangeData = {
            type: this.sv.dataStore.get('type'),
            colType: this.sv.columnService.type,
            newData: {},
            mapping: {},
            newRange,
            oldRange: rangeToCopy,
        };
        const { mapping, changed } = this.sv.columnService.getRangeData(rangeData, this.sv.columnService.columns);
        rangeData.newData = changed;
        rangeData.mapping = mapping;
        let e = this.sv.selectionChanged(rangeData);
        // if default prevented - clear range
        if (e.defaultPrevented) {
            this.sv.setTempRange(null);
            return;
        }
        e = this.sv.rangeCopy(rangeData);
        if (e.defaultPrevented) {
            this.sv.setRange(newRange);
            return;
        }
        this.onRangeApply(rangeData.newData, newRange, rangeToCopy);
    }
    /**
     * Update range selection only,
     * no data change (mouse selection)
     */
    applyRangeOnly(start, end) {
        // no changes to apply
        if (!start || !end) {
            return;
        }
        const newRange = getRange(start, end);
        this.sv.setRange(newRange);
    }
}
