import React from "react";
import PropTypes from "prop-types";
import classNames from "classnames";
import IDUtil from "../../../util/IDUtil";
import SegmentLayerHeader from "./SegmentLayerHeader";
import Segment from "./Segment";
import SelectionTemporalForm from "./SelectionTemporalForm";
import SelectionSpatialForm from "./SelectionSpatialForm";
import Strings from "../_Strings";

import { AnnotationEvents } from "../AnnotationClient";
import {
    SELECTION_TEMPORAL,
    SELECTION_SPATIAL,
    ANNOTATION_COLUMN_SEGMENT_ID,
} from "../../../util/AnnotationConstants";

export default class SegmentAnnotations extends React.PureComponent {
    constructor(props) {
        super(props);
        this.state = {
            selectionType: this.getSelectionType(this.props.mediaObject),
        };

        this.lastActiveAnnotationId = "";
    }

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

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

    onUpdate = (data) => {
        if (data && data.annotation && data.annotation.id) {
            // skip if already active
            if (this.lastActiveAnnotationId == data.annotation.id) {
                return;
            }

            // scroll segment into view
            const elem = document.getElementById(
                ANNOTATION_COLUMN_SEGMENT_ID + data.annotation.id
            );
            if (elem) {
                elem.scrollIntoView();
                this.lastActiveAnnotationId = data.annotation.id;

                // Prevent the column itself from scrolling
                const column = (document.getElementsByClassName(
                    "bg__annotation-column"
                )[0].scrollTop = 0);
            }
        }
    };

    getSelectionType(mediaObject) {
        const mimeType = mediaObject.mimeType;
        switch (true) {
            case mimeType.startsWith("video"):
            case mimeType.startsWith("audio"):
                return SELECTION_TEMPORAL;
            case mimeType.startsWith("image"):
                return SELECTION_SPATIAL;
            default:
                return "";
        }
    }

    toggle = () => {
        this.props.toggle(this.props.layerId);
    };

    // Create a new segment, set it to 0:00
    async createSegment(selection) {
        // set activeAnnotation to null, so a new root annotation is created & activated (in saveSelection)
        this.props.annotationClient.activeAnnotation = null;

        // Create and activate a new segment for current layerId
        await this.props.annotationClient.saveSelection(
            selection,
            true,
            true,
            this.props.layerId
        );
    }

    // Update segment with given selection
    // If the selection is null, delete the segment
    async updateSegment(segment, selection) {
        // When the selection is null: delete the segment
        if (selection == null) {
            this.props.annotationClient.delete(segment);
            return;
        }

        // make the segment the active annotation, so it will be updated
        this.props.annotationClient.activeAnnotation = segment;

        // set the active selection
        this.props.annotationClient.activeSelection = selection;

        // Update the segment
        await this.props.annotationClient.saveSelection(selection);
    }

    onUpdateSegment = (segment, selection) => {
        this.updateSegment(segment, selection);
    };

    onAddSegment = (selection) => {
        this.createSegment(selection);
    };

    onChangeTitle = (title) => {
        this.props.annotationClient.segmentLayers.renameLayer(
            this.props.layerId,
            title
        );
    };

    onDeleteLayer = () => {
        this.props.annotationClient.segmentLayers.deleteLayerWithCheck(
            this.props.layerId
        );
    };

    createTemporalSelection = (start, end) => ({
        type: "temporal",
        start,
        end,
    });

    createSpatialSelection = (x, y, w, h) => ({
        type: "spatial",
        rect: { x, y, w, h },
    });

    createSelection(selectionType) {
        switch (selectionType) {
            case SELECTION_TEMPORAL:
                return this.createTemporalSelection(0, 0);
            case SELECTION_SPATIAL:
                return this.createSpatialSelection(0, 0, 0, 0);
            default:
                console.error(
                    "Could not create selection for type",
                    selectionType
                );
        }
    }

    renderForm(selection) {
        switch (selection.type) {
            case SELECTION_TEMPORAL:
                return (
                    <SelectionTemporalForm
                        selection={selection}
                        onUpdate={this.onAddSegment}
                    />
                );
            case SELECTION_SPATIAL:
                return (
                    <SelectionSpatialForm
                        selection={selection}
                        onUpdate={this.onAddSegment}
                    />
                );
            default:
                console.error("Could not render form for type ", selectionType);
        }
    }

    sortSegments = (segments, selectionType) => {
        switch (selectionType) {
            case SELECTION_TEMPORAL:
                // start -> end
                return segments.sort((a, b) => {
                    a = a.target.selector.refinedBy;
                    b = b.target.selector.refinedBy;

                    // fallback for erronous values
                    if (a === null) {
                        return b;
                    }
                    if (b === null) {
                        return a;
                    }

                    if (a.start == b.start) {
                        return a.end - b.end;
                    }
                    return a.start - b.start;
                });
            case SELECTION_SPATIAL:
                // top -> down
                return segments.sort((a, b) => {
                    a = a.target.selector.refinedBy.rect;
                    b = b.target.selector.refinedBy.rect;
                    if (!a || !b) {
                        return 0;
                    }
                    if (a.y == b.y) {
                        return a.x - b.x;
                    }
                    return a.y - b.y;
                });
            default:
                return segments;
        }
    };

    render() {
        // count
        const count = this.props.segments ? this.props.segments.length : 0;

        // header
        const header = (
            <SegmentLayerHeader
                active={this.props.active}
                onToggle={this.toggle}
                title={this.props.title}
                count={count}
                onChangeTitle={this.onChangeTitle}
                onDeleteLayer={this.onDeleteLayer}
            />
        );

        // Form
        const form = this.props.active
            ? this.renderForm(this.createSelection(this.state.selectionType))
            : null;

        // All segments
        const segments = this.props.active
            ? this.sortSegments(
                  this.props.segments,
                  this.state.selectionType
              ).map((segment) => (
                  <Segment
                      key={segment.id}
                      segment={segment}
                      updateSegment={this.onUpdateSegment}
                      {...this.props}
                  />
              ))
            : null;

        return this.state.selectionType === "" ? null : (
            <div
                className={classNames(
                    IDUtil.cssClassName("segment-annotations")
                )}
            >
                {header}
                {form}
                {segments}
            </div>
        );
    }
}

SegmentAnnotations.propTypes = {
    annotationClient: PropTypes.object.isRequired,
    title: PropTypes.string.isRequired,
    target: PropTypes.string.isRequired,
    layerId: PropTypes.number.isRequired,
    toggle: PropTypes.func.isRequired,
    active: PropTypes.bool.isRequired,
    activeTypes: PropTypes.arrayOf(PropTypes.string).isRequired,
    segments: PropTypes.arrayOf(PropTypes.object),
    mediaObject: PropTypes.object.isRequired,
};
