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

import IconUtil from '../../../util/IconUtil';
import IDUtil from '../../../util/IDUtil';

import Strings from '../_Strings';
import Info from '../../shared/Info';

import { AnnotationEvents } from '../AnnotationClient';

import SingleAnnotationTarget from './SingleAnnotationTarget';
import SegmentAnnotations from './SegmentAnnotations';
import {
    ANNOTATION_TARGET,
    ANNOTATION_TYPE,
    SESSION_STORAGE_ACTIVE_TARGETS,
    SESSION_STORAGE_ACTIVE_TYPES
} from './Constants';

export default class AnnotationList extends React.PureComponent {
    constructor(props) {
        super(props);

        // get active targets from session storage
        const activeTargets = window.sessionStorage.getItem(
            SESSION_STORAGE_ACTIVE_TARGETS
        )
            ? window.sessionStorage
                  .getItem(SESSION_STORAGE_ACTIVE_TARGETS)
                  .split(',')
            : [];

        // get active types from session storage
        const activeTypes = window.sessionStorage.getItem(
            SESSION_STORAGE_ACTIVE_TYPES
        )
            ? window.sessionStorage
                  .getItem(SESSION_STORAGE_ACTIVE_TYPES)
                  .split(',')
            : Object.values(ANNOTATION_TYPE);

        // initial state
        this.state = {
            activeTargets,
            activeTypes
        };
    }

    componentDidMount() {
        this.props.annotationClient.events.bind(
            AnnotationEvents.ON_SAVE,
            this.onUpdate
        );
        this.props.annotationClient.events.bind(
            AnnotationEvents.ON_DELETE,
            this.onUpdate
        );
        this.props.annotationClient.events.bind(
            AnnotationEvents.ON_SET_ANNOTATION,
            this.onUpdate
        );
        this.props.annotationClient.events.bind(
            AnnotationEvents.ON_CHANGE_TARGET,
            this.onUpdate
        );
    }

    componentWillUnmount() {
        this.props.annotationClient.events.unbind(
            AnnotationEvents.ON_SAVE,
            this.onUpdate
        );
        this.props.annotationClient.events.unbind(
            AnnotationEvents.ON_DELETE,
            this.onUpdate
        );
        this.props.annotationClient.events.unbind(
            AnnotationEvents.ON_SET_ANNOTATION,
            this.onUpdate
        );
        this.props.annotationClient.events.unbind(
            AnnotationEvents.ON_CHANGE_TARGET,
            this.onUpdate
        );
    }

    onUpdate = () => this.forceUpdate();

    // Wrapper function for retrieving an annotation / all annotations for a given target
    getAnnotationsForTarget(allAnnotations, resource, mediaObject) {
        return target => {
            const annotations = allAnnotations.filter(
                annotation =>
                    // only with target type
                    annotation.target.type === target
            );

            // the checks on current resource/mediaobject are needed
            // as sometimes other annotations from the project seem to slip in
            switch (target) {
                case ANNOTATION_TARGET.RESOURCE: {
                    const result = annotations.filter(
                        annotation =>
                            // current resource
                            annotation.target.source == resource.resourceId
                    );
                    // check number of annotations
                    if (result.length > 1) {
                        console.error(
                            'Multiple root annotations for target ' +
                                target +
                                '. Expecting 1 or 0'
                        );
                    }
                    if (result.length > 0) {
                        return result[0];
                    }
                    return null;
                }
                case ANNOTATION_TARGET.MEDIAOBJECT: {
                    const result = annotations.filter(
                        annotation =>
                            // current mediaObject
                            annotation.target.source == mediaObject.assetId
                    );
                    // check number of annotations
                    if (result.length > 1) {
                        console.error(
                            'Multiple root annotations for target ' +
                                target +
                                '. Expecting 1 or 0'
                        );
                    }
                    if (result.length > 0) {
                        return result[0];
                    }
                    return null;
                }
                case ANNOTATION_TARGET.SEGMENT:
                    // can return multiple in an array
                    return annotations.filter(annotation =>
                        // segment from current mediaObject
                        resource.playableContent.some(
                            mediaObject =>
                                annotation.target.source == mediaObject.assetId
                        )
                    );
                default:
                    console.error(
                        'Could not find annotations for type ',
                        target
                    );
                    return [];
            }
        };
    }

    /* ---------------------------------- UI FUNCTIONS -------------------------------- */

    // Toggle given target
    // Persist the state in session storage
    toggleTarget = target => {
        this.setState(
            {
                activeTargets: this.state.activeTargets.includes(target)
                    ? this.state.activeTargets.filter(l => l != target)
                    : [target, ...this.state.activeTargets]
            },
            () => {
                // store active targets to session storage
                window.sessionStorage.setItem(
                    SESSION_STORAGE_ACTIVE_TARGETS,
                    this.state.activeTargets.join(',')
                );
            }
        );
    };

