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

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

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

//TODO only add a function to enforce adding a NEW annotation
//TODO test/implement highlighting the initial search result
//TODO prevent zooming when clicking on the overlay

export default class ImageViewer extends React.PureComponent {

	constructor(props) {
		super(props);

		this.viewer = null;
		this.selector = null; //needed to access picturae selector
		this.CLASS_PREFIX = 'imv';
	}

	async componentDidMount() {
		await this.initViewer();
		this.props.annotationClient.events.bind(AnnotationEvents.ON_SET_ANNOTATION, this.onSetActiveAnnotation);
		this.props.annotationClient.events.bind(AnnotationEvents.ON_CHANGE_TARGET, this.onChangeTarget);
		this.props.annotationClient.events.bind(AnnotationEvents.ON_DELETE, this.onDeleteAnnotation);
		this.props.annotationClient.events.bind(AnnotationEvents.ON_SAVE, this.onSaveAnnotation);
	}

	componentWillUnmount() {
		this.props.annotationClient.events.unbind(AnnotationEvents.ON_SET_ANNOTATION, this.onSetActiveAnnotation);
		this.props.annotationClient.events.unbind(AnnotationEvents.ON_CHANGE_TARGET, this.onChangeTarget);
		this.props.annotationClient.events.unbind(AnnotationEvents.ON_DELETE, this.onDeleteAnnotation);
		this.props.annotationClient.events.unbind(AnnotationEvents.ON_SAVE, this.onSaveAnnotation);
	}

	/* --------------------------------------------------------------
	-------------------------- VIEWER INITIALIZATION ----------------
	---------------------------------------------------------------*/

	__toOSDUrl(mediaObject) {
		const index = mediaObject.url.indexOf('.tif'); //FIXME very weak way to check if it's IIIF!
		let moClone = JSON.parse(JSON.stringify(mediaObject));
		if(index === -1) {
			moClone.infoUrl = mediaObject.url;
		} else {
			moClone.infoUrl = mediaObject.url.substring(0, index + 4) + '/info.json';
    	}
    	return moClone;
	}

	//TODO make sure this still works
	__showSearchHighlight(mediaObject) {
		const r = this.viewer.viewport.imageToViewportRectangle(
            parseInt(mediaObject.x),
            parseInt(mediaObject.y),
            parseInt(mediaObject.w),
            parseInt(mediaObject.h)
        );
        const elt = document.createElement("div");
        elt.id = 'runtime-overlay';
        elt.className = IDUtil.cssClassName('highlight', this.CLASS_PREFIX);
        this.viewer.addOverlay(elt, r);
	}

	//first load the viewer using the provided this.props.mediaObjects
	initViewer() {
		//map the media objects to sources OpenSeaDragon likes
		const sources = this.props.mediaObjects.map(mo => {
			return this.__toOSDUrl(mo);
		});
		//TODO change this when using nested PID targets
		//TODO don't use the annotationClient anymore for fetching the active mediaObject!
		const initialPage = this.props.activeMediaObject ? this.props.mediaObjects.findIndex(
			mo => mo.assetId == this.props.activeMediaObject.assetId
		) : 0;
		this.viewer = OpenSeadragon({
			id: 'img_viewer' ,
			prefixUrl: '/static/node_modules/openseadragon/build/openseadragon/images/',
			showSelectionControl: true,
			sequenceMode : true,
			preserveViewport: true,
			height: '100px',
			ajaxWithCredentials : this.props.useCredentials,
			tileSources: sources.map(s => s.infoUrl),
			initialPage : initialPage
		});

		//make sure the selection button tooltips have translations (otherwise annoying debug messages)
		OpenSeadragon.setString('Tooltips.SelectionToggle', 'Toggle selection');
		OpenSeadragon.setString('Tooltips.SelectionConfirm', 'Confirm selection');

		//add the selection (rectangle) support (Picturae plugin)
		if(this.props.annotationClient.config.editOptions.Segment) { //TODO change this check later on ()
			this.selector = this.viewer.selection({
				showConfirmDenyButtons: true,
				styleConfirmDenyButtons: true,
				returnPixelCoordinates: true,
				//crossOriginPolicy: 'Anonymous',
				keyboardShortcut: 'c', // key to toggle selection mode
				rect: null, // initial selection as an OpenSeadragon.SelectionRect object
				startRotated: false, // alternative method for drawing the selection; useful for rotated crops
				startRotatedHeight: 0.1, // only used if startRotated=true; value is relative to image height
				restrictToImage: false, // true = do not allow any part of the selection to be outside the image
				onSelection: this.onSelection.bind(this),
				prefixUrl: '/static/vendor/openseadragonselection-master/images/',
				navImages: { // overwrites OpenSeadragon's options
					selection: {
						REST:   'selection_rest.png',
						GROUP:  'selection_grouphover.png',
						HOVER:  'selection_hover.png',
						DOWN:   'selection_pressed.png'
					},
					selectionConfirm: {
						REST:   'selection_confirm_rest.png',
						GROUP:  'selection_confirm_grouphover.png',
						HOVER:  'selection_confirm_hover.png',
						DOWN:   'selection_confirm_pressed.png'
					},
					selectionCancel: {
						REST:   'selection_cancel_rest.png',
						GROUP:  'selection_cancel_grouphover.png',
						HOVER:  'selection_cancel_hover.png',
						DOWN:   'selection_cancel_pressed.png'
					},
				}
			});

			//make sure the annotations are updated per page/image
			this.viewer.addHandler('page', e => {
				//console.debug(e.page);
			});

			//for debugging only
			/*this.viewer.addHandler('canvas-click', (target, info) => {
		        // The canvas-click event gives us a position in web coordinates.
		        const webPoint = target.position;
		        // Convert that to viewport coordinates, the lingua franca of OpenSeadragon coordinates.
		        const viewportPoint = this.viewer.viewport.pointFromPixel(webPoint);
		        // Convert from viewport coordinates to image coordinates.
		        const imagePoint = this.viewer.viewport.viewportToImageCoordinates(viewportPoint);
		        // Show the results.
		        console.log(webPoint.toString(), viewportPoint.toString(), imagePoint.toString());
		    });*/

			return new Promise(resolve => {
				this.viewer.addHandler('open', (target, info) => {
					resolve(true);
					this.viewer.removeOverlay('runtime-overlay');
    				this.__showSearchHighlight(this.props.activeMediaObject)
				});
			});
		}

	}

