/* global window */

import React from 'react';
import styled, { css } from 'styled-components';
import PropTypes from 'prop-types';
import { transparentize } from 'polished';

import {
    flex_row, flex_col, icon_base,
} from '@descarteslabs/pizzazz/styles/mixins';
import * as colors from '@descarteslabs/pizzazz/styles/colors';
import * as shadows from '@descarteslabs/pizzazz/styles/shadows';
import * as dimensions from '@descarteslabs/pizzazz/styles/dimensions';
import * as fonts from '@descarteslabs/pizzazz/styles/fonts';
import ShareIcon from '@descarteslabs/pizzazz/styles/icons/share.svg';
import CloseIcon from '@descarteslabs/pizzazz/styles/icons/close_small.svg';
import LoadingSpinner from '@descarteslabs/pizzazz/styles/icons/loading-blue.svg';

import Toast from '@descarteslabs/cycle-widgets/es2015/Toast';
import Alert from '@descarteslabs/cycle-widgets/es2015/Alert';
import Banner from '@descarteslabs/cycle-widgets/es2015/Banner';
import { getSelectors, accessClass } from '@descarteslabs/cycle-widgets/es2015/utils/access';

import ContextMenu from '../ContextMenu';
import StaticXYZPanel from '../StaticXYZPanel';
import SharePanel from '../SharePanel';
import { fullAccess, groupAccess } from '../util/access';

import { isMacOS, isNotEditable, getErrorPreamble } from './utils';

// Get CSS selectors that disable access for certain features, based on user groups.
let accessSelectors = '';
if (window.descartes_user) {
    accessSelectors = getSelectors(window.descartes_user.groups, groupAccess, fullAccess);
}

// TODO: Factor some of these components out into child components.

/* Styled components. */

// Top-level container.
const MainContainer = styled.div.attrs({ tabIndex: 0 })`
    ${flex_row()}
    height: inherit;
    width: inherit;
    position: relative;
    ${accessSelectors}
`;
const NavContainer = styled.div`
    height: inherit;
    z-index: 5;
`;

/* Main content wrappers. */

const MainContentContainer = styled.div`
    position: relative;
    flex: 1;
    ${flex_col()};

    & .ModalContainer {
        position: initial;
        z-index: 3;

        & .ModalOverlay {
            height: 100%;
            width: 100%;
            z-index: 1;
        }

        & .ModalContentContainer {
            padding: 0;
            overflow: initial;
            z-index: 1;

            & .ModalVtreeContainer {
                width: 100%;
                overflow: auto;
            }

            & .ButtonsContainer {
                min-height: 4rem;
                padding: 1rem 0;
            }
        }
    }
`;
const ContentWrapper = styled.div`
    ${flex_row({ align: 'stretch', justify: 'stretch' })}
    position: relative;
    flex: 1 auto;
    filter: ${({ isLoading }) => (isLoading ? 'grayscale(80%) contrast(80%)' : null)}
`;

/* Main application content containers. */

const PanelContainer = styled.div`
    background-color: ${colors.fill[2]};
    position: absolute;
    height: auto;
    z-index: 1;
    ${shadows.medium}
`;

const TimelineContainer = styled.div`
    z-index: 2;
    height: 7rem;
    width: 100%;
    background: ${colors.primary.dark};
    position: absolute;
    bottom: 0
`;

