import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import AnnotationAPI from '../../api/AnnotationAPI';
import IDUtil from '../../util/IDUtil'; // for generating unique CSS classnames for this component
import SessionStorageHandler from '../../util/SessionStorageHandler';

import { AnnotationEvents } from './AnnotationClient';
import PopupHeader from './PopupHeader';
import {
    ANNOTATION_TARGET,
    CLASSIFICATION,
    METADATA,
    LINK,
    COMMENT,
} from '../../util/AnnotationConstants';
import AnnotationFilter from './annotationColumn/AnnotationFilter';
import {
    renderPerType,
    getAnnotationsPerType,
    getAnnotationTargetTitle,
} from './AnnotationHelpers';
import { ResourceViewerContext } from './ResourceViewerContext';
import Strings from './_Strings';
import debounce from 'debounce';

/*
	AnnotationPopup shows annotations for the given fragment

	This component should react to changes to:
	- selected project
	- selected annotation target (active media object or parts of that media object or pieces of selected text later on)

	The user is required for the annotation API

	Supplies the ResourceViewerContext with all current annotation data

    This component utilizes components from the annotation column (for re-use and consistency)
*/

const SESSION_STORAGE_ANNOTATION_POPUP_LEFT = "bg__annotation-popup-left";
const SESSION_STORAGE_ANNOTATION_POPUP_TOP = "bg__annotation-popup-top";

// Amount of height to add to the popup for each annotation type
const ANNOTATION_TYPE_HEIGHT = {
    [CLASSIFICATION]: 250,
    [COMMENT]: 400,
    [LINK]: 250,
    [METADATA]: 400,
};

export default class AnnotationPopup extends React.Component {
    static contextType = ResourceViewerContext;

    constructor(props) {
        super(props);

        // Size
        this.width = Math.max(300, Math.min(window.innerWidth / 3, 400));
        this.maxHeight = 500;

        // Position from session storage, default to screen center
        const left = SessionStorageHandler.getInt(
            SESSION_STORAGE_ANNOTATION_POPUP_LEFT,
            window.innerWidth / 2 - this.width / 2
        );

        const top = SessionStorageHandler.getInt(
            SESSION_STORAGE_ANNOTATION_POPUP_TOP,
            window.innerHeight / 2 - this.maxHeight / 2
        );

        // Initial state
        this.state = {
            isBookmarked: false,
            left,
            top,
            height: this.maxHeight,
        };

        // Variables to store drag changes independent of state
        this._left = left;
        this._top = top;

        // Debounce storeposition function to limit calls to sessionStorage
        this.storePosition = debounce(this.storePosition, 200);

        this.annotationEvents = [
            AnnotationEvents.ON_EDIT,
            AnnotationEvents.ON_SET_ANNOTATION,
            AnnotationEvents.ON_SET_SELECTION,
            AnnotationEvents.ON_SAVE,
            AnnotationEvents.ON_DELETE,
            AnnotationEvents.ON_CHANGE_TARGET,
        ];

        this._lastActiveAnnotationTypes = null;
    }

    // Calculate popup height, dependent on active annotation types
    // Update top/left, limit to always keep the popup on screen
    updateDimensions(maxHeight = 500) {
        let height = 30;
        this.context.activeAnnotationTypes.forEach((annotationType) => {
            if (annotationType in ANNOTATION_TYPE_HEIGHT) {
                height += ANNOTATION_TYPE_HEIGHT[annotationType];
            }
        });
        this.setState(
            {
                height: Math.max(100, Math.min(maxHeight, height)),
            },
            () => {
                // update position
                this.setState({
                    top: this.limitTop(this.state.top),
                    left: this.limitLeft(this.state.left),
                });
            }
        );
    }

    /* ----------------------------- LIFECYCLE ------------------------ */

    componentDidMount() {
        // Bind to annotation updates
        this.annotationEvents.forEach((event) => {
            this.context.annotationClient.events.bind(event, this.update);
        });

        // Bind to keypress
        document.addEventListener("keydown", this.onKeyDown);

        // Calculutate popup height
        this.updateDimensions();
    }

    componentWillUnmount() {
        // Unbind annotation updates
        this.annotationEvents.forEach((event) => {
            this.context.annotationClient.events.unbind(event, this.update);
        });

        // Unbind keypress
        document.removeEventListener("keydown", this.onKeyDown);
    }

    componentDidUpdate() {
        // When active annotation types changed; update popup height
        if (
            this.context.activeAnnotationTypes !=
            this._lastActiveAnnotationTypes
        ) {
            this.updateDimensions();
            this._lastActiveAnnotationTypes = this.context.activeAnnotationTypes;
        }
    }

    update = () => {
        this.forceUpdate();
    };

    /* ----------------------------- KEYPRESS ------------------------ */

