import * as React from 'react';
import * as _ from 'lodash';
import * as DefaultStyles from './style';

import { PAGER_ELEMENT, Control, PrevControls, NextControls, PrevSetControl, NextSetControl } from './constants';
import { PagerControl } from './PagerControl';

export namespace Pager {
    export interface Props {
        pagesToShow: number;
        totalItems: number;
        itemsPerPage: number
        startingPage?: number;
        styles?: Styles;
        onPageChange: (newPage: number) => void;
        id?: string;
        name?: string;
    }

    export interface State {
        currentPageNumber: number
    }

    export interface Styles {
        Container?: any;
        PagerContainer?: any;
        PagerItemSelected?: any;
        PagerItemUnselected?: any;
        PagerItemContent?: any;
    }
}

export default class Pager extends React.Component<Pager.Props, Pager.State> {
    constructor(props: Pager.Props) {
        super(props);
        this.state = { currentPageNumber: props.startingPage ? props.startingPage : 0 };
    }

    componentWillReceiveProps(props: Pager.Props): void {
        if (props.startingPage) this.setState({ currentPageNumber: props.startingPage })
    }

    onPagerItemClicked = (action: string): void => {
        const pagesToShow: number = this.props.pagesToShow;
        const totalItems: number = this.getTotalPages();
        let currentPageNumber: number = this.state.currentPageNumber;
        const currentPageSet: number = this.getCurrentPageSet(currentPageNumber);

        switch (action) {
            case PAGER_ELEMENT.NEXT_SET: {
                currentPageNumber = (currentPageSet + 1) * pagesToShow;
                break;
            }
            case PAGER_ELEMENT.PREVIOUS_SET: {
                currentPageNumber = (currentPageSet - 1) * pagesToShow;
                break;
            }
            case PAGER_ELEMENT.NEXT_PAGE: {
                currentPageNumber = Math.min(this.state.currentPageNumber + 1, totalItems - 1);
                break;
            }
            case PAGER_ELEMENT.PREVIOUS_PAGE: {
                currentPageNumber = Math.max(this.state.currentPageNumber - 1, 0);
                break;
            }
            case PAGER_ELEMENT.LAST_PAGE: {
                currentPageNumber = totalItems - 1;
                break;
            }
            case PAGER_ELEMENT.FIRST_PAGE: {
                currentPageNumber = 0;
                break;
            }
            default: {
                const pageSelected = _.toNumber(action) - 1;
                currentPageNumber = pageSelected;
                break;
            }
        }

        this.setState({ currentPageNumber })
        this.props.onPageChange(currentPageNumber);
    }

    getCurrentPageSet = (currentPageNumber: number): number => {
        return (currentPageNumber < this.props.pagesToShow) ? 0 : Math.floor(currentPageNumber / this.props.pagesToShow);
    }

    getPagesInCurrentPageSet = (currentPageNumber: number, currentPageSet: number, totalPageSet: number, totalItems: number): number[] => {
        const totalPages: number = this.getTotalPages()
        const pagesToShow: number = this.props.pagesToShow;

        if ((totalPages % (pagesToShow) !== 0) && (currentPageNumber >= ((totalPageSet - 1) * pagesToShow))) {
            const rest: number = totalPages - ((totalPageSet - 1) * pagesToShow)
            return _.times(rest, (i) => ((totalPageSet - 1) * pagesToShow) + i + 1);
        }

        return _.times(Math.min(totalPages, pagesToShow), (i) => (currentPageSet * pagesToShow) + i + 1);
    }

    getTotalPageSets = (): number => {
        const totalPages: number = this.getTotalPages();
        const pagesToShow: number = this.props.pagesToShow;
        return (totalPages < pagesToShow ? 1 : 0) + Math.floor(totalPages / pagesToShow) + (totalPages % pagesToShow !== 0 ? 1 : 0);
    }

    getTotalPages = (): number => {
        return Math.floor(this.props.totalItems / this.props.itemsPerPage) + (this.props.totalItems % this.props.itemsPerPage === 0 ? 0 : 1);
    }

