import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import {TabPanel} from './TabPanel';
import {Icon} from './Icon';
import {NavDropdown} from './NavDropdown';
import ReactDOM from 'react-dom';
import Measure from 'react-measure';
import {getElementBorder, getElementMargin, getElementPadding} from './utils';
import {cleanEmptyChildren} from '../utils/functions';

const DROPDOWN_STYLES = {
    visibility: 'hidden',
    position: 'absolute',
    left: '-100%'
};

const TAB_PANEL_STYLES = {whiteSpace: 'nowrap'};

let a11yIdCounter = 1;

export class TabPanelOverflowContainer extends React.Component {
    static propTypes = {
        children: PropTypes.node
    };

    static defaultProps = {
        children: null
    };

    constructor(props, context) {
        super(props, context);
        this.tabPanelRef = React.createRef();
        this.dropdownRef = React.createRef();
        this.dropdownA11yId = 'dropdown' + a11yIdCounter++;

        this.state = {
            visibleAmount: -1,
            dropdownWidth: -1, // assume it's static and does not changed
            dimensions: {
                width: -1,
                right: -1
            }
        };
    }

    componentDidUpdate(prevProps, prevState) {
        let {visibleAmount, dimensions} = this.state;

        let dropdownNode = ReactDOM.findDOMNode(this.dropdownRef.current);
        if (dropdownNode) {
            let dropdownWidth = dropdownNode.getBoundingClientRect().width;
            if (this.state.dropdownWidth !== dropdownWidth) {
                this.setState({
                    dropdownWidth
                });
            }
        }

        let nextVisibleAmount = this.calculateVisibleAmount();
        let actualWidth = dimensions.width;
        let prevWidth = prevState.dimensions.width;
        let isDimensionsChanged = prevWidth !== actualWidth;
        let isChildrenChanged = prevProps.children.length !== this.props.children.length;

        if (isChildrenChanged || isDimensionsChanged) {
            // force to show all items to measure
            nextVisibleAmount = cleanEmptyChildren(this.props.children).length;
        }

        if (visibleAmount !== nextVisibleAmount) {
            this.setState({
                visibleAmount: nextVisibleAmount
            });
        }
    }

    calculateVisibleAmount() {
        let rootNode = ReactDOM.findDOMNode(this.tabPanelRef.current);
        let dropdownNode = ReactDOM.findDOMNode(this.dropdownRef.current);

        if (!rootNode) {
            return -1;
        }

        let children = Array.from(rootNode.children).filter(child => child !== dropdownNode);

        let dropdownNodeWidth = this.state.dropdownWidth;
        let rootNodePaddingRight = getElementPadding(rootNode).paddingRight;
        let rootNodeBorderRight = getElementBorder(rootNode).borderRight;

        let allowedRightBoundaryPoint =
            this.state.dimensions.right - rootNodePaddingRight - rootNodeBorderRight;

        let lastVisibleIndex = this.findLastVisibleIndex(children, allowedRightBoundaryPoint);
        let isOverflow = lastVisibleIndex !== cleanEmptyChildren(this.props.children).length - 1;

        if (isOverflow) {
            // in case of overflow, recalculate, taking into consideration that dropdown is on the screen
            let lastVisibleIndexIncludingDropdown = this.findLastVisibleIndex(
                children,
                allowedRightBoundaryPoint - dropdownNodeWidth
            );
            lastVisibleIndex = Math.min(lastVisibleIndex, lastVisibleIndexIncludingDropdown);
        }

        return lastVisibleIndex + 1;
    }

    findLastVisibleIndex(nodes, allowedRightBoundaryPoint) {
        let childIndex = nodes.length - 1;
        let visibleIndex = nodes.length - 1;

        while (childIndex >= 0) {
            let child = nodes[childIndex];
            childIndex--;

            let boundaryRect = child.getBoundingClientRect();
            let rightBoundaryPoint = boundaryRect.right;
            let childRightMargin = getElementMargin(child).marginRight;

            if (rightBoundaryPoint + childRightMargin > allowedRightBoundaryPoint) {
                visibleIndex--;
            } else {
                break;
            }
        }

        return visibleIndex;
    }

    handleResizeRootContainer = contentRect => {
        this.setState({
            dimensions: contentRect.bounds,
            visibleAmount: -1 // reset to recalculate visible amount
        });
    };

    render() {
        let {children: rawChildren, ...props} = this.props;
        let {visibleAmount, dropdownWidth} = this.state;

        let children = cleanEmptyChildren(rawChildren);

        visibleAmount = visibleAmount === -1 ? children.length : visibleAmount;

        let head = _.take(children, visibleAmount);
        let tail = _.takeRight(children, children.length - visibleAmount);

        let isOverflow = tail.length > 0;

        let resultChildren = head;
        let dropdown = null;

        if (dropdownWidth === -1 || isOverflow) {
            // show it to measure dropdown size, but out of viewport to avoid UI flicker
            let dropdownStyles = null;
            if (dropdownWidth === -1) {
                dropdownStyles = DROPDOWN_STYLES;
            }
            let id = this.dropdownA11yId;

            dropdown = (
                <NavDropdown
                    role="tab"
                    testAutomationId="menuItemMore"
                    id={id}
                    ref={this.dropdownRef}
                    className="pull-right"
                    title={<Icon name="three-dots" />}
                    style={dropdownStyles}>
                    {tail}
                </NavDropdown>
            );

            if (!isOverflow) {
                dropdown = null;
            }

            if (dropdown) {
                resultChildren.push(dropdown);
            }
        }

        let tabPanel = (
            <TabPanel ref={this.tabPanelRef} {...props} style={TAB_PANEL_STYLES}>
                {resultChildren}
            </TabPanel>
        );

        return (
            <Measure bounds onResize={this.handleResizeRootContainer}>
                {({measureRef}) => <div ref={measureRef}>{tabPanel}</div>}
            </Measure>
        );
    }
}