    onKeyDown = (e) => {
        if (e.key == "Escape") {
            this.props.onClose();
        }
    };

    /* ----------------------------- DRAGGING ------------------------ */

    onDrag = (dx, dy) => {
        this._left = this.limitLeft(this._left - dx);
        this._top = this.limitTop(this._top - dy);
        this.setState({
            left: this._left,
            top: this._top,
        });

        this.storePosition();
    };

    storePosition = () => {
        SessionStorageHandler.set(
            SESSION_STORAGE_ANNOTATION_POPUP_TOP,
            this._top
        );
        SessionStorageHandler.set(
            SESSION_STORAGE_ANNOTATION_POPUP_LEFT,
            this._left
        );
    };

    // keep popup on screen
    limitLeft(left) {
        return left
            ? Math.min(window.innerWidth - this.width, Math.max(0, left))
            : 0;
    }
    // keep popup on screen
    limitTop(top, mediaSuiteHeaderHeight = 95) {
        return top
            ? Math.min(
                  window.innerHeight - this.state.height,
                  Math.max(mediaSuiteHeaderHeight, top)
              )
            : mediaSuiteHeaderHeight;
    }

    /* ----------------------------- BOOKMARK HELPER ------------------------ */

    isBookmarked(userId, project) {
        const filter = {
            "user.keyword": userId,
            motivation: "bookmarking",
        };
        if (project) {
            filter["project"] = project.id;
        }
        AnnotationAPI.getFilteredAnnotations(
            // load all "bookmark group annotations" of current user project
            userId,
            filter,
            null, //not_filters
            () => {
                this.setState({ isBookmarked: true });
            }
        );
    }

    /* ----------------------------- RENDER FUNCTIONS ------------------------ */

    renderWarningList = (activeProject, isBookmarked, showBookmarkSelector) => {
        const warnings = [];

        // active project required warning
        !activeProject &&
            warnings.push(
                <div className="warning" key="project">
                    <i className="fas fa-info-circle" />
                    {Strings.ANNOTATIONS_FIRST_SELECT_PROJECT}
                </div>
            );

        // bookmark required warning, only when active project is available
        // because else the bookmark link won't work properly
        if (activeProject && !isBookmarked) {
            const parts = Strings.ANNOTATIONS_FIRST_BOOKMARK.split("%ACTION%");
            if (parts.length < 2) {
                console.error(
                    "ANNOTATIONS_FIRST_BOOKMARK requires: aaa %ACTION% bbb"
                );
            } else {
                warnings.push(
                    <div className="warning" key="bookmark">
                        <i className="fa fa-info-circle" />
                        {parts[0]}
                        <span className="action" onClick={showBookmarkSelector}>
                            bookmark
                        </span>
                        {parts[1]}
                    </div>
                );
            }
        }

        return warnings;
    };

    render() {
        // active annotation required
        if (!this.context.annotationClient.activeAnnotation) {
            return null;
        }

        // get active project
        const activeProject = this.context.activeProject;
        const isBookmarked = activeProject && this.state.isBookmarked;

        // Check if the resource has been bookmarked
        if (activeProject && !isBookmarked) {
            this.isBookmarked(this.context.user.id, activeProject);
        }

        // Warnings that can be shown before showing the annotation list
        const warnings = this.renderWarningList(
            activeProject,
            isBookmarked,
            this.props.showBookmarkSelector
        );

        // types
        const types =
            warnings.length > 0
                ? null
                : renderPerType(
                      getAnnotationsPerType(
                          this.context.activeAnnotationTypes,
                          this.context.annotationClient.activeAnnotation
                      ),
                      ANNOTATION_TARGET.SEGMENT,
                      this.context.annotationClient.activeAnnotation,
                      this.context.annotationClient,
                      this.context.activeAnnotationTypes,
                      true // show forms by default
                  );

        // title
        const title = getAnnotationTargetTitle(
            this.context.annotationClient.activeAnnotation
        );

        // filter
        const annotationFilter =
            warnings.length > 0 ? null : <AnnotationFilter />;

        return (
            <div
                className={classNames(IDUtil.cssClassName("annotation-popup"), {
                    active: this.props.active,
                    inactive: !this.props.active,
                })}
                style={{
                    left: this.state.left,
                    top: this.state.top,
                    width: this.width,
                    height: this.state.height,
                }}
            >
                <PopupHeader
                    onClose={this.props.onClose}
                    title={title}
                    onDrag={this.onDrag}
                ></PopupHeader>
                <div key="column-content" className="column-content">
                    {warnings}
                    {types}
                    {annotationFilter}
                </div>
            </div>
        );
    }
}

AnnotationPopup.propTypes = {
    showBookmarkSelector: PropTypes.func.isRequired,
    onClose: PropTypes.func.isRequired,
};