const MapContainer = styled.div`
    ${({ modifying, drawing }) => (!modifying && drawing
        ? css`
            cursor: initial;
        `
        : css`
            &:active {
                cursor: grabbing;
            }

            cursor: grab;
        `
    )}
    &:focus {
        outline: none;
    }
    width: 100%;

    & .ol-control {
        background-color: ${colors.fill[0]};
        border-radius: 3px;
        padding: 0;
        ${shadows.small};

        & button {
            margin: 0;
            color: ${colors.primary.dark};
            height: ${dimensions.medium};
            width: ${dimensions.medium};
            font-weight: ${fonts.weights.light};
            font-size: ${fonts.sizes.x_large};
            background-color: ${colors.fill[0]};
            border-radius: 3px;
            transition: background-color 0.4s ease;

            &:hover {
                cursor: pointer;
                background-color: ${colors.fill[3]};
            }
            &:focus {
                outline: none;
                background-color: ${colors.fill[1]};
            }
        }
    }

    & .ol-overviewmap {
        left: auto;
        right: 0.375rem;
        bottom: 8rem;
        box-shadow: none;

        & button {
            ${shadows.small}
        }

        &:not(.ol-collapsed) {
            right: 3rem;
            bottom: 0.375rem;
            ${shadows.medium}
            background: white;

            & button {
                bottom: auto;
                left: auto;
                right: -2.625rem;
                top: 0;
            }

            & .ol-overviewmap-map {
                display: block;
            }
        }

        & .ol-overviewmap-map {
            margin: 0;
            border: none;
            height: 9.875rem;
            width: 15vw;

            & .ol-overviewmap-box {
                border: 2px dotted ${colors.red};
            }
        }
    }

    & .ol-zoom {
        bottom: ${dimensions.base_small};
        right: ${dimensions.base_small};
        top: initial;
        left: initial;

        & .ol-zoom-in {
            border-radius: 3px 3px 0 0;
        }
        & .ol-zoom-out {
            border-radius: 0 0 3px 3px;
        }
    }

    & .ol-scale-line {
        background: ${transparentize(0.3, colors.primary.dark)};
        border-radius: 0;
        bottom: ${dimensions.base_small};
        left: ${dimensions.base_small};
    }

    & .ol-attribution {
        top: ${dimensions.base_small};
        right: calc(3*${dimensions.base_small} + 2*${dimensions.medium});
        bottom: initial;
        left: initial;
        ${flex_row({ align: 'center' })}
        background-color: ${colors.fill[2]};

        &:not(.ol-collapsed) {
            background: ${colors.fill[2]};
        }

        & ul {
            font-size: 0.8rem;
        }
    }

    & .ol-rotate {
        top: calc(3*${dimensions.base_small} + 2*${dimensions.medium});
        right: ${dimensions.base_small};
    }
`;
const TileLoadProgress = styled.div`
    position: absolute;
    top: 0;
    left: 0;
    height: calc(${dimensions.base_small}/2);
    background: ${transparentize(0.5, colors.primary.blue)};
    width: 0;
    transition: width 250ms;
    z-index: 5;
`;
const ToolbarContainer = styled.div`
    background-color: ${colors.fill[2]};
    position: absolute;
    top: 14%;
    right: ${dimensions.base_small};
    ${shadows.medium}
    z-index: 1;
    border-radius: 3px;
`;

const HelpPanelContainer = styled.div`
    display: ${({ visible }) => (visible ? 'initial' : 'none')};
    background-color: ${colors.fill[2]};
    ${shadows.medium}
    position: absolute;
    bottom: 0;
    left: 0;
    max-height: 80%;
    width: 100%;
    transition: opacity 0.5s;
    overflow: auto;
    z-index: 2;
`;
const HelpIconContainer = styled.div`
    z-index: 1;
    position: absolute;
    top: ${dimensions.base_small};
    right: ${dimensions.base_small};
`;
const ShareIconContainer = styled.div`
    z-index: 1;
    position: absolute;
    top: ${dimensions.base_small};
    right: calc(2*${dimensions.base_small} + ${dimensions.medium});
`;
const iconContainerWidth = dimensions.medium;
const iconWidth = dimensions.x_small;
const IconContainer = styled.div`
    position: relative;
    width: ${iconContainerWidth};
    height: ${iconContainerWidth};
    z-index: 1;
    padding: ${dimensions.base_small};

    ${({ disabled }) => (disabled
        ? 'cursor: default;'
        : css`
            &:hover {
                background-color: ${colors.fill[3]};
                cursor: pointer;
            }
        `
    )}

    & svg {
        ${icon_base({ size: iconWidth, width: iconWidth })};
    }
`;
const MetadataPopupContainer = styled.div`
    display: none;
    position: absolute;
    padding: ${dimensions.base_small};
    background-color: ${colors.fill[2]};
    border-radius: 3px;
    bottom: ${dimensions.base};
    left: -50px;
    min-width: 330px;
    max-width: 500px;
    min-height: 2.5rem;
    ${shadows.medium}

    &::before, &::after {
        top: 100%;
        border: solid transparent;
        content: " ";
        height: 0;
        width: 0;
        position: absolute;
        pointer-events: none;
    }

    &::after {
        border-top-color: white;
        border-width: 10px;
        left: 48px;
        margin-left: -10px;
    }
    &::before {
        border-top-color: #ccc;
        border-width: 11px;
        left: 48px;
        margin-left: -11px;
    }
`;
const MetadataPopupLabel = styled.div`
    text-align: center;
    margin-bottom: ${dimensions.base_small};
`;
const MetadataPopupContent = styled.div`
    overflow: auto;
    max-height: 300px;
    font-size: ${fonts.sizes.small};
    font-weight: ${fonts.weights.light};
    padding: ${dimensions.base_small};
    border: 1px solid ${colors.stroke[0]};
    cursor: text;
    user-select: text;

    & .bold {
        font-weight: ${fonts.weights.semibold};
    }
`;
const closeIconWidth = '1.25rem';
const MetadataPopupCloser = styled.div`
    position: absolute;
    top: 0.375rem;
    right: 0.375rem;
    & svg {
        ${icon_base({ size: closeIconWidth, width: closeIconWidth })}
        height: initial;
        transform: rotate(0deg);
        transition: all 0.25s ease-in-out;
    }
    &:hover {
        cursor: pointer;
    }
`;