    renderControls = (styles: Pager.Styles, controlList: Control[]): JSX.Element[] => {
        return controlList.map(control => {
            return (
                <PagerControl
                    key={control.action}
                    isSelected={true}
                    onPagerItemClicked={this.onPagerItemClicked}
                    control={control}
                    styles={{
                        PagerItemSelected: styles.PagerItemSelected,
                        PagerItemUnselected: styles.PagerItemUnselected,
                        PagerItemContent: styles.PagerItemContent
                    }}
                />
            )
        })
    }

    renderPages = (Styles: Pager.Styles, pagesInPageSet: number[]): JSX.Element[] => {
        return pagesInPageSet.map(i => {
            const pageNumber: number = i
            const label: string = "" + pageNumber;
            const pagerItemContent: JSX.Element = <Styles.PagerItemContent data-testid="pager_item_component" id={this.props.id && this.props.id + '-pager-item-content'} name={this.props.name && this.props.name + '-pager-item-content'}> {label} </Styles.PagerItemContent>;
            const pagerItem: JSX.Element = pageNumber === this.state.currentPageNumber + 1 ?
                <Styles.PagerItemSelected key={i}
                    children={pagerItemContent}
                    onClick={() => this.onPagerItemClicked(label)}
                    id={this.props.id && this.props.id + '-pager-item-selected'}
                    name={this.props.name && this.props.name + '-pager-item-selected'}
                /> :
                <Styles.PagerItemUnselected key={i}
                    children={pagerItemContent}
                    onClick={() => this.onPagerItemClicked(label)}
                    id={this.props.id && this.props.id + '-pager-item-unselected'}
                    name={this.props.name && this.props.name + '-pager-item-unselected'}
                />;
            return pagerItem
        })
    }

    renderPageSetNav = (styles: Pager.Styles, action: Control): JSX.Element => {
        return <PagerControl
            isSelected={false}
            onPagerItemClicked={this.onPagerItemClicked}
            control={action}
            styles={{
                PagerItemSelected: styles.PagerItemSelected,
                PagerItemUnselected: styles.PagerItemUnselected,
                PagerItemContent: styles.PagerItemContent
            }}
        />;
    }

    render(): JSX.Element {
        const Styles: Pager.Styles = _.merge(DefaultStyles, this.props.styles);

        const totalPages: number = this.getTotalPages();
        const totalPageSet: number = this.getTotalPageSets();
        const currentPageSet: number = this.getCurrentPageSet(this.state.currentPageNumber);
        const pagesInPageSet: number[] = this.getPagesInCurrentPageSet(this.state.currentPageNumber, currentPageSet, totalPageSet, this.props.totalItems);

        const canShowPrevSetControl: boolean = currentPageSet > 0;
        const canShowNextSetControl: boolean = (currentPageSet + 1) * this.props.pagesToShow < totalPages;

        return (
            <Styles.Container id={this.props.id && this.props.id + '-container'} name={this.props.name && this.props.name + '-container'}>
                <Styles.PagerContainer data-testid="pager_control_component" id={this.props.id && this.props.id + '-page-container'} name={this.props.name && this.props.name + '-page-container'}>
                    {this.renderControls(Styles, PrevControls)}
                </Styles.PagerContainer>
                <Styles.PagerContainer id={this.props.id && this.props.id + '-page-container-2'} name={this.props.name && this.props.name + '-page-container-2'}>
                    {canShowPrevSetControl && this.renderPageSetNav(Styles, PrevSetControl)}
                    {this.renderPages(Styles, pagesInPageSet)}
                    {canShowNextSetControl && this.renderPageSetNav(Styles, NextSetControl)}
                </Styles.PagerContainer>
                <Styles.PagerContainer data-testid="pager_control_component" id={this.props.id && this.props.id + '-page-container-3'} name={this.props.name && this.props.name + '-page-container-3'}>
                    {this.renderControls(Styles, NextControls)}
                </Styles.PagerContainer>
            </Styles.Container>
        );
    }
}
