// Lib
import React, {Component} from 'react';
import {MdAdd, MdDragHandle} from 'react-icons/md';
import {SortableHandle, SortableContainer, SortableElement} from 'react-sortable-hoc';

import {withTheme, injectSheet} from '../../../lib/theme';
import SchemaEditor from "../EditorLoader";
import ButtonInteractiveDelete from "../../ButtonInteractiveDelete";

const styling = theme => ({
    wrapper: {},
    wrapperSelect: {
        overflow: 'hidden'
    },
});

class DragHandleBase extends React.Component {
    timeoutid = false;

    componentWillUnmount() {
        if(this.timeoutid) {
            clearTimeout(this.timeoutid);
            this.timeoutid = false;
        }
    }

    render() {
        const {theme} = this.props;
        const style = {
            cursor: 'inherit',
            boxSizing: 'content-box',
            marginTop: theme.gutter.base * -2 + 'px',
            marginLeft: theme.gutter.base * -2 + 'px',
            padding: theme.gutter.base * 2 + 'px'
        };

        return <button style={style}
                       onMouseEnter={() => {
                           document.body.style.cursor = 'grab';
                       }}
                       onMouseLeave={() => {
                           if('grabbing' !== document.body.style.cursor) {
                               document.body.style.removeProperty('cursor')
                               if(this.timeoutid) {
                                   clearTimeout(this.timeoutid);
                               }
                           }
                       }}
                       onClick={() => {
                           this.timeoutid = setTimeout(() => {
                               document.body.style.cursor = 'grabbing';
                           }, 105);
                       }}
                       onMouseUp={() => {
                           document.body.style.removeProperty('cursor')
                           if(this.timeoutid) {
                               clearTimeout(this.timeoutid);
                           }
                       }}
        ><MdDragHandle size={'1.125em'} color={theme.textColor}/></button>
    }
}

const DragHandle = SortableHandle(withTheme(DragHandleBase));

const SortItem = SortableElement(withTheme((props) => {
    const buttonStyle = {
        marginLeft: 'auto',
        marginTop: props.theme.gutter.base * -2 + 'px',
        marginRight: props.theme.gutter.base * -2 + 'px',
        padding: props.theme.gutter.base * 2 + 'px'
    };

    return <div style={{
        display: 'flex',
        flexWrap: 'wrap',
        background: props.theme.bgPage,
        transition: props.theme.transition + ' background, ' + props.theme.transition + ' border',
        border: '1px solid ' + props.theme.textColorLight,
        padding: props.theme.gutter.base * 2 + 'px'
    }}>
        {props.sortable ? <DragHandle/> : null}
        <ButtonInteractiveDelete onClick={props.delete} size={'1.125em'} style={buttonStyle}/>

        <p style={{width: '100%', wordBreak: 'break-all'}}>{props.index}</p>

        <div style={{width: '100%'}}>
            <SchemaEditor data={props.data}
                          schema={props.schema}
                          addSave={props.addSave}
                          rmSave={props.rmSave}
                          cacheid={props.cacheid}
            />
        </div>
    </div>
}));

const SortContainer = SortableContainer(withTheme((props) => {
    return <div style={{
        display: 'grid',
        gridTemplateColumns: '1fr 1fr',
        gridTemplateRows: '1fr',
        columnGap: props.theme.gutter.base * 2 + 'px',
        rowGap: props.theme.gutter.base * 2 + 'px'
    }}>{props.children}</div>;
}));

class GenericList extends Component {

    state = {
        list: [],
    };

    evts = {};

    componentWillUnmount() {
        if(this.props.rmSave) {
            this.props.rmSave(this.props.name);
        }
    }

    componentDidUpdate(prevProps) {
        if(
            prevProps.data !== this.props.data ||
            prevProps.schema !== this.props.schema
        ) {
            this.buildList();
        }
    }

    componentDidMount() {
        if(this.props.addSave) {
            this.props.addSave(this.props.name, () => {
                const val_all = this.itemSaveAll();
                const data = [];
                val_all.forEach(elem => {
                    if(elem.data) {
                        data.push(elem.data);
                    }
                });
                return data;
            });
        }

        if(this.props.schema) {
            this.buildList();
        } else {
            console.log('GenericList without schema');
        }
    }

    buildList() {
        let items_data = [];
        if(this.props.data && 'object' === typeof this.props.data && Array.isArray(this.props.data)) {
            // todo: sometime when switching position with another block it throws a data error, seems like mounting the element before data is transferred from old to new item
            const items = [...this.props.data];
            items.forEach(elem => {
                items_data.push({
                    data: elem,
                    schema: {
                        type: 'object',
                        properties: this.props.schema['properties']
                    }
                });
            });
        }

        this.setState({list: items_data});
    }

    createItem() {
        let list = [...this.state.list];
        list.push({
            data: {},
            schema: {
                type: 'object',
                properties: this.props.schema['properties']
            }
        });
        this.setState({list: list});
    }

    itemDelete(key) {
        let list = [...this.state.list];
        if(list.hasOwnProperty(key)) {
            setTimeout(() => {
                list.splice(key, 1);
                this.setState({list: list});
            }, 0);
        }
    }

    itemAddSave = (index, evt) => {
        this.evts[index] = evt;
    };

    itemRmSave = (index) => {
        if(this.evts[index]) {
            delete this.evts[index];
        }
    };

    itemSave = (index) => {
        if(this.evts.hasOwnProperty(index)) {
            return this.evts[index]();
        }
        return null;
    };

    itemSaveAll = () => {
        const list = [...this.state.list];
        Object.keys(this.evts).forEach((index) => {
            const item_val = this.itemSave(index);
            if(null !== item_val && list.hasOwnProperty(index)) {
                const item = {...list[index]};
                item.data = {...item_val};
                list[index] = item;
            }
        });

        return list;
    };

    onDragStart = () => {
        document.body.style.cursor = 'grabbing';
    };

    onDragEnd = ({oldIndex, newIndex}) => {
        let list = this.itemSaveAll();
        const movingItem = list[oldIndex];
        list.splice(oldIndex, 1);
        list.splice(newIndex, 0, movingItem);

        document.body.style.removeProperty('cursor');

        this.setState({
            prevCursor: false,
            list
        });
    };

    render() {
        const {classes, theme} = this.props;

        const sortable = true;

        return (
            <div className={classes.wrapper}>
                <SortContainer updateBeforeSortStart={this.onDragStart}
                               onSortEnd={this.onDragEnd}
                               useWindowAsScrollContainer={true}
                               axis={'xy'}
                               pressDelay={100}
                               useDragHandle={true}>
                    {this.state.list.map((elem, i) => (
                        <SortItem key={elem + i}
                                  index={i}
                                  sortable={sortable}
                                  delete={() => {
                                      this.itemDelete(i)
                                  }}
                                  data={elem.data}
                                  schema={elem.schema}
                                  addSave={(evt) => {
                                      this.itemAddSave(i, evt);
                                  }}
                                  rmSave={() => {
                                      this.itemRmSave(i);
                                  }}
                                  cacheid={this.props.name + '#' + i}
                        />
                    ))}
                </SortContainer>
                <button className={classes.toggle} onClick={this.createItem.bind(this)}><MdAdd size={'1em'} color={theme.textColor}/></button>
            </div>
        );
    };
}

export default withTheme(injectSheet(styling)(GenericList));