const loadingIconWidth = '6rem';
const StyledLoadingIcon = styled.div`
    height: inherit;
    position: absolute;
    top: 50%;
    left: 50%;
    margin-top: -3rem;
    margin-left: -3rem;
    z-index: 5;
    & svg {
        fill: white;
    }
`;
const LoadingIcon = styled(LoadingSpinner)`
    ${icon_base({ size: loadingIconWidth, width: loadingIconWidth })}
    @keyframes spin {
        from { transform: rotate(0deg); }
        to { transform: rotate(360deg); }
    }
    animation: spin 1s linear infinite;
`;

/* Helpers */

const keyHandlerHelper = ({
    onNextLayer,
    onPreviousLayer,
    onNextFeature,
    onPreviousFeature,
    onCopyId,
    onToggleMenu,
}) => e => {
    const keyCode = e.keyCode || e.which || 0;
    const key = e.key;
    if (keyCode === 27 || key === 'esc') {
        e.preventDefault();
        return onToggleMenu(e);
    }
    if (isNotEditable(e.target)) {
        if (keyCode === 74 || key === 'j') {
            e.preventDefault();
            return onPreviousLayer(e);
        } else if (keyCode === 75 || key === 'k') {
            e.preventDefault();
            return onNextLayer(e);
        } else if (keyCode === 83 || key === 's') {
            e.preventDefault();
            return onNextFeature();
        } else if (keyCode === 65 || key === 'a') {
            e.preventDefault();
            return onPreviousFeature();
        } else if (
            (isMacOS ? e.metaKey : e.ctrlKey)
            && (keyCode === 67 || key === 'c')
            && window.getSelection().isCollapsed
        ) {
            e.preventDefault();
            return onCopyId(e);
        }
    }
    return void 0;
};

/* View */

