import { createHoverifier, HoverOverlay, } from '@sourcegraph/codeintellify';
import { isEqual, upperFirst } from 'lodash';
import AlertCircleIcon from 'mdi-react/AlertCircleIcon';
import MapSearchIcon from 'mdi-react/MapSearchIcon';
import * as React from 'react';
import { Route, Switch } from 'react-router';
import { Link } from 'react-router-dom';
import { Subject, Subscription } from 'rxjs';
import { filter, map, withLatestFrom } from 'rxjs/operators';
import { getHover, getJumpURL } from '../../backend/features';
import { HeroPage } from '../../components/HeroPage';
import { eventLogger } from '../../tracking/eventLogger';
import { getModeFromPath } from '../../util';
import { propertyIsDefined } from '../../util/types';
import { escapeRevspecForURL } from '../../util/url';
import { RepoHeaderBreadcrumbNavItem } from '../RepoHeaderBreadcrumbNavItem';
import { RepoHeaderContributionPortal } from '../RepoHeaderContributionPortal';
import { RepositoryCompareHeader } from './RepositoryCompareHeader';
import { RepositoryCompareOverviewPage } from './RepositoryCompareOverviewPage';
const NotFoundPage = () => (React.createElement(HeroPage, { icon: MapSearchIcon, title: "404: Not Found", subtitle: "Sorry, the requested repository comparison page was not found." }));
const logTelemetryEvent = (event, data) => eventLogger.log(event, data);
const LinkComponent = (props) => React.createElement(Link, Object.assign({}, props));
/**
 * Renders pages related to a repository comparison.
 */
