/**
 * Container component for displaying cardPanel in a dynamic grid
 * allows you to configure the width, height, and position of the cardPanel, which are its descendants
 *
 * @author Brauer Ilya
 * @date 2020-04-15
 */

import * as React from 'react';
import * as GridLayout from 'react-grid-layout';

import 'react-grid-layout/css/styles.css';
import 'react-resizable/css/styles.css';

import * as styles from './cardPanel.m.scss';

import {propsGetter} from '../../utils/propsGetter';
import {joinClassNames} from '../../index';
import debounce from 'lodash/debounce';

type IProps = {
    isDraggable?: boolean;
    isResizable?: boolean;
    onLayoutChange?: (layout: GridLayout.Layout []) => void;
    draggableHandle?: string;
    layout?: GridLayout.Layout [];
    margin?: [number, number];
    cols?: number;
} & Partial<DefaultProps>;

const defaultProps = {
    rowHeight: 110,
    isDraggable: true,
    isResizable: true
};

type DefaultProps = Readonly<typeof defaultProps>;

const getProps = propsGetter(defaultProps);

interface IState {
    width: number;
}

export type PanelGridLayout = GridLayout.Layout;

export type GridActionCallback = GridLayout.ItemCallback;

export class CardPanelGrid extends React.PureComponent<IProps, IState> {

    static defaultProps = defaultProps;

    gridLayoutContainer = React.createRef<HTMLDivElement>();

    override state = {
        width: 0
    };

    constructor (props: IProps) {
        super(props);

        this.onWindowResize = debounce(this.onWindowResize.bind(this), 800, {
            leading: true,
            trailing: false
        });
    }

    override componentDidMount (): void {
        this.calculateGridWidth(this.state.width);
        window.addEventListener('resize', this.onWindowResize);
    }

    override componentDidUpdate (prevProps: Readonly<IProps>, prevState: Readonly<IState>, snapshot?: any): void {
        if (prevState.width !== 0) {
            this.calculateGridWidth(prevState.width);
        }
    }

    override componentWillUnmount (): void {
        window.removeEventListener('resize', this.onWindowResize);
    }

    onWindowResize = () => {
        if (this.gridLayoutContainer.current) {
            const width = this.gridLayoutContainer.current.clientWidth;

            this.setState({width});
        }
    }

    calculateGridWidth (prevWidth: number) {
        if (this.gridLayoutContainer.current) {
            const width = this.gridLayoutContainer.current.clientWidth;

            if (width !== prevWidth) {
                this.setState({width});
            }
        }
    }

    override render () {
        const {rowHeight} = getProps(this.props);

        const containerClassName = joinClassNames(
            [styles.gridLayout, Boolean(this.props.isDraggable || this.props.isResizable)]
        );

        return (
            <div ref={this.gridLayoutContainer} className={containerClassName}>
                <GridLayout
                    cols={this.props.cols}
                    width={this.state.width}
                    rowHeight={rowHeight}
                    margin={this.props.margin}
                    layout={this.props.layout}
                    onLayoutChange={this.props.onLayoutChange}
                    isDraggable={this.props.isDraggable}
                    isResizable={this.props.isResizable}
                    draggableHandle={this.props.draggableHandle}
                >
                    {React.Children.map(this.props.children, (Child: React.ReactElement<{
                        dataGrid?: GridLayout.Layout;
                        'data-grid'?: GridLayout.Layout;
                    }>, index) => {

                        if (React.isValidElement(Child)) {
                            let dataGrid: Partial<PanelGridLayout> = {};

                            const defaultItemProp: Partial<PanelGridLayout> = Child.props.dataGrid || Child.props['data-grid'] || {};

                            if (this.props.layout) {
                                const grid = this.props.layout.find((item) => {
                                    return item.i === defaultItemProp.i || item.i === Child.key;
                                });

                                if (grid) {
                                    dataGrid = grid;
                                } else {
                                    dataGrid = defaultItemProp;
                                }
                            }


                            return (
                                <div data-grid={{...dataGrid}}>
                                    {Child}
                                </div>
                            );
                        }

                        return null;
                    })}
                </GridLayout>
            </div>
        );
    }
}