const AppView = ({
    addMetadataToastVisible, copyShareUrlToastVisible, copyToastVisible, exportPanelVisible,
    staticURLToastVisible, staticURLDeletedToastVisible,
    isContextMenuOpen, contextMenuOptions, contextPosition, gvsIsLoading, staticLayer,
    staticLayerStatus, errorBannerData, numCopiedIds, modifying, drawing,
    hashStatePutLoading, hashStateURL,
    showErrorBanner, showHelp, showLocalAlert, showSessionAlert, showSharePanel, showTimeline,
    navVtree, helpVtree, timelineVtree, mapVtree, panelVtree, toolbarVtree, modalVtree,
    onNextLayer, onPreviousLayer, onNextFeature, onPreviousFeature, onOpenHelpPanel, onCloseHelpPanel,
    onCloseSharePanel, onCloseXYZPanel, onCloseContext, onCopyId, onCopyShareUrl, onCopyXYZLayer,
    onDeleteXYZLayer, onLocalStateUse, onSessionStateUse, onShare, onToggleLocalAlert,
    onToggleSessionAlert, onToggleMenu, onDismissErrorBanner,
}) => (
    <MainContainer
        onKeyDown={keyHandlerHelper({ onNextLayer, onPreviousLayer, onNextFeature, onPreviousFeature, onCopyId, onToggleMenu })}
    >
        <NavContainer
            className={accessClass('sidebar')}
        >
            {navVtree}
        </NavContainer>
        <MainContentContainer>
            <ContentWrapper
                isLoading={gvsIsLoading > 0}
            >
                <PanelContainer>
                    {panelVtree}
                </PanelContainer>
                <MapContainer
                    modifying={modifying}
                    drawing={drawing}
                >
                    <ShareIconContainer
                        className="ol-control ol-unselectable"
                    >
                        <IconContainer
                            onClick={onShare}
                        >
                            <ShareIcon />
                        </IconContainer>
                    </ShareIconContainer>
                    <HelpIconContainer
                        className="ol-control ol-unselectable"
                    >
                        <button
                            type="button"
                            onClick={showHelp ? onCloseHelpPanel : onOpenHelpPanel}
                        >
                            ?
                        </button>
                    </HelpIconContainer>
                    <TileLoadProgress
                        className="tile-load-progress"
                    />
                    {mapVtree}
                </MapContainer>
                <ToolbarContainer
                    className={accessClass('toolbar')}
                >
                    {toolbarVtree}
                </ToolbarContainer>
            </ContentWrapper>
            {showTimeline
                ? <TimelineContainer>
                    {timelineVtree}
                </TimelineContainer>
                : null
            }
            {modalVtree}
            <HelpPanelContainer
                visible={showHelp}
            >
                {helpVtree}
            </HelpPanelContainer>
            <MetadataPopupContainer
                className="ol-metadata-popup"
            >
                <MetadataPopupLabel>
                    Feature Metadata
                </MetadataPopupLabel>
                <MetadataPopupContent
                    className="ol-metadata-popup-content"
                />
                <MetadataPopupCloser
                    className="ol-metadata-popup-closer"
                >
                    <CloseIcon />
                </MetadataPopupCloser>
            </MetadataPopupContainer>
            <ContextMenu
                left={contextPosition[0]}
                top={contextPosition[1]}
                options={contextMenuOptions}
                isOpen={isContextMenuOpen}
                onClickOutside={onCloseContext}
            />
            <StyledLoadingIcon>
                {gvsIsLoading > 0 ? <LoadingIcon /> : null}
            </StyledLoadingIcon>
        </MainContentContainer>
        <StaticXYZPanel
            expanded={exportPanelVisible}
            loading={staticLayerStatus > 0}
            staticLayer={staticLayer}
            onCopyXYZLayer={onCopyXYZLayer}
            onDeleteXYZLayer={onDeleteXYZLayer}
            onCloseXYZPanel={onCloseXYZPanel}
        />
        <Toast
            key="add_metadata"
            visible={addMetadataToastVisible}
            size="medium"
        >
            Added metadata
        </Toast>
        <Toast
            key="copy"
            visible={copyToastVisible}
            size="medium"
        >
            Copied scene ID{numCopiedIds > 1 ? 's' : ''}
        </Toast>
        <Toast
            key="static_xyz"
            visible={staticURLToastVisible}
            size="medium"
        >
            Static URL copied
        </Toast>
        <Toast
            visible={staticURLDeletedToastVisible}
            size="medium"
        >
            Static URL layer deleted
        </Toast>
        <Toast
            visible={copyShareUrlToastVisible}
            size="medium"
        >
            Shareable URL copied
        </Toast>
        <Alert
            key="session"
            isOpen={showSessionAlert}
            text="Would you like to restore your last session?"
            okLabel="Yes"
            cancelLabel="No"
            onOkHandler={_ => { onToggleSessionAlert(false); onSessionStateUse(true); }}
            onCancelHandler={_ => { onToggleSessionAlert(false); onSessionStateUse(false); }}
        />
        <Alert
            key="local"
            isOpen={showLocalAlert}
            text="Would you like to restore your last session?"
            okLabel="Yes"
            cancelLabel="No"
            onOkHandler={_ => { onToggleLocalAlert(false); onLocalStateUse(true); }}
            onCancelHandler={_ => { onToggleLocalAlert(false); onLocalStateUse(false); }}
        />
        <SharePanel
            visible={showSharePanel}
            loading={hashStatePutLoading > 0}
            url={hashStateURL}
            onCopyUrl={onCopyShareUrl}
            onClosePanel={onCloseSharePanel}
        />
        <Banner
            isOpen={showErrorBanner}
            isError={true}
            onDismiss={onDismissErrorBanner}
        >
            {errorBannerData && `${getErrorPreamble(errorBannerData.type)}: ${errorBannerData.message}`}
        </Banner>
    </MainContainer>
);

