"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DualListBoxEditor = void 0;
const tslib_1 = require("tslib");
const React = tslib_1.__importStar(require("react"));
const react_1 = require("react");
const Enums_1 = require("../../../AdaptableState/Common/Enums");
const ListBoxFilterSortComponent_1 = require("./ListBoxFilterSortComponent");
const ArrayExtensions_1 = require("../../../Utilities/Extensions/ArrayExtensions");
const ListGroupItem_1 = tslib_1.__importDefault(require("../../../components/List/ListGroupItem"));
const SimpleButton_1 = tslib_1.__importDefault(require("../../../components/SimpleButton"));
const rebass_1 = require("rebass");
const Panel_1 = tslib_1.__importDefault(require("../../../components/Panel"));
const ListGroup_1 = tslib_1.__importDefault(require("../../../components/List/ListGroup"));
const SelectableList_1 = tslib_1.__importDefault(require("../../../components/SelectableList"));
const useLatest_1 = require("../../../components/utils/useLatest");
const listGroupStyle = {
    overflowY: 'auto',
    marginBottom: '0px',
};
const listGroupItemStyle = {
    fontSize: 'small',
    padding: 'var(--ab-space-1)',
};
const ButtonDirection = (props) => (React.createElement(SimpleButton_1.default, { ...props, style: { whiteSpace: 'nowrap', justifyContent: 'center', ...props.style } }));
const DualListBoxEditor = (props) => {
    const [placeholder] = (0, react_1.useState)(() => {
        let placeholder = document.createElement('button');
        placeholder.className = 'placeholder';
        placeholder.classList.add('list-group-item');
        placeholder.type = 'button';
        return placeholder;
    });
    const refDraggedHTMLElement = (0, react_1.useRef)(null);
    const refDraggedElement = (0, react_1.useRef)(null);
    const refOverHTMLElement = (0, react_1.useRef)(null);
    const refFirstSelected = (0, react_1.useRef)(null);
    const createAvailableValuesList = (availableValues, sortOrder) => {
        return ArrayExtensions_1.ArrayExtensions.sortArray(availableValues, sortOrder);
    };
    const [state, updateState] = (0, react_1.useState)(() => {
        let availableValues = new Array();
        props.AvailableValues.forEach((x) => {
            if (props.SelectedValues.indexOf(x) < 0) {
                availableValues.push(x);
            }
        });
        return {
            SelectedValues: props.SelectedValues,
            AvailableValues: createAvailableValuesList(availableValues, Enums_1.SortOrder.Asc),
            UiSelectedSelectedValues: [],
            UiSelectedAvailableValues: [],
            FilterValue: '',
            SelectedValuesFilterValue: '',
            SortOrder: Enums_1.SortOrder.Asc,
            SelectedValuesSortOrder: Enums_1.SortOrder.Asc,
            AllValues: props.AvailableValues,
        };
    });
    const callbackRefs = (0, react_1.useRef)([]);
    const getProps = (0, useLatest_1.useLatest)(props);
    const setState = (newState, callback) => {
        updateState({ ...state, ...newState });
        if (newState.SelectedValues && props.SelectedValues !== newState.SelectedValues) {
            getProps().onChange(newState.SelectedValues);
        }
        if (callback) {
            callbackRefs.current.push(callback);
        }
    };
    (0, react_1.useEffect)(() => {
        const callbacks = [...callbackRefs.current];
        callbacks.forEach((fn) => fn());
        callbackRefs.current = [];
    });
    (0, react_1.useEffect)(() => {
        const availableValues = [];
        props.AvailableValues.forEach((x) => {
            if (props.SelectedValues.indexOf(x) < 0) {
                availableValues.push(x);
            }
        });
        setState({
            SelectedValues: props.SelectedValues,
            AvailableValues: createAvailableValuesList(availableValues, state.SortOrder),
        });
    }, [props.SelectedValues, props.AvailableValues]);
    const isValueFilteredOut = (item, FilterValue = state.FilterValue) => {
        return (FilterValue != '' && item.toLocaleLowerCase().indexOf(FilterValue.toLocaleLowerCase()) < 0);
    };
    const canGoTopOrUp = () => {
        return (state.UiSelectedSelectedValues.length != 0 &&
            state.UiSelectedSelectedValues.every((x) => state.SelectedValues.indexOf(x) > 0));
    };
    const canGoDownOrBottom = () => {
        return (state.UiSelectedSelectedValues.length != 0 &&
            state.UiSelectedSelectedValues.every((x) => state.SelectedValues.indexOf(x) < state.SelectedValues.length - 1));
    };
    const ensureFirstSelectedItemVisible = (top) => {
        var itemComponentDOMNode = refFirstSelected.current;
        if (itemComponentDOMNode) {
            itemComponentDOMNode.scrollIntoView(top);
        }
    };
    const Top = () => {
        let newSelectedValues = [].concat(state.UiSelectedSelectedValues, state.SelectedValues.filter((x) => state.UiSelectedSelectedValues.indexOf(x) < 0));
        setState({
            SelectedValues: newSelectedValues,
            UiSelectedSelectedValues: [],
        }, () => {
            ensureFirstSelectedItemVisible(true);
        });
    };
    const Up = () => {
        let newSelectedValues = [...state.SelectedValues];
        for (let selElement of state.UiSelectedSelectedValues) {
            let index = newSelectedValues.indexOf(selElement);
            ArrayExtensions_1.ArrayExtensions.moveArray(newSelectedValues, index, index - 1);
        }
        setState({
            SelectedValues: newSelectedValues,
        }, () => {
            ensureFirstSelectedItemVisible(false);
        });
    };
    const Bottom = () => {
        let newSelectedValues = [].concat(state.SelectedValues.filter((x) => state.UiSelectedSelectedValues.indexOf(x) < 0), state.UiSelectedSelectedValues);
        setState({
            SelectedValues: newSelectedValues,
            UiSelectedSelectedValues: [],
        }, () => {
            ensureFirstSelectedItemVisible(true);
        });
    };
    const Down = () => {
        let newSelectedValues = [...state.SelectedValues];
        for (var index = state.UiSelectedSelectedValues.length - 1; index >= 0; index--) {
            let indexglob = newSelectedValues.indexOf(state.UiSelectedSelectedValues[index]);
            ArrayExtensions_1.ArrayExtensions.moveArray(newSelectedValues, indexglob, indexglob + 1);
        }
        setState({
            SelectedValues: newSelectedValues,
        }, () => {
            ensureFirstSelectedItemVisible(false);
        });
    };
    const Add = () => {
        let newSelectedValues = [...state.SelectedValues];
        let newAvailableValues = [...state.AvailableValues];
        let valuesToAdd = state.UiSelectedAvailableValues;
        valuesToAdd.forEach((x) => {
            let index = newAvailableValues.indexOf(x);
            newAvailableValues.splice(index, 1);
            newSelectedValues.push(x);
        });
        newAvailableValues = createAvailableValuesList(newAvailableValues, state.SortOrder);
        setState({
            UiSelectedAvailableValues: [],
            SelectedValues: newSelectedValues,
            AvailableValues: newAvailableValues,
        });
    };
    const AddAll = () => {
        let newSelectedValues = [].concat(state.SelectedValues);
        let valuesToAdd = state.AvailableValues;
        valuesToAdd.forEach((x) => {
            newSelectedValues.push(x);
        });
        setState({
            UiSelectedSelectedValues: [],
            UiSelectedAvailableValues: [],
            SelectedValues: newSelectedValues,
            AvailableValues: [],
        });
    };
    const RemoveAll = () => {
        let newSelectedValues = [];
        let newAvailableValues = [].concat(state.AllValues);
        newAvailableValues = createAvailableValuesList(newAvailableValues, state.SortOrder);
        setState({
            UiSelectedSelectedValues: [],
            UiSelectedAvailableValues: [],
            SelectedValues: newSelectedValues,
            AvailableValues: newAvailableValues,
        });
    };
    const Remove = () => {
        let newSelectedValues = [...state.SelectedValues];
        let newAvailableValues = [...state.AvailableValues];
        state.UiSelectedSelectedValues.forEach((x) => {
            let index = newSelectedValues.indexOf(x);
            newSelectedValues.splice(index, 1);
            let originalItem = state.AllValues.find((y) => y == x);
            if (originalItem) {
                newAvailableValues.push(originalItem);
            }
        });
        newAvailableValues = createAvailableValuesList(newAvailableValues, state.SortOrder);
        setState({
            UiSelectedSelectedValues: [],
            SelectedValues: newSelectedValues,
            AvailableValues: newAvailableValues,
        });
    };
    const DragSelectedStart = (e, listElement) => {
        refDraggedHTMLElement.current = e.currentTarget;
        refDraggedElement.current = listElement;
    };
    const DragSelectedEnd = () => {
        if (refOverHTMLElement.current && refDraggedElement.current) {
            //now we need to check in which drop area we dropped the selected item
            let to;
            let from = state.SelectedValues.indexOf(refDraggedElement.current);
            let newSelectedArray;
            let newAvailableValues;
            if (refOverHTMLElement.current.classList.contains('Available')) {
                to = state.AvailableValues.indexOf(refOverHTMLElement.current.innerText);
                newSelectedArray = [...state.SelectedValues];
                newSelectedArray.splice(from, 1);
                newAvailableValues = [...state.AvailableValues];
                let originalItem = state.AllValues.find((y) => y == refDraggedElement.current);
                if (originalItem) {
                    let checkForExistig = newAvailableValues.find((x) => x == originalItem);
                    if (!checkForExistig) {
                        newAvailableValues.splice(to, 0, originalItem);
                    }
                }
            }
            else if (refOverHTMLElement.current.classList.contains('ab-AvailableDropZone')) {
                newSelectedArray = [...state.SelectedValues];
                newSelectedArray.splice(from, 1);
                newAvailableValues = [...state.AvailableValues];
                let originalItem = state.AllValues.find((y) => y == refDraggedElement.current);
                if (originalItem) {
                    let checkForExistig = newAvailableValues.find((x) => x == originalItem);
                    if (!checkForExistig) {
                        newAvailableValues.push(originalItem);
                    }
                }
            }
            else if (refOverHTMLElement.current.classList.contains('Selected')) {
                to = state.SelectedValues.indexOf(refOverHTMLElement.current.innerText);
                newSelectedArray = [...state.SelectedValues];
                newSelectedArray.splice(from, 1);
                newSelectedArray.splice(to, 0, refDraggedElement.current);
                newAvailableValues = [...state.AvailableValues];
            }
            else if (refOverHTMLElement.current.classList.contains('ab-SelectedDropZone')) {
                newSelectedArray = [...state.SelectedValues];
                newSelectedArray.splice(from, 1);
                newSelectedArray.push(refDraggedElement.current);
                newAvailableValues = [...state.AvailableValues];
            }
            //We remove our awesome placeholder
            if (refOverHTMLElement.current.classList.contains('ab-SelectedDropZone') ||
                refOverHTMLElement.current.classList.contains('ab-AvailableDropZone')) {
                refOverHTMLElement.current.removeChild(placeholder);
            }
            else {
                refOverHTMLElement.current.parentNode.removeChild(placeholder);
            }
            refOverHTMLElement.current = null;
            refDraggedHTMLElement.current = null;
            refDraggedElement.current = null;
            // Update state
            newAvailableValues = createAvailableValuesList(newAvailableValues, state.SortOrder);
            setState({
                SelectedValues: newSelectedArray,
                AvailableValues: newAvailableValues,
                UiSelectedSelectedValues: [],
                UiSelectedAvailableValues: [],
            });
        }
    };
    const DragAvailableStart = (e, listElement) => {
        refDraggedHTMLElement.current = e.currentTarget;
        refDraggedElement.current = listElement;
    };
    const DragAvailableEnd = () => {
        if (refOverHTMLElement.current && refDraggedElement.current) {
            let to;
            let from = state.AvailableValues.indexOf(refDraggedElement.current);
            let newSelectedArray;
            let newAvailableValues;
            if (refOverHTMLElement.current.classList.contains('Selected')) {
                from = state.AvailableValues.indexOf(refDraggedElement.current);
                to = state.SelectedValues.indexOf(refOverHTMLElement.current.innerText);
                newSelectedArray = [...state.SelectedValues];
                newSelectedArray.splice(to, 0, refDraggedElement.current);
                newAvailableValues = [...state.AvailableValues];
                newAvailableValues.splice(from, 1);
            }
            else if (refOverHTMLElement.current.classList.contains('ab-SelectedDropZone')) {
                newSelectedArray = [...state.SelectedValues];
                newSelectedArray.push(refDraggedElement.current);
                newAvailableValues = [...state.AvailableValues];
                newAvailableValues.splice(from, 1);
            }
            //We remove our awesome placeholder
            if (refOverHTMLElement.current.classList.contains('ab-SelectedDropZone')) {
                refOverHTMLElement.current.removeChild(placeholder);
            }
            else {
                refOverHTMLElement.current.parentNode.removeChild(placeholder);
            }
            refOverHTMLElement.current = null;
            refDraggedHTMLElement.current = null;
            refDraggedElement.current = null;
            // Update state
            setState({
                SelectedValues: newSelectedArray,
                AvailableValues: newAvailableValues,
                UiSelectedSelectedValues: [],
                UiSelectedAvailableValues: [],
            });
        }
    };
    const DragEnterAvailable = (e) => {
        e.preventDefault();
        e.stopPropagation();
    };
    const DragOverAvailable = (e) => {
        e.preventDefault();
        e.stopPropagation();
        //we can only drop selected data into available
        if (!refDraggedHTMLElement.current.classList.contains('Selected')) {
            e.dataTransfer.dropEffect = 'none';
            return;
        }
        let targetElement = e.target;
        //we want to keep the reference of the last intem we were over to
        if (targetElement.classList.contains('placeholder')) {
            return;
        }
        refOverHTMLElement.current = targetElement;
        if (refOverHTMLElement.current.classList.contains('ab-AvailableDropZone')) {
            targetElement.appendChild(placeholder);
        }
        else {
            targetElement.parentNode.insertBefore(placeholder, targetElement);
        }
    };
    const DragLeaveAvailable = (e) => {
        e.preventDefault();
        e.stopPropagation();
        let targetElement = e.target;
        if (targetElement.classList.contains('ab-AvailableDropZone') ||
            targetElement.classList.contains('placeholder')) {
            if (refOverHTMLElement.current) {
                if (refOverHTMLElement.current.classList.contains('ab-AvailableDropZone')) {
                    refOverHTMLElement.current.removeChild(placeholder);
                }
                else {
                    refOverHTMLElement.current.parentNode.removeChild(placeholder);
                }
                refOverHTMLElement.current = null;
            }
        }
    };
    const DragEnterSelected = (e) => {
        e.preventDefault();
        e.stopPropagation();
    };
    const DragOverSelected = (e) => {
        e.preventDefault();
        e.stopPropagation();
        let targetElement = e.target;
        //we want to keep the reference of the last intem we were over to
        if (targetElement.classList.contains('placeholder')) {
            return;
        }
        refOverHTMLElement.current = targetElement;
        if (refOverHTMLElement.current.classList.contains('ab-SelectedDropZone')) {
            targetElement.appendChild(placeholder);
        }
        else {
            targetElement.parentNode.insertBefore(placeholder, targetElement);
        }
    };
    const DragLeaveSelected = (e) => {
        e.preventDefault();
        e.stopPropagation();
        let targetElement = e.target;
        if (targetElement.classList.contains('ab-SelectedDropZone') ||
            targetElement.classList.contains('placeholder')) {
            if (refOverHTMLElement.current) {
                if (refOverHTMLElement.current.classList.contains('ab-SelectedDropZone')) {
                    refOverHTMLElement.current.removeChild(placeholder);
                }
                else {
                    refOverHTMLElement.current.parentNode.removeChild(placeholder);
                }
                refOverHTMLElement.current = null;
            }
        }
    };
    const handleChangeFilterValue = (x) => {
        setState({
            FilterValue: x,
        });
    };
    const handleChangeSelectedValuesFilterValue = (x) => {
        setState({
            SelectedValuesFilterValue: x,
        });
    };
    const sortColumnValues = () => {
        if (state.SortOrder == Enums_1.SortOrder.Asc) {
            setState({
                AvailableValues: ArrayExtensions_1.ArrayExtensions.sortArray(state.AvailableValues, Enums_1.SortOrder.Desc),
                SortOrder: Enums_1.SortOrder.Desc,
            });
        }
        else {
            setState({
                AvailableValues: ArrayExtensions_1.ArrayExtensions.sortArray(state.AvailableValues, Enums_1.SortOrder.Asc),
                SortOrder: Enums_1.SortOrder.Asc,
            });
        }
    };
    const sortSelectedColumnValues = () => {
        if (state.SelectedValuesSortOrder == Enums_1.SortOrder.Asc) {
            setState({
                SelectedValues: ArrayExtensions_1.ArrayExtensions.sortArray(state.SelectedValues, Enums_1.SortOrder.Desc),
                SelectedValuesSortOrder: Enums_1.SortOrder.Desc,
            });
        }
        else {
            setState({
                SelectedValues: ArrayExtensions_1.ArrayExtensions.sortArray(state.SelectedValues, Enums_1.SortOrder.Asc),
                SelectedValuesSortOrder: Enums_1.SortOrder.Asc,
            });
        }
    };
    const getSelectedItemId = (index) => {
        const item = state.SelectedValues[index];
        if (!item) {
            return -1;
        }
        let display = item;
        if (isValueFilteredOut(display, state.SelectedValuesFilterValue)) {
            return -1;
        }
        return item;
    };
    const onSelectedListSelectionChange = (selection) => {
        const UiSelectedSelectedValues = Object.keys(selection);
        UiSelectedSelectedValues.sort((a, b) => state.SelectedValues.indexOf(a) - state.SelectedValues.indexOf(b));
        setState({ UiSelectedSelectedValues });
    };
    const getAvailableItemId = (index) => {
        const item = state.AvailableValues[index];
        if (!item) {
            return -1;
        }
        let display = item;
        let value = item;
        if (isValueFilteredOut(display)) {
            return -1;
        }
        return value;
    };
    /**
     * @param selection - is a map, values being item keys (their textual representation), while values being true
     */
    const onAvailableListSelectionChange = (selection) => {
        const UiSelectedAvailableValues = Object.keys(selection);
        const availableValues = state.AvailableValues;
        UiSelectedAvailableValues.sort((a, b) => availableValues.indexOf(a) - availableValues.indexOf(b));
        setState({ UiSelectedAvailableValues });
    };
    let setRefFirstSelectedSelected = true;
    // build selected elements
    const selectedElements = state.SelectedValues.map((x, index) => {
        let isActive = state.UiSelectedSelectedValues.indexOf(x) >= 0;
        if (isValueFilteredOut(x, state.SelectedValuesFilterValue)) {
            return null;
        }
        const result = (React.createElement(ListGroupItem_1.default, { key: `${x}-1`, index: index, className: "Selected", draggable: true, style: listGroupItemStyle, active: isActive, ref: isActive && setRefFirstSelectedSelected ? refFirstSelected : null, onDragStart: (event) => DragSelectedStart(event, x), onDragEnd: () => DragSelectedEnd(), value: x }, x));
        if (isActive && setRefFirstSelectedSelected) {
            setRefFirstSelectedSelected = false;
        }
        return result;
    });
    // build available elements
    const availableElements = state.AvailableValues.map((x, index) => {
        let isActive = state.UiSelectedAvailableValues.indexOf(x) >= 0;
        let value = x;
        if (isValueFilteredOut(x)) {
            return null;
        }
        else {
            return (React.createElement(ListGroupItem_1.default, { className: "Available", style: listGroupItemStyle, active: isActive, index: index, draggable: true, key: `${value}-item`, onDragStart: (event) => DragAvailableStart(event, x), onDragEnd: () => DragAvailableEnd(), value: value }, x));
        }
    });
    const headerFirstListBox = (React.createElement(ListBoxFilterSortComponent_1.ListBoxFilterSortComponent, { FilterValue: state.FilterValue, sortColumnValues: sortColumnValues, SortOrder: state.SortOrder, handleChangeFilterValue: handleChangeFilterValue }));
    const headerSecondListBox = (React.createElement(ListBoxFilterSortComponent_1.ListBoxFilterSortComponent, { FilterValue: state.SelectedValuesFilterValue, sortColumnValues: sortSelectedColumnValues, SortOrder: state.SelectedValuesSortOrder, handleChangeFilterValue: handleChangeSelectedValuesFilterValue }));
    return (React.createElement(rebass_1.Flex, { alignItems: "stretch", flexDirection: "row", className: "ab-DualListBoxEditor", style: { ...props.style, maxHeight: '100%', width: '100%' } },
        React.createElement(Panel_1.default, { header: props.HeaderAvailable, className: "ab-DualListBoxEditor__source", bodyProps: { padding: 0 }, marginRight: 2, style: { flex: '4 0 0%' }, bodyScroll: true },
            headerFirstListBox,
            React.createElement(SelectableList_1.default, { getItemId: getAvailableItemId, onSelectedChange: onAvailableListSelectionChange },
                React.createElement(ListGroup_1.default, { className: "ab-AvailableDropZone", style: listGroupStyle, onDragEnter: (event) => DragEnterAvailable(event), onDragOver: (event) => DragOverAvailable(event), onDragLeave: (event) => DragLeaveAvailable(event) }, availableElements))),
        React.createElement(rebass_1.Flex, { flexDirection: "column", justifyContent: "center", className: "ab-DualListBoxEditor__action-buttons" },
            React.createElement(ButtonDirection, { "data-name": "add-all", marginBottom: 2, icon: "fast-forward", iconPosition: "end", disabled: state.AvailableValues.length == 0, onClick: () => AddAll() }, "Add All"),
            React.createElement(ButtonDirection, { "data-name": "add", iconPosition: "end", icon: 'arrow-right', marginBottom: 3, disabled: state.UiSelectedAvailableValues.length == 0, onClick: () => Add() }, "Add"),
            React.createElement(ButtonDirection, { "data-name": "remove", icon: 'arrow-left', marginBottom: 2, iconPosition: "start", disabled: state.UiSelectedSelectedValues.length == 0, onClick: () => Remove() }, "Remove"),
            React.createElement(ButtonDirection, { "data-name": "remove-all", marginBottom: 2, icon: "fast-backward", iconPosition: "start", disabled: state.SelectedValues.length == 0, onClick: () => RemoveAll() }, "Remove All")),
        React.createElement(Panel_1.default, { header: props.HeaderSelected, className: "ab-DualListBoxEditor__destination", bodyScroll: true, bodyProps: {
                padding: 0,
            }, style: { flex: '4 0 0%' }, marginLeft: 2, marginRight: 2 },
            headerSecondListBox,
            React.createElement(SelectableList_1.default, { getItemId: getSelectedItemId, onSelectedChange: onSelectedListSelectionChange },
                React.createElement(ListGroup_1.default, { style: listGroupStyle, className: "ab-SelectedDropZone", onDragEnter: (event) => DragEnterSelected(event), onDragOver: (event) => DragOverSelected(event), onDragLeave: (event) => DragLeaveSelected(event) }, selectedElements))),
        React.createElement(rebass_1.Flex, { flexDirection: "column", justifyContent: "center", className: "ab-DualListBoxEditor__order-buttons" },
            React.createElement(ButtonDirection, { "data-name": "top", marginBottom: 2, iconPosition: "start", icon: "triangle-up", disabled: !canGoTopOrUp(), onClick: () => Top() }, "Top"),
            React.createElement(ButtonDirection, { "data-name": "up", marginBottom: 2, iconPosition: "start", icon: "arrow-up", disabled: !canGoTopOrUp(), onClick: () => Up() }, "Up"),
            React.createElement(ButtonDirection, { "data-name": "down", marginBottom: 2, icon: "arrow-down", iconPosition: "start", disabled: !canGoDownOrBottom(), onClick: () => Down() }, "Down"),
            React.createElement(ButtonDirection, { "data-name": "bottom", marginBottom: 2, icon: "triangle-down", iconPosition: "start", disabled: !canGoDownOrBottom(), onClick: () => Bottom() }, "Bottom"))));
};
exports.DualListBoxEditor = DualListBoxEditor;
