import React from 'react';
import {SortableHandle, SortableContainer, SortableElement} from 'react-sortable-hoc';
import {MdDragHandle} from 'react-icons/md';
import posed from 'react-pose';

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

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

const Wrapper = posed.div({
    hidden: {
        height: 0,
        marginBottom: 0,
    },
    visible: {
        height: 'auto',
        marginBottom: '6px',
    }
});

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.multiple ? <DragHandle/> : null}
        <ButtonInteractiveDelete onClick={props.delete} size={'1.125em'} style={buttonStyle}/>

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

        <div style={{width: '100%'}}>
            <SchemaEditor data={props.data}
                          schema={props.schema}
                          addSave={props.addSave}
                          rmSave={props.rmSave}
                          cacheid={props.cacheid}
                          tScope={'file-folder'}
            />
        </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 FileFolderBase extends React.PureComponent {

    state = {
        openExplorer: false,
        choosenFiles: {},
        prevCursor: false,
        fileOrder: []
    };

    evts = {};

    componentWillUnmount() {
        this.evts = {};
        this.setState({
            openExplorer: false,
            choosenFiles: {},
            prevCursor: false,
            fileOrder: []
        });

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

    componentDidMount() {
        if(this.props.addSave) {
            // add save evt handle
            this.props.addSave(this.props.name, () => {
                const {choosenFiles, data} = this.saveAll();

                this.setState({choosenFiles});

                return {
                    file: [...this.state.fileOrder],
                    data: data
                };
            });
        }

        // create initial data out of props
        if(this.props.data && this.props.data.file) {
            const choosenFiles = {};
            const props = {...this.props};
            const data = {...props.data};
            const files = [...data.file];
            const fileOrder = [];
            files.forEach((file_name) => {
                choosenFiles[file_name] = {
                    addSave: (evt) => {
                        this.evts[file_name] = evt;
                    },
                    rmSave: () => {
                        if(this.evts[file_name]) {
                            delete this.evts[file_name];
                        }
                    },
                    delete: () => {
                        this.deleteItem(file_name);
                    }
                };

                // add default data, when existing
                if(this.props.data.data && this.props.data.data[file_name]) {
                    const schemaData = {...data.data};
                    choosenFiles[file_name].data = {...schemaData[file_name]};
                }

                fileOrder.push(file_name);
            });

            this.setState({
                choosenFiles,
                fileOrder
            });
        }
    }

    deleteItem = (name) => {
        if(this.evts.hasOwnProperty(name)) {
            delete this.evts[name];
        }

        const choosenFiles = {...this.state.choosenFiles};
        const fileOrder = [...this.state.fileOrder];

        if(choosenFiles.hasOwnProperty(name)) {
            delete choosenFiles[name];
        }

        if(-1 !== fileOrder.indexOf(name)) {
            fileOrder.splice(fileOrder.indexOf(name), 1);
        }

        this.setState({
            choosenFiles,
            fileOrder
        });
    };

    saveItem = (name) => {
        if(this.evts.hasOwnProperty(name)) {
            return this.evts[name]();
        }
        return false;
    };

    saveAll = () => {
        const choosenFiles = {...this.state.choosenFiles};
        const data = {};
        Object.keys(this.evts).forEach((item_name) => {
            const item_val = this.saveItem(item_name);
            if(item_val) {
                data[item_name] = {...item_val};
                const item = {...choosenFiles[item_name]};
                item.data = {...item_val};
                choosenFiles[item_name] = item;
            }
        });

        return {choosenFiles, data};
    };

    addSelected = (selected) => {
        let choosenFiles = {...this.state.choosenFiles};
        let fileOrder = [...this.state.fileOrder];

        const {multiple} = this.props;

        if(!multiple) {
            choosenFiles = {};
            fileOrder = [];
            this.evts = [];
        }

        if(selected.file) {
            Object.keys(selected.file).forEach((file_name) => {
                if(!choosenFiles.hasOwnProperty(file_name)) {
                    choosenFiles[file_name] = {
                        addSave: (evt) => {
                            this.evts[file_name] = evt;
                        },
                        rmSave: () => {
                            if(this.evts[file_name]) {
                                delete this.evts[file_name];
                            }
                        },
                        delete: () => {
                            this.deleteItem(file_name);
                        },
                        data: {}
                    };

                    fileOrder.push(file_name);
                }
            });
        }

        this.setState({
            choosenFiles,
            fileOrder
        });
    };

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

    onDragEnd = ({oldIndex, newIndex}) => {
        let choosenFiles = {...this.state.choosenFiles};
        let fileOrder = [...this.state.fileOrder];

        const movingFile = fileOrder[oldIndex];
        fileOrder.splice(oldIndex, 1);
        fileOrder.splice(newIndex, 0, movingFile);

        if(choosenFiles.hasOwnProperty(movingFile)) {
            const res = this.saveAll();
            choosenFiles = res.choosenFiles;
        }

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

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

    render() {
        const {
            t, theme, classes,
            selectFile, selectDir, multiple
        } = this.props;

        // todo: only file atm
        let replaceSingle = (!multiple && this.state.fileOrder.length);

        return (
            <div className={classes.wrapper}>
                <ButtonSmall onClick={() => {
                    this.setState({openExplorer: !this.state.openExplorer})
                }}
                             style={{marginBottom: theme.gutter.base * 2 + 'px'}}
                >{this.state.openExplorer ? t('file-folder.close-explorer') :
                    selectDir && selectFile ? multiple ? t('file-folder.add-items') : (replaceSingle ? t('file-folder.replace-item') : t('file-folder.add-item')) :
                        selectFile ? multiple ? t('file-folder.add-files') : (replaceSingle ? t('file-folder.replace-file') : t('file-folder.add-file')) :
                            selectDir ? multiple ? t('file-folder.add-dirs') : (replaceSingle ? t('file-folder.replace-dir') : t('file-folder.add-dir')) :
                                t('file-folder.open-explorer')
                }</ButtonSmall>

                <Wrapper className={classes.wrapperSelect} withParent={false} pose={this.state.openExplorer ? 'visible' : 'hidden'}>
                    <ExplorerSelect selectFile={selectFile}
                                    choosenFiles={this.state.fileOrder}
                                    selectDir={selectDir}
                                    multiple={multiple}
                                    save={this.addSelected}/>
                </Wrapper>

                <SortContainer updateBeforeSortStart={this.onDragStart}
                               onSortEnd={this.onDragEnd}
                               useWindowAsScrollContainer={true}
                               axis={'xy'}
                               pressDelay={100}
                               useDragHandle={true}>
                    {this.state.fileOrder.map((elem, i) => (
                        <SortItem key={elem + i}
                                  index={i}
                                  name={elem}
                                  multiple={multiple}
                                  delete={this.state.choosenFiles[elem].delete}
                                  data={this.state.choosenFiles[elem].data}
                                  schema={{
                                      type: 'object',
                                      properties: this.props.schema['properties']
                                  }}
                                  addSave={this.state.choosenFiles[elem].addSave}
                                  rmSave={this.state.choosenFiles[elem].rmSave}
                                  cacheid={this.props.name + '#' + i}
                        />
                    ))}
                </SortContainer>
            </div>
        );
    }
    ;
}

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