    // Toggle given type, but keep type order intact
    // Persist the state in session storage
    toggleType = type => {
        // add or remove type
        const newTypes = this.state.activeTypes.includes(type)
            ? this.state.activeTypes.filter(t => t != type)
            : [type, ...this.state.activeTypes];

        // keep ANNOTATION_TYPE order intact
        const activeTypes = Object.values(ANNOTATION_TYPE).filter(type =>
            newTypes.includes(type)
        );

        this.setState(
            {
                activeTypes
            },
            () => {
                // store active targets to session storage
                window.sessionStorage.setItem(
                    SESSION_STORAGE_ACTIVE_TYPES,
                    this.state.activeTypes.join(',')
                );
            }
        );
    };

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

    // Render type filter
    renderFilter(active) {
        return (
            <div className="annotation-filter">
                {Object.keys(ANNOTATION_TYPE).map(type => (
                    <span key={type}>
                        <input
                            type="checkbox"
                            checked={active.includes(ANNOTATION_TYPE[type])}
                            onChange={this.toggleType.bind(
                                this,
                                ANNOTATION_TYPE[type]
                            )}
                            id={'annotation-filter-' + type}
                        />
                        <label htmlFor={'annotation-filter-' + type}>
                            {Strings['ANNOTATION_TYPE_TITLE_' + type]}
                        </label>
                    </span>
                ))}
            </div>
        );
    }

    render() {
        const { resource, mediaObject } = this.props;

        // at least a resource is required
        if (!resource) {
            return null;
        }

        // all annotations for this project
        const annotations = this.props.annotationClient.annotations;

        // create a wrapper for getting all annotations per target
        // for the RESOURCE and MEDIAOBJECT targets, there should only be 1 annotation
        // for the SEGMENTS, there will be multiple
        const getAnnotations = this.getAnnotationsForTarget(
            annotations,
            resource,
            mediaObject
        );

        // Props used by all targets
        const defaultTargetProps = {
            annotationClient: this.props.annotationClient,
            activeTypes: this.state.activeTypes,
            toggle: this.toggleTarget
        };

        // Resource target annotations
        const resourceAnnotation = getAnnotations(ANNOTATION_TARGET.RESOURCE);
        const resourceActive = this.state.activeTargets.includes(
            ANNOTATION_TARGET.RESOURCE
        );
        const resourceTarget = (
            <SingleAnnotationTarget
                target={ANNOTATION_TARGET.RESOURCE}
                icon={
                    <Info
                        id="annotation-target-resource-help"
                        text={Strings.ANNOTATION_TARGET_HELP_RESOURCE}
                        className="black left"
                    />
                }
                title={Strings.ANNOTATION_TARGET_TITLE_RESOURCE}
                active={resourceActive}
                annotation={resourceAnnotation}
                {...defaultTargetProps}
            />
        );

        // Mediaobject target annotations
        const mediaObjectAnnotation = getAnnotations(
            ANNOTATION_TARGET.MEDIAOBJECT
        );
        const mediaObjectActive = this.state.activeTargets.includes(
            ANNOTATION_TARGET.MEDIAOBJECT
        );
        const mediaObjectTarget = this.props.mediaObject ? (
            <SingleAnnotationTarget
                target={ANNOTATION_TARGET.MEDIAOBJECT}
                icon={
                    <span
                        title={Strings.ANNOTATION_TARGET_HELP_MEDIAOBJECT}
                        className={IconUtil.getMimeTypeIcon(
                            this.props.mediaObject.mimeType,
                            false,
                            false,
                            false
                        )}
                    />
                }
                title={this.props.mediaObject.assetId}
                active={mediaObjectActive}
                annotation={mediaObjectAnnotation}
                {...defaultTargetProps}
            />
        ) : null;

        // Segment target annotations
        const segmentTarget = this.props.mediaObject ? (
            <SegmentAnnotations
                target={ANNOTATION_TARGET.SEGMENT}
                icon={
                    <Info
                        id="annotation-target-segment-help"
                        text={Strings.ANNOTATION_TARGET_HELP_SEGMENT}
                        className="black left"
                    />
                }
                title={Strings.ANNOTATION_TARGET_TITLE_SEGMENT}
                active={this.state.activeTargets.includes(
                    ANNOTATION_TARGET.SEGMENT
                )}
                segments={getAnnotations(ANNOTATION_TARGET.SEGMENT)}
                mediaObject={this.props.mediaObject}
                {...defaultTargetProps}
            />
        ) : null;

        // Type filter
        const filter = this.renderFilter(this.state.activeTypes);

        // Full annotation list
        return (
            <div className={IDUtil.cssClassName('annotation-list')}>
                {/* All annotation targets */}
                <div className="targets">
                    {resourceTarget}
                    {mediaObjectTarget}
                    {segmentTarget}
                </div>

                {/* Filter*/}
                {filter}
            </div>
        );
    }
}

AnnotationList.propTypes = {
    annotationClient: PropTypes.object.isRequired,
    mediaObject: PropTypes.object,
    resource: PropTypes.object
};
