import { Component } from "react";

import { injectStyle, resizeOpposite } from "../util/index.js";

export interface ResizeButtonProps {
    /**
     * If restoreWidth is not setted this width in px is used
     */
    initialWidth?: number;
    minWidth: number;
    maxWidth: number;
    /**
     * The restored width in px (if you save in localStorage for example)
     */
    restoreWidth?: number;
    onResize: (width: number, collapse: boolean) => void;
    onResizeFinished?: (width: number) => void;
    onResizeOpposite?: (
        containerId: string,
        oppositeId: string,
        width: number,
        injectStyleFn: (id: string, style: string) => ReturnType<typeof injectStyle>,
    ) => void;
    opposite?: HTMLElement;
    /**
     * The offset reduced width in px
     */
    oppositeOffset: number;
    containerId: string;
}

interface ResizeButtonState {
    defaultRestoreWidth: number;
    restoreWidth: number;
}

/**
 * Resize button with resizer (three vertical dots) and collapse button.
 *
 * @returns
 */
export class ResizeButton extends Component<ResizeButtonProps, ResizeButtonState> {
    public static stateKeys = "defaultRestoreWidth,restoreWidth".split(",");

    public currentlyResizing = false;
    public _container: HTMLElement;

    public constructor(props: ResizeButtonProps) {
        super(props);
        const { initialWidth, minWidth, restoreWidth } = props;
        const defaultRestoreWidth = typeof initialWidth === "number" ? initialWidth : minWidth;
        this.state = {
            defaultRestoreWidth,
            restoreWidth: restoreWidth || defaultRestoreWidth,
        };
    }

    /**
     * Avoid rerender of the resizebutton during resizing.
     */
    public shouldComponentUpdate(nextProps: ResizeButtonProps, nextState: ResizeButtonState) {
        const changedState = ResizeButton.stateKeys.filter((k) => this.state[k] !== nextState[k]);
        if (changedState.length === 1 && changedState[0] === "restoreWidth") {
            return false;
        }
        return true;
    }

    public componentDidMount() {
        // Add handler to the resize button
        this._getContainer(".aiot-split-resizer").addEventListener("pointerdown", this.handleMouseDown);
        document.addEventListener("pointerup", this.handleMouseUp as EventListenerOrEventListenerObject);

        // Initialize resize width
        const { defaultRestoreWidth } = this.state;
        this.handleResize(defaultRestoreWidth, null);
        this.props.onResizeFinished && this.props.onResizeFinished(defaultRestoreWidth);
    }

    public handleDoubleClick = () => {
        const width = this._getContainerWidth() > 0 ? 0 : this.state.restoreWidth;
        this.handleResize(width, null);
        this.props.onResizeFinished && this.props.onResizeFinished(width);
    };

    public handleMouseDown = (ev: MouseEvent) => {
        ev.preventDefault();
        document.addEventListener("pointermove", this.handleResize as EventListenerOrEventListenerObject);
        this.currentlyResizing = true;
    };

    public handleMouseUp = (ev: MouseEvent) => {
        document.removeEventListener("pointermove", this.handleResize as EventListenerOrEventListenerObject);
        this.currentlyResizing && this.props.onResizeFinished && this.props.onResizeFinished(this._getContainerWidth());

        this.currentlyResizing = false;
    };

    public handleOpposite = (width: number) => {
        if (this.props.onResizeOpposite) {
            return this.props.onResizeOpposite(this._container.id, this.props.opposite.id, width, injectStyle);
        } else {
            return resizeOpposite(this._container.id, this.props.opposite.id, width);
        }
    };

    public handleResize = (ev: any, force: any) => {
        const { minWidth, maxWidth } = this.props;
        const isEvent = !!(ev && ev.pageX);
        let x: number = isEvent
            ? ev.pageX - (this._container.getBoundingClientRect().left + document.body.scrollLeft) - 15
            : ev; // Offset
        let move: boolean | number = x >= minWidth && x <= maxWidth;

        // Prevent default if event
        isEvent && ev.preventDefault();

        // Allow collapse
        x < minWidth - 50 && (move = x = 1);

        const collapse = move === 1;
        const xOpposite = x + this.props.oppositeOffset;
        window.requestAnimationFrame(() => {
            if ((move || force) && this.handleOpposite(collapse ? x : xOpposite) !== false) {
                this._container.style.width = `${xOpposite}px`;
                !collapse && this.setState({ restoreWidth: x });
                this.props.onResize && this.props.onResize(x, collapse);
            }
        });
    };

    public render() {
        return (
            <span className="aiot-split">
                <div className="aiot-split-resizer" />
                <div className="aiot-split-collapse" onClick={this.handleDoubleClick} />
            </span>
        );
    }

    public _getContainer(find: string, singleFind = true) {
        const elem = document.getElementById(this.props.containerId);
        const findObj: any = find ? elem && elem.querySelectorAll(find) : elem;
        this._container = elem;

        if (find && singleFind) {
            return findObj && findObj[0];
        }

        return findObj;
    }

    public _getContainerWidth() {
        const computed = window.getComputedStyle(this._container);
        const width = parseInt(computed.width, 10);
        return width - parseInt(computed.borderLeftWidth, 10) - parseInt(computed.borderRightWidth, 10);
    }
}
