import * as React from 'react';
import * as PropTypes from 'prop-types';
import classnames from 'classnames';

import {FlexBlockItem} from './FlexBlockItem';

enum FlexBlockDirectionProperty {
    ROW = 'row',
    COLUMN = 'column'
}

enum FlexBlockAlignProperty {
    CENTER = 'center',
    START = 'start',
    END = 'end'
}

enum FlexBlockJustifyProperty {
    CENTER = 'center',
    START = 'start',
    END = 'end',
    SPACE_BETWEEN = 'space-between',
    SPACE_AROUND = 'space-around',
    SPACE_EVENLY = 'space-evenly'
}

type FlexBlockProps = {
    children?: React.ReactNode;
    className?: string;
    direction: FlexBlockDirectionProperty;
    align?:
        | FlexBlockAlignProperty
        | FlexBlockJustifyProperty.CENTER
        | FlexBlockJustifyProperty.START
        | FlexBlockJustifyProperty.END;
    justify?: FlexBlockJustifyProperty;
    gutterWidth: number;
    shouldFlexChildren: boolean;
    shouldStretchChildren: boolean;
    shouldWrap: boolean;
    shouldUnwrapChildren: boolean;
    childWrapperStyle: React.CSSProperties;
    style: React.CSSProperties;
};

export class FlexBlock extends React.PureComponent<FlexBlockProps> {
    static Item = FlexBlockItem;

    static ROW = FlexBlockDirectionProperty.ROW;
    static COLUMN = FlexBlockDirectionProperty.COLUMN;

    static CENTER: FlexBlockJustifyProperty.CENTER = FlexBlockJustifyProperty.CENTER;
    static START: FlexBlockJustifyProperty.START = FlexBlockJustifyProperty.START;
    static END: FlexBlockJustifyProperty.END = FlexBlockJustifyProperty.END;
    static SPACE_BETWEEN = FlexBlockJustifyProperty.SPACE_BETWEEN;
    static SPACE_AROUND = FlexBlockJustifyProperty.SPACE_AROUND;
    static SPACE_EVENLY = FlexBlockJustifyProperty.SPACE_EVENLY;

    static propTypes = {
        children: PropTypes.node,
        className: PropTypes.string,
        direction: PropTypes.oneOf([
            FlexBlockDirectionProperty.ROW,
            FlexBlockDirectionProperty.COLUMN
        ]),
        align: PropTypes.oneOf([
            FlexBlockJustifyProperty.CENTER,
            FlexBlockJustifyProperty.START,
            FlexBlockJustifyProperty.END
        ]),
        justify: PropTypes.oneOf([
            FlexBlockAlignProperty.CENTER,
            FlexBlockAlignProperty.START,
            FlexBlockAlignProperty.END
        ]),
        gutterWidth: PropTypes.number,
        shouldFlexChildren: PropTypes.bool,
        shouldWrap: PropTypes.bool,
        shouldUnwrapChildren: PropTypes.bool,
        childWrapperStyle: PropTypes.object,
        style: PropTypes.object
    };

    static defaultProps = {
        children: null,
        className: null,
        direction: FlexBlockDirectionProperty.ROW,
        align: null,
        justify: null,
        gutterWidth: 5,
        shouldFlexChildren: false,
        shouldStretchChildren: false,
        shouldWrap: false,
        shouldUnwrapChildren: false,
        childWrapperStyle: {},
        style: {}
    };

    wrapChild = (child: React.ReactElement<any>, index) => {
        const {
            gutterWidth,
            shouldFlexChildren,
            shouldStretchChildren,
            childWrapperStyle
        } = this.props;

        if (!child) {
            return child;
        }

        const style: React.CSSProperties = {};

        const className = classnames(child.props && child.props.className, 'rc-flex-block-child', {
            'rc-flex-block-child-flex': shouldFlexChildren,
            'rc-flex-block-child-stretch': shouldStretchChildren
        });

        if (gutterWidth) {
            style.padding = `${gutterWidth / 2}px`;
        }

        return child.type === FlexBlockItem ? (
            React.cloneElement(child, {
                style: {
                    ...style,
                    ...childWrapperStyle,
                    ...child.props.style
                }
            })
        ) : (
            <div
                key={child.key || index}
                className={className}
                style={{...style, ...childWrapperStyle}}>
                {child}
            </div>
        );
    };

    renderChildren() {
        const {children, shouldUnwrapChildren} = this.props;

        if (shouldUnwrapChildren) {
            return children;
        }

        return React.Children.map(children, this.wrapChild);
    }

    render() {
        const {direction, align, justify, shouldWrap, gutterWidth, style} = this.props;

        const className = classnames('rc-flex-block', this.props.className, {
            'rc-flex-block-vertical': direction === FlexBlockDirectionProperty.COLUMN,
            'rc-flex-block-wrap': shouldWrap,
            'rc-flex-block-align-start': align === FlexBlockAlignProperty.START,
            'rc-flex-block-align-center': align === FlexBlockAlignProperty.CENTER,
            'rc-flex-block-align-end': align === FlexBlockAlignProperty.END,
            'rc-flex-block-justify-start': justify === FlexBlockJustifyProperty.START,
            'rc-flex-block-justify-center': justify === FlexBlockJustifyProperty.CENTER,
            'rc-flex-block-justify-end': justify === FlexBlockJustifyProperty.END,
            'rc-flex-block-justify-space-around': justify === FlexBlockJustifyProperty.SPACE_AROUND,
            'rc-flex-block-justify-space-between':
                justify === FlexBlockJustifyProperty.SPACE_BETWEEN,
            'rc-flex-block-justify-space-evenly': justify === FlexBlockJustifyProperty.SPACE_EVENLY
        });

        const containerStyle: React.CSSProperties = {...style};

        if (gutterWidth) {
            containerStyle.margin = `-${gutterWidth / 2}px`;
        }

        return (
            <div className={className} style={containerStyle}>
                {this.renderChildren()}
            </div>
        );
    }
}