	onChangeTarget = target => { //whenever the annotation target (possibly the viewed image here) has changed
		this.renderAnnotations();
	};

	onSaveAnnotation = annotation => {
		this.renderAnnotations();
		if(this.selector) this.selector.toggleState();
	};

	renderAnnotations = () => {
		this.viewer.clearOverlays();
		const overlays = this.props.annotationClient.annotations || [];
		overlays.forEach(annotation => {
			this.renderOverlay(annotation);
		});
	};

	deleteAnnotation(annotation, e) {
		if(e) { //FIXME prevent zoom (this solution does not work...)
			e.preventDefault();
			e.stopPropagation();
		}
		this.props.annotationClient.delete(annotation);
	}

	onDeleteAnnotation = annotation => { //remove the overlay, once the annotation was deleted from the server
		this.viewer.removeOverlay(annotation.id);
	};

	/* --------------------------------------------------------------
	-------------------------- ANNOTATION CRUD ----------------------
	---------------------------------------------------------------*/

	async onSelection(rect) { //first set the active selection in the annotation client
		await this.props.annotationClient.setActiveSelection({
            rect : {
                x : rect.x,
                y : rect.y,
                w : rect.width,
                h : rect.height
            },
            rotation : rect.rotation
        });
        this.props.annotationClient.saveSelection(null, false);
	};

	onSetActiveAnnotation = eventData => {
		this.renderAnnotations();
	};

	renderOverlay = annotation => {
		//make sure the annotation has  a spatial segment in the target
		if(annotation.target.selector && annotation.target.selector.refinedBy) {
			const selectedArea = annotation.target.selector.refinedBy.rect;
			const translatedArea = this.viewer.viewport.imageToViewportRectangle(
				parseInt(selectedArea.x),
				parseInt(selectedArea.y),
				parseInt(selectedArea.w),
				parseInt(selectedArea.h)
			);

			//the actual overlay div
			const overlay = document.createElement('div');
			const classNames = [
				IDUtil.cssClassName('overlay', this.CLASS_PREFIX)
			]
			if(this.props.annotationClient.activeAnnotation && annotation.id == this.props.annotationClient.activeAnnotation.id) {
				classNames.push('active');
			}
			overlay.className = classNames.join(' ');
			overlay.onclick= this.props.annotationClient.setActiveAnnotation.bind(this, annotation);
			overlay.ondblclick = this.props.annotationClient.edit.bind(this, annotation);
			overlay.id = annotation.id;

			//add the remove button
			const removeBtn = document.createElement('button');
			removeBtn.onclick = this.deleteAnnotation.bind(this, annotation);
			const removeIcon = document.createElement('span');
			removeIcon.className = IconUtil.getUserActionIcon('remove');
			removeBtn.appendChild(removeIcon);

			overlay.appendChild(removeBtn);

			this.viewer.addOverlay({
				element: overlay,
				location: translatedArea
			});
		}
	}

	render = () => <div id="img_viewer" className={IDUtil.cssClassName('image-viewer')}></div>;

}

ImageViewer.propTypes = {
    useCredentials: PropTypes.bool,
    mediaObjects: PropTypes.array,
    annotationClient: PropTypes.object
};