export class RepositoryCompareArea extends React.Component {
    constructor(props) {
        super(props);
        this.componentUpdates = new Subject();
        /** Emits whenever the ref callback for the hover element is called */
        this.hoverOverlayElements = new Subject();
        this.nextOverlayElement = (element) => this.hoverOverlayElements.next(element);
        /** Emits whenever the ref callback for the hover element is called */
        this.repositoryCompareAreaElements = new Subject();
        this.nextRepositoryCompareAreaElement = (element) => this.repositoryCompareAreaElements.next(element);
        /** Emits when the go to definition button was clicked */
        this.goToDefinitionClicks = new Subject();
        this.nextGoToDefinitionClick = (event) => this.goToDefinitionClicks.next(event);
        /** Emits when the close button was clicked */
        this.closeButtonClicks = new Subject();
        this.nextCloseButtonClick = (event) => this.closeButtonClicks.next(event);
        this.subscriptions = new Subscription();
        this.onUpdateComparisonSpec = (newBaseSpec, newHeadSpec) => {
            this.props.history.push(`/${this.props.repo.name}/-/compare${newBaseSpec || newHeadSpec
                ? `/${escapeRevspecForURL(newBaseSpec || '')}...${escapeRevspecForURL(newHeadSpec || '')}`
                : ''}`);
        };
        this.hoverifier = createHoverifier({
            closeButtonClicks: this.closeButtonClicks,
            goToDefinitionClicks: this.goToDefinitionClicks,
            hoverOverlayElements: this.hoverOverlayElements,
            hoverOverlayRerenders: this.componentUpdates.pipe(withLatestFrom(this.hoverOverlayElements, this.repositoryCompareAreaElements), map(([, hoverOverlayElement, repositoryCompareAreaElement]) => ({
                hoverOverlayElement,
                // The root component element is guaranteed to be rendered after a componentDidUpdate
                relativeElement: repositoryCompareAreaElement,
            })), 
            // Can't reposition HoverOverlay if it wasn't rendered
            filter(propertyIsDefined('hoverOverlayElement'))),
            pushHistory: path => this.props.history.push(path),
            logTelemetryEvent,
            fetchHover: hoveredToken => getHover(this.getLSPTextDocumentPositionParams(hoveredToken), this.props),
            fetchJumpURL: hoveredToken => getJumpURL(this.getLSPTextDocumentPositionParams(hoveredToken), this.props),
        });
        this.subscriptions.add(this.hoverifier);
        this.state = this.hoverifier.hoverState;
        this.subscriptions.add(this.hoverifier.hoverStateUpdates.subscribe(update => this.setState(update)));
    }
    getLSPTextDocumentPositionParams(hoveredToken) {
        return {
            repoPath: hoveredToken.repoPath,
            rev: hoveredToken.rev,
            filePath: hoveredToken.filePath,
            commitID: hoveredToken.commitID,
            position: hoveredToken,
            mode: getModeFromPath(hoveredToken.filePath || ''),
        };
    }
    componentDidMount() {
        this.componentUpdates.next(this.props);
    }
    shouldComponentUpdate(nextProps, nextState) {
        return !isEqual(this.props, nextProps) || !isEqual(this.state, nextState);
    }
    componentDidUpdate() {
        this.componentUpdates.next(this.props);
    }
    componentWillUnmount() {
        this.subscriptions.unsubscribe();
    }
    render() {
        if (this.state.error) {
            return React.createElement(HeroPage, { icon: AlertCircleIcon, title: "Error", subtitle: upperFirst(this.state.error) });
        }
        let spec;
        if (this.props.match.params.spec) {
            spec = parseComparisonSpec(this.props.match.params.spec);
        }
        const commonProps = {
            repo: this.props.repo,
            base: { repoID: this.props.repo.id, repoPath: this.props.repo.name, rev: spec && spec.base },
            head: { repoID: this.props.repo.id, repoPath: this.props.repo.name, rev: spec && spec.head },
            routePrefix: this.props.match.url,
            extensions: this.props.extensions,
        };
        return (React.createElement("div", { className: "repository-compare-area area--vertical", ref: this.nextRepositoryCompareAreaElement },
            React.createElement(RepoHeaderContributionPortal, { position: "nav", element: React.createElement(RepoHeaderBreadcrumbNavItem, { key: "compare" }, "Compare"), repoHeaderContributionsLifecycleProps: this.props.repoHeaderContributionsLifecycleProps }),
            React.createElement(RepositoryCompareHeader, Object.assign({ className: "area--vertical__header" }, commonProps, { onUpdateComparisonSpec: this.onUpdateComparisonSpec })),
            React.createElement("div", { className: "area--vertical__content" },
                React.createElement("div", { className: "area--vertical__content-inner" }, spec === null ? (React.createElement("div", { className: "alert alert-danger" }, "Invalid comparison specifier")) : (React.createElement(Switch, null,
                    React.createElement(Route, { path: `${this.props.match.url}`, key: "hardcoded-key" // see https://github.com/ReactTraining/react-router/issues/4578#issuecomment-334489490
                        , exact: true, 
                        // tslint:disable-next-line:jsx-no-lambda
                        render: routeComponentProps => (React.createElement(RepositoryCompareOverviewPage, Object.assign({}, routeComponentProps, commonProps, { hoverifier: this.hoverifier, extensionsController: this.props.extensionsController, extensionsOnVisibleTextDocumentsChange: this.props.extensionsOnVisibleTextDocumentsChange }))) }),
                    React.createElement(Route, { key: "hardcoded-key", component: NotFoundPage }))))),
            this.state.hoverOverlayProps && (React.createElement(HoverOverlay, Object.assign({}, this.state.hoverOverlayProps, { logTelemetryEvent: logTelemetryEvent, linkComponent: LinkComponent, hoverRef: this.nextOverlayElement, onGoToDefinitionClick: this.nextGoToDefinitionClick, onCloseButtonClick: this.nextCloseButtonClick })))));
    }
}
function parseComparisonSpec(spec) {
    if (!spec.includes('...')) {
        return null;
    }
    const parts = spec.split('...', 2).map(decodeURIComponent);
    return {
        base: parts[0] || null,
        head: parts[1] || null,
    };
}
//# sourceMappingURL=RepositoryCompareArea.js.map