import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { CommandRegistry } from '@theia/core/lib/common/command';
import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { Port } from '../../common/protocol';
import { BoardsConfig } from './boards-config';
import { ArduinoCommands } from '../arduino-commands';
import { BoardsServiceProvider, AvailableBoard } from './boards-service-provider';

export interface BoardsDropDownListCoords {
    readonly top: number;
    readonly left: number;
    readonly width: number;
    readonly paddingTop: number;
}

export namespace BoardsDropDown {
    export interface Props {
        readonly coords: BoardsDropDownListCoords | 'hidden';
        readonly items: Array<AvailableBoard & { onClick: () => void, port: Port }>;
        readonly openBoardsConfig: () => void;
    }
}

export class BoardsDropDown extends React.Component<BoardsDropDown.Props> {

    protected dropdownElement: HTMLElement;

    constructor(props: BoardsDropDown.Props) {
        super(props);

        let list = document.getElementById('boards-dropdown-container');
        if (!list) {
            list = document.createElement('div');
            list.id = 'boards-dropdown-container';
            document.body.appendChild(list);
            this.dropdownElement = list;
        }
    }

    render(): React.ReactNode {
        return ReactDOM.createPortal(this.renderNode(), this.dropdownElement);
    }

    protected renderNode(): React.ReactNode {
        const { coords, items } = this.props;
        if (coords === 'hidden') {
            return '';
        }
        return <div className='arduino-boards-dropdown-list'
            style={{
                position: 'absolute',
                ...coords
            }}>
            {this.renderItem({
                label: 'Select Other Board & Port',
                onClick: () => this.props.openBoardsConfig()
            })}
            {items.map(({ name, port, selected, onClick }) => ({ label: `${name} at ${Port.toString(port)}`, selected, onClick })).map(this.renderItem)}
        </div>
    }

    protected renderItem({ label, selected, onClick }: { label: string, selected?: boolean, onClick: () => void }): React.ReactNode {
        return <div key={label} className={`arduino-boards-dropdown-item ${selected ? 'selected' : ''}`} onClick={onClick}>
            <div>
                {label}
            </div>
            {selected ? <span className='fa fa-check' /> : ''}
        </div>
    }

}

export class BoardsToolBarItem extends React.Component<BoardsToolBarItem.Props, BoardsToolBarItem.State> {

    static TOOLBAR_ID: 'boards-toolbar';

    protected readonly toDispose: DisposableCollection = new DisposableCollection();

    constructor(props: BoardsToolBarItem.Props) {
        super(props);

        const { availableBoards } = props.boardsServiceClient;
        this.state = {
            availableBoards,
            coords: 'hidden'
        };

        document.addEventListener('click', () => {
            this.setState({ coords: 'hidden' });
        });
    }

    componentDidMount() {
        this.props.boardsServiceClient.onAvailableBoardsChanged(availableBoards => this.setState({ availableBoards }));
    }

    componentWillUnmount(): void {
        this.toDispose.dispose();
    }

    protected readonly show = (event: React.MouseEvent<HTMLElement>) => {
        const { currentTarget: element } = event;
        if (element instanceof HTMLElement) {
            if (this.state.coords === 'hidden') {
                const rect = element.getBoundingClientRect();
                this.setState({
                    coords: {
                        top: rect.top,
                        left: rect.left,
                        width: rect.width,
                        paddingTop: rect.height
                    }
                });
            } else {
                this.setState({ coords: 'hidden' });
            }
        }
        event.stopPropagation();
        event.nativeEvent.stopImmediatePropagation();
    };

    render(): React.ReactNode {
        const { coords, availableBoards } = this.state;
        const boardsConfig = this.props.boardsServiceClient.boardsConfig;
        const title = BoardsConfig.Config.toString(boardsConfig, { default: 'no board selected' });
        const decorator = (() => {
            const selectedBoard = availableBoards.find(({ selected }) => selected);
            if (!selectedBoard || !selectedBoard.port) {
                return 'fa fa-times notAttached'
            }
            if (selectedBoard.state === AvailableBoard.State.guessed) {
                return 'fa fa-exclamation-triangle guessed'
            }
            return ''
        })();

        return <React.Fragment>
            <div className='arduino-boards-toolbar-item-container'>
                <div className='arduino-boards-toolbar-item' title={title}>
                    <div className='inner-container' onClick={this.show}>
                        <span className={decorator} />
                        <div className='label noWrapInfo'>
                            <div className='noWrapInfo noselect'>
                                {title}
                            </div>
                        </div>
                        <span className='fa fa-caret-down caret' />
                    </div>
                </div>
            </div>
            <BoardsDropDown
                coords={coords}
                items={availableBoards.filter(AvailableBoard.hasPort).map(board => ({
                    ...board,
                    onClick: () => {
                        if (board.state === AvailableBoard.State.incomplete) {
                            this.props.boardsServiceClient.boardsConfig = {
                                selectedPort: board.port
                            };
                            this.openDialog();
                        } else {
                            this.props.boardsServiceClient.boardsConfig = {
                                selectedBoard: board,
                                selectedPort: board.port
                            }
                        }
                    }
                }))}
                openBoardsConfig={this.openDialog}>
            </BoardsDropDown>
        </React.Fragment>;
    }

    protected openDialog = () => {
        this.props.commands.executeCommand(ArduinoCommands.OPEN_BOARDS_DIALOG.id);
        this.setState({ coords: 'hidden' });
    };

}
export namespace BoardsToolBarItem {

    export interface Props {
        readonly boardsServiceClient: BoardsServiceProvider;
        readonly commands: CommandRegistry;
    }

    export interface State {
        availableBoards: AvailableBoard[];
        coords: BoardsDropDownListCoords | 'hidden';
    }

}
