import * as React from 'react'
import * as cx from 'classnames'
import { sum } from 'lodash'
import { connect } from 'react-redux'

import { Column } from '../flex'
import { Row } from '../flex'
import { StylableComponent } from '../theme'
import { childrenChanged, forceArray } from '../../utils'

import {
    panelInit,
    panelDestroy,
    panelResizeStart,
    panelResize,
    panelResizeStop,
} from './actions'
import { getPanelById } from './selectors'

import { PanelComponentProps, PanelProps } from './interfaces'

class Component extends StylableComponent<PanelComponentProps> {
    private container: HTMLDivElement

    public componentWillMount() {
        this.props.panelInit(this.props.id, this.props.defaultIntervals)
    }

    public componentWillUnmount() {
        // this.props.panelDestroy(this.props.id)
    }

    public render(): JSX.Element {
        return (
            <div
                ref={this.setContainerRef}
                style={{
                    display: 'flex',
                    overflow: 'hidden',
                    width: '100%',
                    height: '100%',
                    flexDirection: this.layout('row', 'column'),
                }}
                className={this.props.className}
                onMouseMove={this.handleMouseMove}
                onMouseUp={this.handleStopResizing}>
                {this.childNodes()}
            </div>
        )
    }

    private childNodes(): JSX.Element[] {
        const children = (forceArray(this.props.children) as JSX.Element[])
            .filter(Boolean)
            .map((child: JSX.Element, idx: number) =>
                <div
                    style={{
                        flexBasis: `${this.intervals()[idx]}%`,
                    }}>
                    {child}
                </div>,
            )
            .reduce(
            (acc: JSX.Element[], child: JSX.Element, idx: number) => [
                ...acc,
                child,
                <Separator
                    width={this.props.separatorWidth}
                    layout={this.props.layout}
                    onMouseDown={this.mkHandleStartResizing(idx)} />,
            ],
            [],
        )
        // TODO: Ew.
        return children.slice(0, children.length - 1)
    }

    private intervals(): number[] {
        const { panel } = this.props
        if (!panel) return this.props.defaultIntervals
        return panel.intervals
    }

    private setContainerRef = (ref: HTMLDivElement) => {
        this.container = ref
    }

    private mkHandleStartResizing = (idx: number) =>
        (e: React.MouseEvent<any>) => {
            e.preventDefault()
            e.stopPropagation()
            this.props.panelResizeStart(
                this.props.id,
                idx,
            )
        }

    private getPercentage(e: React.MouseEvent<any>): number {
        const cursorPosition = this.layout(e.clientX, e.clientY)
        const rect = this.container.getBoundingClientRect()
        const size = this.layout(rect.width, rect.height)
        const intervals = this.intervals()
        const adjustedPos = cursorPosition - this.layout(rect.left, rect.top)
        return 100 * (adjustedPos / size)
    }

    private handleMouseMove = (e: React.MouseEvent<any>) => {
        const { panel } = this.props
        if (!panel || !panel.dragging) return false
        const { idx } = panel
        const intervals = this.intervals()
        const beforeIntervals = intervals.slice(0, idx)
        const afterIntervals = intervals.slice(idx + 2, intervals.length)
        const relevantIntervals = intervals.slice(idx, idx + 2)
        const [a, b] = relevantIntervals
        const nextA = this.getPercentage(e) - sum(beforeIntervals)
        const nextB = 100 - (nextA + sum(afterIntervals) + sum(beforeIntervals))
        const nextIntervals = [...beforeIntervals, nextA, nextB, ...afterIntervals]
        this.props.panelResize(this.props.id, nextIntervals)

    }

    private handleStopResizing = (e: React.SyntheticEvent<any>) => {
        e.stopPropagation()
        const { panel } = this.props
        if (!panel || !panel.dragging) return false
        this.props.panelResizeStop(this.props.id)
    }

    private layout = (columns: any, rows: any): any => this.props.layout === 'columns'
        ? columns
        : rows

}

const separatorWidth = (width: number | undefined) =>
    `${width !== undefined
        ? width
        : 3}px`

const Separator = ({ layout, onMouseDown, width }: any) =>
    <div
        style={{
            backgroundColor: 'transparent',
            height: layout === 'columns' ? '100%' : separatorWidth(width),
            width: layout === 'columns' ? separatorWidth(width) : '100%',
            cursor: layout === 'columns' ? 'ew-resize' : 'ns-resize',
        }}
        onMouseDown={onMouseDown}
    />

const mapStateToProps
    = (state: any, ownProps: PanelProps) => ({
        ...ownProps,
        panel: getPanelById(state, ownProps.id),
    })

const mapDispatchToProps = {
    panelInit,
    panelDestroy,
    panelResizeStart,
    panelResize,
    panelResizeStop,
}

export const Panel = connect(mapStateToProps, mapDispatchToProps)(Component)