AppView.propTypes = {
    addMetadataToastVisible: PropTypes.bool.isRequired,
    contextMenuOptions: PropTypes.array.isRequired,
    contextPosition: PropTypes.array.isRequired,
    copyShareUrlToastVisible: PropTypes.bool.isRequired,
    copyToastVisible: PropTypes.number.isRequired,
    drawing: PropTypes.bool.isRequired,
    errorBannerData: PropTypes.object.isRequired,
    exportPanelVisible: PropTypes.bool.isRequired,
    gvsIsLoading: PropTypes.number.isRequired,
    hashStatePutLoading: PropTypes.number.isRequired,
    hashStateURL: PropTypes.string.isRequired,
    helpVtree: PropTypes.element.isRequired,
    isContextMenuOpen: PropTypes.bool.isRequired,
    mapVtree: PropTypes.element.isRequired,
    modalVtree: PropTypes.element.isRequired,
    modifying: PropTypes.bool.isRequired,
    navVtree: PropTypes.element.isRequired,
    numCopiedIds: PropTypes.number.isRequired,
    panelVtree: PropTypes.element.isRequired,
    showErrorBanner: PropTypes.bool.isRequired,
    showHelp: PropTypes.bool.isRequired,
    showLocalAlert: PropTypes.bool.isRequired,
    showSessionAlert: PropTypes.bool.isRequired,
    showSharePanel: PropTypes.bool.isRequired,
    showTimeline: PropTypes.bool.isRequired,
    staticLayer: PropTypes.object.isRequired,
    staticLayerStatus: PropTypes.number.isRequired,
    staticURLDeletedToastVisible: PropTypes.bool.isRequired,
    staticURLToastVisible: PropTypes.bool.isRequired,
    timelineVtree: PropTypes.element.isRequired,
    toolbarVtree: PropTypes.element.isRequired,
    onCloseContext: PropTypes.func.isRequired,
    onCloseHelpPanel: PropTypes.func.isRequired,
    onCloseSharePanel: PropTypes.func.isRequired,
    onCloseXYZPanel: PropTypes.func.isRequired,
    onCopyId: PropTypes.func.isRequired,
    onCopyShareUrl: PropTypes.func.isRequired,
    onCopyXYZLayer: PropTypes.func.isRequired,
    onDeleteXYZLayer: PropTypes.func.isRequired,
    onDismissErrorBanner: PropTypes.func.isRequired,
    onLocalStateUse: PropTypes.func.isRequired,
    onNextFeature: PropTypes.func.isRequired,
    onNextLayer: PropTypes.func.isRequired,
    onOpenHelpPanel: PropTypes.func.isRequired,
    onPreviousFeature: PropTypes.func.isRequired,
    onPreviousLayer: PropTypes.func.isRequired,
    onSessionStateUse: PropTypes.func.isRequired,
    onShare: PropTypes.func.isRequired,
    onToggleLocalAlert: PropTypes.func.isRequired,
    onToggleMenu: PropTypes.func.isRequired,
    onToggleSessionAlert: PropTypes.func.isRequired,
};

export default (
    navVtree, helpVtree, timelineVtree, mapVtree, panelVtree, toolbarVtree, modalVtree,
    layerModalVtree,
    {
        addMetadataToastVisible, copyShareUrlToastVisible, copyToastVisible, staticURLToastVisible,
        staticURLDeletedToastVisible,
        isContextMenuOpen, contextMenuOptions, contextPosition, gvsIsLoading,
        exportPanelVisible, staticLayer, staticLayerStatus, errorBannerData, numCopiedIds,
        modifying, drawing, hashStatePutLoading, hashStateURL,
        showErrorBanner, showHelp, showLocalAlert, showSessionAlert, showSharePanel, showTimeline,
        onCloseContext, onNextLayer, onPreviousLayer, onNextFeature, onPreviousFeature,
        onOpenHelpPanel, onCloseHelpPanel, onCloseSharePanel, onCloseXYZPanel, onCopyId,
        onCopyShareUrl, onCopyXYZLayer, onDeleteXYZLayer, onLocalStateUse, onSessionStateUse,
        onShare, onToggleLocalAlert, onToggleSessionAlert, onToggleMenu, onDismissErrorBanner,
    }
) => AppView({
    addMetadataToastVisible,
    copyShareUrlToastVisible,
    copyToastVisible,
    staticURLToastVisible,
    staticURLDeletedToastVisible,
    isContextMenuOpen,
    contextMenuOptions,
    contextPosition,
    gvsIsLoading,
    exportPanelVisible,
    staticLayer,
    staticLayerStatus,
    errorBannerData,
    numCopiedIds,
    modifying,
    drawing,
    hashStatePutLoading,
    hashStateURL,
    showErrorBanner,
    showHelp,
    showLocalAlert,
    showSessionAlert,
    showSharePanel,
    showTimeline,
    onCloseContext,
    onNextLayer,
    onPreviousLayer,
    onNextFeature,
    onPreviousFeature,
    onOpenHelpPanel,
    onCloseHelpPanel,
    onCloseSharePanel,
    onCloseXYZPanel,
    onCopyId,
    onCopyShareUrl,
    onCopyXYZLayer,
    onDeleteXYZLayer,
    onLocalStateUse,
    onSessionStateUse,
    onShare,
    onToggleLocalAlert,
    onToggleSessionAlert,
    onToggleMenu,
    onDismissErrorBanner,
    navVtree,
    helpVtree,
    timelineVtree,
    mapVtree,
    panelVtree,
    toolbarVtree,
    modalVtree,
    layerModalVtree,
});
