"use strict";
// tslint:disable:jsx-no-multiline-js
var __extends = (this && this.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __assign = (this && this.__assign) || Object.assign || function(t) {
    for (var s, i = 1, n = arguments.length; i < n; i++) {
        s = arguments[i];
        for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
            t[p] = s[p];
    }
    return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
var React = require("react");
var _ = require("lodash");
var react_redux_1 = require("react-redux");
var core_1 = require("@jsonforms/core");
var ExpandArray_1 = require("./ExpandArray");
var property_util_1 = require("../services/property.util");
var react_dnd_1 = require("react-dnd");
var dnd_util_1 = require("./dnd.util");
var util_1 = require("../helpers/util");
var reducers_1 = require("../reducers");
var IconButton_1 = require("@material-ui/core/IconButton");
var Delete_1 = require("@material-ui/icons/Delete");
var Add_1 = require("@material-ui/icons/Add");
var Typography_1 = require("@material-ui/core/Typography");
var styles_1 = require("@material-ui/core/styles");
var recompose_1 = require("recompose");
/**
 * The delay (in milliseconds) between removing this object list item's data from the store
 * and resetting the selection.
 * This is necessary because without a delay the resetted selection is overwritten
 * by the on click handler of this object list item which sets the selection to this item.
 */
var RESET_SELECTION_DELAY = 40;
var styles = function (theme) { return ({
    listItem: {
        minHeight: '1em',
        position: 'relative',
        paddingLeft: '1.5em',
        '&:before': {
            content: '""',
            position: 'absolute',
            left: '0.2em',
            borderTop: '1px solid lightgrey',
            top: '0.5em',
            width: '1em'
        },
        '&:after': {
            content: '""',
            position: 'absolute',
            left: '0.2em',
            borderLeft: '1px solid lightgrey',
            top: '-0.5em',
            height: '100%'
        }
    },
    withoutBorders: {
        '&:last-child:after': {
            display: 'none'
        },
        '&:only-child': {
            paddingLeft: '0.25em'
        },
        '&:only-child:before': {
            display: 'none'
        }
    },
    itemContainer: {
        display: 'flex',
        flexDirection: 'row',
        '& .icon': {
            flexBasis: '1em',
            minHeight: '1em',
            marginRight: '0.25em',
            backgroundRepeat: 'no-repeat',
            backgroundPosition: 'center'
        }
    },
    label: {
        display: 'flex',
        flex: 1,
        marginRight: '1em',
        minHeight: '1.5em',
        alignItems: 'center',
        '& span:first-child:empty': {
            background: '#ffff00',
            maxHeight: '1.5em'
        },
        '&:hover': {
            fontWeight: 'bold',
            cursor: 'pointer',
            color: 'white',
            opacity: 0.9,
            backgroundColor: theme.palette.secondary.main,
        },
        '&:hover $actionButton': {
            display: 'flex',
            justifyContent: 'center',
            '&:hover': {
                color: 'white',
                backgroundColor: theme.palette.secondary.dark,
                borderRadius: '50%'
            }
        }
    },
    actionButton: {
        display: 'none',
        cursor: 'pointer',
        marginLeft: '0.25em',
        fontWeight: 'normal',
        width: 'inherit',
        height: 'inherit'
    },
    selected: {
        fontWeight: 'bold'
    }
}); };
var ObjectListItem = /** @class */ (function (_super) {
    __extends(ObjectListItem, _super);
    function ObjectListItem() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    ObjectListItem.prototype.render = function () {
        var _a = this.props, path = _a.path, schema = _a.schema, rootData = _a.rootData, data = _a.data, handlers = _a.handlers, selection = _a.selection, filterPredicate = _a.filterPredicate, containerProperties = _a.containerProperties, labelProvider = _a.labelProvider, imageProvider = _a.imageProvider, classes = _a.classes;
        var pathSegments = path.split('.');
        var parentPath = _.initial(pathSegments).join('.');
        var labelClass = selection === data ? classes.selected : '';
        var hasParent = !_.isEmpty(parentPath);
        var liClass = !hasParent ? [classes.listItem, classes.withoutBorders].join(' ') : classes.listItem;
        var scopedData = core_1.resolveData(rootData, parentPath);
        var groupedProps = _.groupBy(containerProperties, function (property) { return property.property; });
        var imageClass = imageProvider(schema.$id);
        // TODO: key should be set in caller
        return (React.createElement("li", { className: liClass, key: path },
            React.createElement("div", { className: classes.itemContainer },
                imageClass !== '' ? React.createElement("span", { className: imageClass }) : '',
                React.createElement("span", { className: classes.label, onClick: handlers.onSelect(schema, data, path) },
                    React.createElement(Typography_1.default, { className: labelClass }, labelProvider(schema)(data)),
                    !_.isEmpty(containerProperties) ?
                        (React.createElement(IconButton_1.default, { className: classes.actionButton, "aria-label": 'Add', onClick: handlers.onAdd(schema, path) },
                            React.createElement(Add_1.default, null))) : '',
                    (hasParent || _.isArray(scopedData)) &&
                        React.createElement(IconButton_1.default, { className: classes.actionButton, "aria-label": 'Remove', onClick: handlers.onRemove },
                            React.createElement(Delete_1.default, null)))),
            Object.keys(groupedProps).map(function (groupKey) {
                return React.createElement(ExpandArray_1.default, { key: groupKey, containmentProps: groupedProps[groupKey], path: core_1.Paths.compose(path, groupKey), schema: schema, selection: selection, handlers: handlers, filterPredicate: filterPredicate, labelProvider: labelProvider, imageProvider: imageProvider });
            })));
    };
    return ObjectListItem;
}(React.Component));
var mapStateToProps = function (state, ownProps) {
    var index = util_1.indexFromPath(ownProps.path);
    var containerProps = reducers_1.getContainerProperties(state);
    var containerProperties;
    if (_.has(containerProps, ownProps.schema.$id)) {
        containerProperties = containerProps[ownProps.schema.$id];
    }
    else {
        containerProperties = property_util_1.retrieveContainerProperties(ownProps.schema, ownProps.schema);
    }
    return {
        index: index,
        rootData: core_1.getData(state),
        rootSchema: core_1.getSchema(state),
        containerProperties: containerProperties
    };
};
var mapDispatchToProps = function (dispatch, ownProps) {
    var parentPath = _.initial(ownProps.path.split('.')).join('.');
    var index = util_1.indexFromPath(ownProps.path);
    return {
        remove: function () {
            dispatch(core_1.update(parentPath, function (array) {
                var clone = _.clone(array);
                clone.splice(index, 1);
                return clone;
            }));
        },
        moveListItem: dnd_util_1.moveListItem(dispatch)
    };
};
var mergeProps = function (stateProps, dispatchProps, ownProps) {
    var data = core_1.resolveData(stateProps.rootData, ownProps.path);
    var resetSelection = ownProps.handlers.resetSelection;
    resetSelection = resetSelection ? resetSelection : function () { return undefined; };
    return __assign({}, stateProps, dispatchProps, ownProps, { data: data, handlers: __assign({}, ownProps.handlers, { onRemove: function () {
                var result = dispatchProps.remove(data);
                // Need to reset the selection with a delay because otherwise it gets overwritten
                // by the on click handler of the item also setting the selection.
                setTimeout(resetSelection, RESET_SELECTION_DELAY);
                return result;
            } }) });
};
var ListItem = recompose_1.compose(styles_1.withStyles(styles, { name: 'ObjectListItem' }))(ObjectListItem);
var ObjectListItemDnd = function (_a) {
    var path = _a.path, schema = _a.schema, rootData = _a.rootData, data = _a.data, handlers = _a.handlers, selection = _a.selection, isRoot = _a.isRoot, 
    // isDragging,
    connectDragSource = _a.connectDragSource, connectDropTarget = _a.connectDropTarget, filterPredicate = _a.filterPredicate, containerProperties = _a.containerProperties, labelProvider = _a.labelProvider, imageProvider = _a.imageProvider;
    var listItem = (React.createElement(ListItem, { path: path, schema: schema, rootData: rootData, data: data, handlers: handlers, selection: selection, filterPredicate: filterPredicate, containerProperties: containerProperties, labelProvider: labelProvider, imageProvider: imageProvider }));
    if (isRoot === true) {
        // No Drag and Drop
        return listItem;
    }
    // const opacity = isDragging ? 0.2 : 1;
    // wrap in div because react-dnd does not allow directly connecting to components
    return connectDragSource(connectDropTarget(React.createElement("div", null, listItem)));
};
/**
 * Define the drag and drop source (item is dragged) behavior of the list items
 */
var objectDragSource = {
    beginDrag: function (props, _monitor, _component) {
        var dragInfo = {
            originalPath: props.path,
            currentPath: props.path,
            data: props.data,
            schema: props.schema
        };
        console.log('drag started', props.path);
        return dragInfo;
    },
    endDrag: function (props, monitor) {
        var dragInfo = monitor.getItem();
        var dropInfo = monitor.getDropResult();
        console.log("End Drag; monitor.didDrop(): " + monitor.didDrop());
        if (!monitor.didDrop()) {
            // List item was not successfully dropped => revert to original position if it was moved
            if (dragInfo.originalPath !== dragInfo.currentPath) {
                console.log("No valid drop. Revert position. Original: " + dragInfo.originalPath + "." +
                    ("Current: " + dragInfo.currentPath));
                props.moveListItem(dragInfo.data, dragInfo.currentPath, dragInfo.originalPath);
            }
        }
        else if (dropInfo.move) {
            console.log("Drop in endDrag. Move Target: " + dropInfo.moveTarget);
            props.moveListItem(dragInfo.data, dragInfo.currentPath, dropInfo.moveTarget);
        }
    }
};
/**
 * Injects drag and drop (drag source) related properties into a list item
 */
var collectDragSource = function (dndConnect, monitor) {
    return {
        connectDragSource: dndConnect.dragSource(),
        isDragging: monitor.isDragging()
    };
};
/**
 * Injects drag and drop (drop target) related properties into a list item
 */
var collectDropTarget = function (dndConnect, monitor) {
    return {
        isOver: monitor.isOver({ shallow: true }),
        connectDropTarget: dndConnect.dropTarget()
    };
};
/**
 * Define the drag and drop target (another item is dragged over this item) behavior of list items.
 */
var objectDropTarget = {
    canDrop: function (props, monitor) {
        return dnd_util_1.canDropDraggedItem(props.parentProperties, monitor.getItem());
    },
    /**
     * On drop of a dragged item on another dragged item, mark the drop as handled
     * and that the item does not need to be moved.
     * This is the case because the item is already moved on hover.
     */
    drop: function () {
        var dropInfo = {
            isHandled: true,
            move: false
        };
        return dropInfo;
    },
    /**
     * If the dragged item can be dropped in the list of the hovered item, move it there on the fly.
     *
     * @param props The properties of the hovered list element
     * @param monitor
     */
    hover: function (props, monitor) {
        if (!monitor.isOver({ shallow: true })) {
            return;
        }
        if (!monitor.canDrop()) {
            return;
        }
        var dragInfo = monitor.getItem();
        if (props.path === dragInfo.currentPath) {
            return;
        }
        // move list item and update current path of the currently dragged item
        var targetPath = props.path;
        var moved = props.moveListItem(dragInfo.data, dragInfo.currentPath, targetPath);
        if (moved) {
            dragInfo.currentPath = targetPath;
            console.log('new current path: ' + dragInfo.currentPath);
        }
    }
};
/**
 * Configure a list item as a drop target and a drag source.
 * Drop Target: Allows to drop other list items on this list item and is necessary
 * to sort elements inside a list
 *
 * Drag Source: Allows to drag a list item
 */
var listItemDnd = react_dnd_1.DropTarget(dnd_util_1.Types.TREE_DND, objectDropTarget, collectDropTarget)(react_dnd_1.DragSource(dnd_util_1.Types.TREE_DND, objectDragSource, collectDragSource)(ObjectListItemDnd));
exports.default = react_redux_1.connect(mapStateToProps, mapDispatchToProps, mergeProps)(listItemDnd);
//# sourceMappingURL=ObjectListItem.js.map