import React from "react";
import ReactTooltip from 'react-tooltip';
import ReactJson from 'react-json-view'
import PropTypes from 'prop-types';

import DocumentAPI from './api/DocumentAPI';
import ProjectAPI from './api/ProjectAPI';
import PlayoutAPI from './api/PlayoutAPI';
import AnnotationAPI from './api/AnnotationAPI';

import IDUtil from './util/IDUtil';
import IconUtil from './util/IconUtil';
import ComponentUtil from './util/ComponentUtil';
import LocalStorageHandler from "./util/LocalStorageHandler";
import PaginationUtil from './util/PaginationUtil';
import CollectionUtil from './util/CollectionUtil';
import BookmarkUtil from './util/BookmarkUtil';

import Query from './model/Query';

import ToolHeader from './components/shared/ToolHeader';
import SimplePaging from './components/search/SimplePaging';
import FlexBox from './components/FlexBox';
import FlexModal from './components/FlexModal';

//these components need to be sorted out
import BookmarkSelector from './components/bookmark/BookmarkSelector';
import FlexRouter from './util/FlexRouter';
import MetadataTable from './components/search/MetadataTable';
import LoadingSpinner from './components/helpers/LoadingSpinner';

//moved to resourceviewer_old (so it can be removed later)
import FlexPlayer from './components/resourceviewer_old/player/FlexPlayer';
import FlexImageViewer from './components/resourceviewer_old/player/FlexImageViewer';
import AnnotationUtil from './components/resourceviewer_old/util/AnnotationUtil';
import AnnotationBox from './components/resourceviewer_old/annotation/AnnotationBox';
import AnnotationList from './components/resourceviewer_old/annotation/AnnotationList';
import LDResourceViewer from './components/resourceviewer_old/linkeddata/LDResourceViewer';

//the flux package is also up for deletion later on
import AnnotationActions from './components/resourceviewer_old/annotation/flux/AnnotationActions';
import AnnotationStore from './components/resourceviewer_old/annotation/flux/AnnotationStore';

//import TranscriptExample from './components/transcript.json';

/*
	1. The ItemDetailsRecipe takes care of tying the components together according to the recipe
	2. Each media player (and any other annotation target) in the recipe takes care of loading its own annotations
	3. Ideally the whole query that led to this page should be reflected in the GET parameters (for sharing)
	4. There can only be one active annotation; this recipe must know which component has the active annotation?
	5.

	leesvoer: http://blog.andrewray.me/flux-for-stupid-people/

	TODO:
	- make sure this old crap is replaced with something new:
		- the fq annotations were found on this record/program
			- however the annotations are related to media fragments (also)
			- distinguish loading of media fragment annotations & record/program annotations

	OFF AIR CONTENT
		http://localhost:5304/tool/default-item-details?id=4272263@program&cid=nisv-catalogue-aggr&st=%224272263@program%22

*/

//TODO move most of the stuff to a new component called ResourceViewer
class ItemDetailsRecipe extends React.Component {

	constructor(props) {
		super(props);
		this.state = {
			showAnnotationModal : false, //triggered by the media players whenever an annotation needs to be edited
			showBookmarkModal : false, //showing the bookmark selector

			activeProject : LocalStorageHandler.getJSONFromLocalStorage('stored-active-project'),

			userProjects : [],

			itemData : null, //populated via componentDidMount

			activeMediaTab : 0, //which tab, i.e. media player, is visible/active

			//These variables are passed on to the media players (as props) that actually show the annotations.
			//These variables are filled by listening to the AnnotationStore, which are triggered by the players...
			//TODO All this is kind of weird and should be optimised
			activeAnnotation: null,
			activeSubAnnotation: null,//TODO this will be removed whenever switching to the new graph model
			annotationTarget : null,

			found : false, //whether the item metadata could be found

			resourceAnnotations : [],

			collectionConfig : null,

			isLoading : false //when paging causes a call to the the search API for fetching the next page of results
		};
		this.tabListeners = false;
		this.CLASS_PREFIX = 'rcp__id';

        PropTypes.checkPropTypes(ItemDetailsRecipe.propTypes, this.props, 'prop', this.constructor.name);
    }

	//starts listening to any annotation actions, triggered by the players, and fetches the item details
	componentDidMount() {
		//make sure to listen to the correct events (TODO determine this based on the recipe)
		AnnotationStore.bind('edit-annotation', this.editAnnotation.bind(this));
		AnnotationStore.bind('set-annotation', this.setActiveAnnotation.bind(this));
		AnnotationStore.bind('play-annotation', this.setActiveAnnotation.bind(this));
		AnnotationStore.bind('save-annotation', this.onSaveAnnotation.bind(this));
		AnnotationStore.bind('del-annotation', this.onDeleteAnnotation.bind(this));

		if(this.props.params.id && this.props.params.cid) {
			let searchLayer = this.props.params.cid;
			if(this.props.params.l) {
				searchLayer += '__' + this.props.params.l;
			}
			DocumentAPI.getItemDetails(
				searchLayer,
				this.props.params.id,
				this.onLoadItemData.bind(this)
			);
		}

		// Get projects list for current user
        ProjectAPI.list(this.props.user.id, null, projects => {
            this.setState({
                userProjects : projects
            })
        });
	}

	//makes sure to update the annotation target whenever the user selects another media object by
	//navigating to another tab (currently each media object is put under a tab...)
	//TODO replace the stupid tabs with a select box or something
	componentDidUpdate() {
		//FIXME a horrible way to attach a tab listener here instead of in componentDidMount
		//(for now we have to wait until the jquery is available... )
		if(!this.tabListeners) {
			$('a[data-toggle="tab"]').on('show.bs.tab', function (e) {
				const target = $(e.target).attr("href"); // activated tab
				const index = target.substring('#mo__'.length);
				const annotationTarget = this.getAnnotationTarget(this.state.itemData, index);
				if(annotationTarget) {
					this.setActiveAnnotationTarget.call(this, annotationTarget);
				} else {
					console.debug('There is no valid target?');
				}

			}.bind(this));
			this.tabListeners = true;
		}
	}

	onComponentOutput = (componentClass, data) => {
		if(componentClass === 'ToolHeader') {
			this.setState(
				{activeProject : data},
				() => {
					this.onProjectChanged.call(this, data)
				}
			);
		} else if(componentClass === 'BookmarkSelector') {
			if(data && data.allGroups && data.selectedGroups) {
				ComponentUtil.hideModal(this, 'showBookmarkModal', 'bookmark__modal', true, () => {
					BookmarkUtil.bookmarkToGroupInProject(
						this.state.itemData.resourceId,
						this.state.itemData.index,
						data.allGroups,
						data.selectedGroups,
						this.onSaveBookmarks
					);
				});
			}
		} else if(componentClass === 'FlexImageViewer') {
			this.setActiveAnnotationTarget({
				source : data.assetId //data => mediaObject
			})
		} else if(componentClass === 'FlexPlayer') {
			this.setActiveAnnotationTarget({
				source : data.assetId //data => mediaObject
			})
		} else if(componentClass === 'LDResourceViewer') {
			this.browseEntity(data);
		}
	};

	onLoadItemData(collectionId, resourceId, data) {
		let found = data ? data.found : false;
		if(data && data.error) {
			found = false; //e.g. in case of access denied
		}
		if(collectionId && found !== false) {
			//add the resource to the list of visited items
			LocalStorageHandler.pushItemToLocalStorage('stored-visited-results', resourceId);

			CollectionUtil.generateCollectionConfig(
				this.props.clientId,
				this.props.user,
				collectionId,
				function(config) {
					const itemDetailData = config.getItemDetailData(data);
					found = itemDetailData !== null;
					if(found) {
						//determine which media contant tab should be active
						let activeMediaTab = 0;
						if(itemDetailData.playableContent && this.props.params.fragmentUrl) {
							for(let i = 0;i<itemDetailData.playableContent.length;i++) {
								const mediaObject = itemDetailData.playableContent[i];
								if(mediaObject.url === this.props.params.fragmentUrl) { //FIXME this should be changed to the asset ID of the media object
									activeMediaTab = i;
									break;
								}
							}
						}
						const desiredState = {
							itemData : itemDetailData,
							annotationTarget : this.getAnnotationTarget.call(this, itemDetailData), //for the list
							found : true,
							activeMediaTab : activeMediaTab,
							collectionConfig : config
						};
						//TODO make sure this works for all carriers!!
						if (config.requiresPlayoutAccess() && itemDetailData.playableContent) {
						    if (itemDetailData.playableContent.length > 0 && !itemDetailData.playableContent[0].playoutAccess === true){
						        PlayoutAPI.requestAccess(
								itemDetailData.playableContent[0].contentServerId,
								itemDetailData.playableContent[0].contentId,
								desiredState,
								this.onLoadPlayoutAccess.bind(this)
							    )
						    } else {
						        this.setState(desiredState);
						    }
						} else {
							this.setState(desiredState);
						}

						//finally load the resource annotation with motivation bookmarking
						this.refreshResourceAnnotations(itemDetailData.resourceId);
					}
				}.bind(this)
			);
		}
		if(found === false) {
			this.setState({
				itemData : data,
				annotationTarget : null,
				found : false
			});
		}
	}

	//TODO call this after the details are loaded
	onLoadPlayoutAccess(accessApproved, desiredState) {
		this.setState(desiredState);
	}

	onPaged = (activeResource, newSearchResults) => { //TODO get the path from the recipe or something else
		if(activeResource) {
			//FlexRouter.gotoResourceViewer('/tool/default-item-details', activeResource, this.props.params.st);
			FlexRouter.gotoResourceViewerOld('tool/default-item-details', activeResource, this.props.params.st);
		}
	}

	onLoading = () => {
		this.setState({isLoading : true});
	}

	/* ------------------------------------------------------------------------------
	------------------------------- ANNOTATION RELATED FUNCTIONS --------------------
	------------------------------------------------------------------------------- */

	//determine which (media object) target on the page should be the active annotation target
	getAnnotationTarget(itemDetailData, index=0) {
		if(itemDetailData && itemDetailData.playableContent) {
			const mediaObject = itemDetailData.playableContent[index];
			if(mediaObject) {
				const annotation = AnnotationUtil.generateW3CEmptyAnnotation(
					this.props.user,
					this.state.activeProject,
					itemDetailData.index,
					itemDetailData.resourceId,
					mediaObject
				);
				return annotation.target;
			}
		}
		return null;
	}

	onSaveAnnotation(annotation) {
		ComponentUtil.hideModal(this, 'showAnnotationModal' , 'annotation__modal', true);
		//finally update the resource annotations (the "bookmark")
		this.refreshResourceAnnotations();
	}

	onDeleteAnnotation(annotation) {
		ComponentUtil.hideModal(this, 'showAnnotationModal', 'annotation__modal', true);
		//finally update the resource annotations (the "bookmark")
		this.refreshResourceAnnotations();
	}

	//TODO currently this is only called via the ugly componentDidUpdate() function
	//FIXME this only properly supports whenever the target is a media object (see FlexPlayer)
	setActiveAnnotationTarget(annotationTarget) {
		this.setState(
			{annotationTarget : annotationTarget},
			() => {
				AnnotationActions.changeTarget(annotationTarget)
			}
		);
	}

	//overall there can be only one active annotation
	//TODO extend with activeSubAnnotation?
	setActiveAnnotation(annotation) {
		this.setState({
			annotationTarget: annotation.target,
			activeAnnotation: annotation,
			activeSubAnnotation : null
		});
	}

	//tied to the annotateResourceBtn; used to annotate a resource as a whole
	annotateResource() {
		if(this.state.collectionConfig && this.state.itemData) {
			let annotation = this.getResourceAnnotation();
			if(!annotation) {
				annotation = AnnotationUtil.generateW3CEmptyAnnotation(
					this.props.user,
					this.state.activeProject,
					this.state.collectionConfig.getCollectionId(),
					this.state.itemData.resourceId
				);
			}
			this.editAnnotation(annotation, null);
		}
	}

	//returns the annotation that is directly added to the resource via the "Annotate resource" button
	getResourceAnnotation() {
		let annotation = null;
		if(this.state.resourceAnnotations) {
			const temp = this.state.resourceAnnotations.filter(
				a => a.motivation !== 'bookmarking' && a.target.source === this.state.itemData.resourceId
			);
			annotation = temp.length > 0 ? temp[0] : null;
		}
		return annotation
	}

	//show the annnotation form with the correct annotation target
	//TODO extend this so the target can also be a piece of text or whatever
	editAnnotation(annotation, subAnnotation) {
		if(annotation.target) {
			this.setState({
				showAnnotationModal: true,
				annotationTarget: annotation.target,
				activeAnnotation: annotation,
				activeSubAnnotation : subAnnotation
			});
		}
	}

	//returns all annotations that are tied to the given resourceId
	refreshResourceAnnotations(resourceId = null) {
		if(resourceId === null) {
			resourceId = this.state.itemData.resourceId;
		}
		const filter = {
			'target.selector.value.id' : resourceId,
			'user.keyword' : this.props.user.id,
		};
		if(this.state.activeProject && this.state.activeProject.id) {
			filter['project'] = this.state.activeProject.id
		}
		AnnotationAPI.getFilteredAnnotations(
			this.props.user.id,
			filter,
			null,
			this.onLoadResourceAnnotations.bind(this),
			0, //offset
			250, //size
			null, //sort direction
			null //dateRange
		);
	}

	onLoadResourceAnnotations(annotationList) {
		this.setState({
			resourceAnnotations : annotationList || []
		})
	}

	renderResourceAnnotationsTooltip() {
    	if(!this.state.resourceAnnotations) {
    		return null;
    	}
    	let html = '';
    	const bookmarkGroups = this.state.resourceAnnotations.filter(a => a.motivation === 'bookmarking');
    	const annotations = this.state.resourceAnnotations.filter(a => a.motivation !== 'bookmarking');

    	if (bookmarkGroups.length > 0) {
    		html += '<h5><u>Bookmark group(s)</u>:</h5><ul>';
    		html += bookmarkGroups.map(
         	   group => group.body && group.body.length > 0 && group.body[0].label ? "<li>" + group.body[0].label + "</li>" : ''
        	).join('');
        	html += '</ul>';
    	}
    	//count the number of annotation bodies
    	if(annotations) {
    		//html += '<h5><u>Number of annotated parts of this resource</u>: '+annotations.length+'</h5>';
    		//count the number of annotation bodies
    		let bodyCount = 0;
    		annotations.forEach(a => bodyCount += a.body ? a.body.length : 0);
    		html += '<h5><u>Number of annotations</u>: '+bodyCount+'</h5>';
    	}


        return html;
    }

	/* ------------------------------------------------------------------
	----------------------- PROJECTS ------------------------------------
	--------------------------------------------------------------------- */

	onProjectChanged(project) {
		LocalStorageHandler.storeJSONInLocalStorage('stored-active-project', project);
		AnnotationActions.changeProject(project);
		this.refreshResourceAnnotations();
	}

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

	//this will first check if a project was selected. Then either bookmarks or opens the project selector first
	bookmark = () => {
		this.setState({
			showBookmarkModal : true
		});
	};

	onSaveBookmarks = data => {
		this.setState({
			selectedRows : {},
			allRowsSelected : false
		}, () => {
			this.refreshResourceAnnotations();
		})
	};

	/************************************************************************
	************************ BROWSING FUNCTIONS *******************************
	*************************************************************************/

	browseEntity(entity) {
		const selectedFacets = {};
		selectedFacets[entity.field] = [entity.value];
		const query = Query.construct({
			collectionId : this.state.collectionConfig.getCollectionId(),
			term : this.props.params.st,
			desiredFacets : [{
				field: entity.field,
				title : this.state.collectionConfig.toPrettyFieldName(entity.field), //FIXME this could be also obtained from a user setting
				type: "string",
				exclude : false
			}],
			selectedFacets : selectedFacets
		}, this.state.collectionConfig);

		LocalStorageHandler.storeJSONInLocalStorage('stored-priority-query', query);
		FlexRouter.gotoSingleSearch('prio')
	}

	getFieldValues(fieldName) {
		//filter out the uninteresting fields
		if(fieldName.indexOf('@context') !== -1 ||
			fieldName.indexOf('hasFormat') !== -1 ||
			fieldName.indexOf('@language') !== -1) {
			return null
		}
		//make sure to remove the ES .keyword suffix, since the rawdata fieldnames don't have them
		fieldName = fieldName.indexOf('.keyword') === -1 ?
			fieldName :
			fieldName.substring(0, fieldName.length - 8)

		//this is the data to search for the values of the selected keyword field
		let curObj = this.state.itemData.rawData;

		//split the field name in path elements for lookup
		const path = fieldName.split('.');
		let i = 0;

		//now look for the values of the selected field
		while(i < path.length) {
			if(curObj) {
				//check if the current object is a list and the current path selects @value attributes from it
				if(typeof(curObj) === "object" && curObj['@value'] === undefined && path[i] === '@value') {
					curObj = curObj.map(obj => obj[path[i]]);
					break;
				}
				//otherwise continue down the path, until the end is reached
				curObj = curObj[path[i]];
				i++;
			} else {
				break;
			}
		}
		//always wrap the end-result in a list
		if(typeof(curObj) === 'string') {
			return [curObj]
		}
		return curObj;
	}

	/************************************************************************
	************************ CALLED BY RENDER *******************************
	*************************************************************************/


	checkMediaObjectIsSelected(mediaObject) {
		if(mediaObject.assetId === this.props.params.assetId) {
			mediaObject.start = this.props.params.s;
			mediaObject.end = this.props.params.e;
			mediaObject.x = this.props.params.x;
			mediaObject.y = this.props.params.y;
			mediaObject.w = this.props.params.w;
			mediaObject.h = this.props.params.h;
			return true;
		}
		return false;
	}


	//TODO this function is almost the same as checkMediaObjectIsSelected, remove the latter
	getSelectedMediaObject() {
		let mediaObject = null;
		if(this.props.params.assetId) {
			mediaObject = {assetId : this.props.params.assetId};
			if(this.props.params.s) {
				mediaObject.start = this.props.params.s;
			}
			if(this.props.params.e) {
				mediaObject.end = this.props.params.e;
			}
			if(this.props.params.x) {
				mediaObject.x = this.props.params.x;
			}
			if(this.props.params.y) {
				mediaObject.y = this.props.params.y;
			}
			if(this.props.params.w) {
				mediaObject.w = this.props.params.w;
			}
			if(this.props.params.h) {
				mediaObject.h = this.props.params.h;
			}
		}
		return mediaObject;
	}

	getRenderedMediaContent() {
		//first get all of the media contents per media type
		const tabs = [
			this.getVideoTabContents(),
			this.getAudioTabContents(),
			this.getImageTabContents(),
			this.getApplicationTabContents()
		].filter(t => t != null);

		//generate the tabs
		const mediaTabs = tabs.map((tab, index) => {
			const iconClass = IconUtil.getMimeTypeIcon(tab.type);
			const active = this.props.params.fragmentUrl ? tab.active : index === 0;
            const mediaType = tab.type === 'application' ? 'External resource' : tab.type;

			return (
				<li key={tab.type + '__tab'}
					className={active ? 'active' : ''}>
					<a data-toggle="tab" href={'#' + tab.type + '__content'}>
						<span className={iconClass}/>&nbsp;{mediaType}
					</a>
				</li>
			)
		}, this);

		//then the contents of the tabs
		const mediaTabContents = tabs.map((tab, index) => {
			const active = this.props.params.fragmentUrl ? tab.active : index === 0;
			return (
				<div key={tab.type + '__content'}
					id={tab.type + '__content'}
					className={active ? 'tab-pane active' : 'tab-pane'}>
					<div className={IDUtil.cssClassName('media-player', this.CLASS_PREFIX)}>
						{tab.content}
					</div>
				</div>
			);
		}, this);

		//finally generate the mediaPanel
		return (
			<FlexBox title="Media">
				<ul className="nav nav-tabs">
					{mediaTabs}
				</ul>
				<div className="tab-content">
					{mediaTabContents}
				</div>
			</FlexBox>
		);
	}

	//each video will get a separate player (for now)
	getVideoTabContents() {
		const mediaObjects = this.state.itemData.playableContent.filter(content => {
			return content.mimeType.indexOf('video') !== -1;
		});
		if(mediaObjects.length > 0) {
			const content = (
				<FlexPlayer
					mediaObjects={mediaObjects}
                    title={this.state.itemData.title}
					mediaType='video'

					transcript={this.state.itemData.rawData.layer__asr || null}

					initialSearchTerm={this.props.params.st} //every player could interpret highlighting a search term

                    useCredentials={this.state.collectionConfig.requiresPlayoutAccess()}

                    hideOffAirContent={this.state.collectionConfig.hideOffAirContent()}

					user={this.props.user} //current user
					project={this.state.activeProject} //selected via the ProjectSelector
					resourceId={this.state.itemData.resourceId}
					collectionId={this.state.itemData.index}

					active={true}

					annotationSupport={this.props.recipe.ingredients.annotationSupport} //annotation support the component should provide
					annotationLayers={this.props.recipe.ingredients.annotationLayers} //so the player can distribute annotations in layers

					onOutput={this.onComponentOutput}
				/>
			);
			return {type : 'video', content : content, active : true}
		}
		return null;
	}

	getAudioTabContents() {
		const mediaObjects = this.state.itemData.playableContent.filter(content => {
			return content.mimeType.indexOf('audio') !== -1;
		});
		if(mediaObjects.length > 0) {
			const content = (
				<FlexPlayer
					mediaObjects={mediaObjects}
                    title={this.state.itemData.title}
					mediaType='audio'

					transcript={this.state.itemData.rawData.layer__asr || null}
					initialSearchTerm={this.props.params.st} //every player could interpret highlighting a search term

					useCredentials={this.state.collectionConfig.requiresPlayoutAccess()}

					user={this.props.user} //current user
					project={this.state.activeProject} //selected via the ProjectSelector
					resourceId={this.state.itemData.resourceId}
					collectionId={this.state.itemData.index}

					active={true}

					annotationSupport={this.props.recipe.ingredients.annotationSupport} //annotation support the component should provide
					annotationLayers={this.props.recipe.ingredients.annotationLayers} //so the player can distribute annotations in layers

					onOutput={this.onComponentOutput}
				/>
			);
			return {type : 'audio', content : content, active : true}
		}
		return null;
	}

	//images all go into one image viewer (as a playlist)
	getImageTabContents() {
		let isActive = false;
		let cors = true;
		let content = null;
		const images = this.state.itemData.playableContent.filter(content => {
			return content.mimeType.indexOf('image') !== -1;
		});
		if(images.length > 0) {
			images.forEach((mediaObject, index) => {
				mediaObject.id = 'application__' + index;
				if(!isActive) {
					isActive = this.checkMediaObjectIsSelected.call(this, mediaObject);
				}
				if(mediaObject.hasOwnProperty('cors') && mediaObject.cors === false) {
					cors = false;
				}
			});
			//CORS is required for OpenSeaDragon support!
			if(cors === false) {
				//for now simply draw a bunch of images on the screen (which means: no annotation support!)
				content = images.map((i) => {
					return (<img src={i.url}/>);
				})
			} else {
				//use openseadragon with annotation support
				content = (
					<FlexImageViewer
						user={this.props.user} //current user
						project={this.state.activeProject} //selected via the ProjectSelector

						resourceId={this.state.itemData.resourceId}
						collectionId={this.state.itemData.index}

						useCredentials={this.state.collectionConfig.requiresPlayoutAccess()}

						mediaObjects={images}
						selectedMediaObject={this.getSelectedMediaObject()}

						annotationSupport={this.props.recipe.ingredients.annotationSupport} //annotation support the component should provide
						annotationLayers={this.props.recipe.ingredients.annotationLayers} //so the player can distribute annotations in layers

						editAnnotation={this.editAnnotation.bind(this)} //each annotation support should call this function

						onOutput={this.onComponentOutput}
					/>
				)
			}
			return {type : 'image', content : content, active : isActive}
		}
	}

	//application mimetypes will be loaded into iFrames
	getApplicationTabContents() {
		let isActive = false;
		const applications = this.state.itemData.playableContent.filter(content => {
			return content.mimeType.indexOf('application') !== -1;
		});
		if(applications.length > 0) {
			const content = applications.map((mediaObject, index) => {
				mediaObject.id = 'application__' + index;
				if(!isActive) {
					isActive = this.checkMediaObjectIsSelected.call(this, mediaObject);
				}
				if(mediaObject.mimeType === 'application/javascript') {
					return (
						<div style={{margin : '10px'}}>
							Deze media kan i.v.m. beperkingen m.b.t. auteursrecht of het type content niet binnen de media suite worden afgespeeld
							<br/>
							<a href={mediaObject.url} target="_external_js">Bekijk de media extern</a>
						</div>
					)
				} else {
					return (
						<iframe src={mediaObject.url} width="650" height="550"/>
					);
				}
			});
			return {type : 'application', content : content, active : isActive}
		}
		return null;
	}

    /* ------------------------------------ TOP BUTTON BAR ----------------------------- */

    //only render when coming from the single search recipe (checking this.props.param.bodyClass == noHeader)
    renderResultListPagingButtons = () => {
        return (
			<SimplePaging
				browseSelection={false}
				searchResult={this.state.itemData}
				collectionConfig={this.state.collectionConfig}

				onPaged={this.onPaged}
				onLoading={this.onLoading} //also attach a loading graphic to this one

				selected={false} //look it up in the storedRows
				onSelect={null} //when supplied, it will render a select button

				enableBackButton={true}
				enableResourceViewerButton={false}
			/>
        )
    };

	renderProjectButtons = (resourceAnnotations, annotationSupport, projectsDisabled) => {
    	if(projectsDisabled) return null;

    	const numBookmarkGroups = resourceAnnotations ? resourceAnnotations.filter(a => a.motivation === 'bookmarking').length : 0;
    	const numResourceAnnotations = resourceAnnotations ? resourceAnnotations.length : 0;
		//const hasResourceAnnotation = this.getResourceAnnotation() ? true : false; //FIXME strange check

		//draw the bookmark group button always (with the number of bookmark groups)
		const bookmarkBtn = (
			<button
				className="btn btn-secondary"
				onClick={this.bookmark}
				title="Adds this resource to one or more bookmark groups (the number of groups is showing in brackets)">
				Bookmark resource ({numBookmarkGroups})
			</button>
		)

		//draw the bookmark icon if there are ANY annotations or bookmark groups attached to this resource
        const bookmarkIcon = numResourceAnnotations > 0 ? (
        	 <div
        	 	data-for="__res_anno"
        	 	data-tip={this.renderResourceAnnotationsTooltip()}
        	 	data-html={true}
        	 	className={IDUtil.cssClassName('bookmarked', this.CLASS_PREFIX)}>
				<i className="fa fa-bookmark"/>
				<ReactTooltip id={'__res_anno'}/>
			</div>
        ) : null;

        //draw the button for annotating the resource (only when annotation support is activated)
		const resourceAnnotationBtn = (
			<button
				className="btn btn-secondary"
				onClick={this.annotateResource.bind(this)}
				title={`Annotate the (abstract) resource that represents all information on this page (ID = ${this.state.itemData.resourceId})`}>
				Annotate resource
			</button>
		);

        return annotationSupport ? (
        	<div className={IDUtil.cssClassName('project-buttons', this.CLASS_PREFIX)}>
				{resourceAnnotationBtn}
				{bookmarkBtn}
				{bookmarkIcon}
			</div>
		) : null;
    };


    /* ------------------------------------ ALL THE MODALS ----------------------------- */

    renderAnnotationModal = (showModal, activeProject, activeAnnotation, activeSubAnnotation) => {
    	if(showModal && activeAnnotation && activeAnnotation.target) {
			return (
				<FlexModal elementId="annotation__modal"
					stateVariable="showAnnotationModal"
					float="right"
					owner={this}
					title={'Annotate: ' + activeAnnotation.target.source}>
					<AnnotationBox
						user={this.props.user} //current user
						project={activeProject} //selected via ProjectSelector
						annotation={activeAnnotation}
						activeSubAnnotation={activeSubAnnotation}
						annotationModes={this.props.recipe.ingredients.annotationModes}/>
				</FlexModal>
			);
		}
		return null
    }

    renderBookmarkModal = (showBookmarkModal, activeProject, itemData) => {
    	if(showBookmarkModal && activeProject) {
			return (
				<FlexModal
					elementId="bookmark__modal"
					stateVariable="showBookmarkModal"
					owner={this}
					size="large"
					title="Select one or more bookmark groups to associate the current resource with">
						<BookmarkSelector
							onOutput={this.onComponentOutput}
							user={this.props.user}
							project={activeProject}
							collectionId={itemData.index}
							resourceId={itemData.resourceId}
							/>
				</FlexModal>
			)
		}
		return null
    }

    /* ----------------------------- EXPLORATION & RECOMMENDATION -------------------- */

    renderExploreBlock = () => {
    	const exploreFields = {};
    	const orgNames = {};
		this.state.collectionConfig.getKeywordFields().forEach(kw => {
			const values = this.getFieldValues(kw);
			if(values) {
                exploreFields[this.state.collectionConfig.toPrettyFieldName(kw)] = values;
                orgNames[this.state.collectionConfig.toPrettyFieldName(kw)] = kw;
			}
		});
		if(Object.keys(exploreFields).length === 0) {
			return null;
		}
        return (
            <FlexBox title="Related content (experimental)" isVisible={false}>
                <div className={IDUtil.cssClassName('keyword-browser', this.CLASS_PREFIX)}>
                    <h4>Search for related content</h4>
                    <div className="property-list">
                        {
                            Object.keys(exploreFields).sort().map((kw, index) => {
                            	if(Array.isArray(exploreFields[kw])) { //This can only be done if it are arrays!

	                                //make nice buttons for each available value for the current keyword
	                                const fieldValues = exploreFields[kw].map((value, i) => {
	                                    const entity = {field: orgNames[kw], value: value};
	                                    return (
	                                        <div key={'__e__' + i} className="keyword" onClick={this.browseEntity.bind(this, entity)}>
	                                            {entity.value}
	                                        </div>
	                                    )
	                                });
	                                //then return a block with a pretty field title + a column of buttons for each value
	                                return (
	                                    <div key={'__kw__' + index} className="cl_table cl_table--2cols">
	                                        <div className="cl_table-cell left-cell">
	                                            <span data-for={'tooltip__' + index} data-tip={orgNames[kw]} data-html={true}>
	                                                <i className="fa fa-info-circle"/>
	                                            </span> {kw}
	                                            <ReactTooltip id={'tooltip__' + index}/>
	                                        </div>
	                                        <div className="cl_table-cell right-cell">
	                                            {fieldValues}
	                                        </div>
	                                    </div>
	                                )
	                            }

                            })
                        }
                    </div>
                </div>
            </FlexBox>
        )
    }

    renderJSONDataBlock = () => {
    	if (this.state.itemData.rawData) {
            return (
	            <FlexBox isVisible={false} title="All Data">
	                <div className={IDUtil.cssClassName('keyword-browser', this.CLASS_PREFIX)}>
	                    <div className={IDUtil.cssClassName('raw-data', this.CLASS_PREFIX)}>
							<ReactJson src={this.state.itemData.rawData} />
						</div>
	                </div>
	            </FlexBox>
            )
        }
        return null;
    };

    renderHeader = (name, activeProject, projects, user) => (
        <ToolHeader
            name={name}
            projects={projects}
            activeProject={activeProject}
            onOutput={this.onComponentOutput}
            user={user}
        />
    );

	render() {
		if(!this.state.itemData) {
           return <LoadingSpinner message="Loading resource..."/>;
		} else if(this.state.found === false) {
			return (<h4>Either you are not allowed access or this item does not exist</h4>);
		} else {
			let annotationList = null;
			let mediaPanel = null;

			//FIXME there is something wrong with the arrow functions, bind() is still required!
			const annotationModal = this.renderAnnotationModal(
				this.state.showAnnotationModal,
				this.state.activeProject,
				this.state.activeAnnotation,
				this.state.activeSubAnnotation
			);

			const bookmarkModal = this.renderBookmarkModal(this.state.showBookmarkModal, this.state.activeProject, this.state.itemData);

			const projectButtons = this.state.activeProject ? this.renderProjectButtons(
				this.state.resourceAnnotations,
				this.props.recipe.ingredients.annotationSupport,
				this.props.recipe.ingredients.disableProjects
			) : null;

            let resourceListPagingButtons = null;

			//on the top level we only check if there is any form of annotationSupport
			if(this.props.recipe.ingredients.annotationSupport) {

				//draw the annotation list, which only shows annotations related to the active annotationTarget
				if(this.state.annotationTarget) {
					annotationList = (
						<AnnotationList
							key={'__anno-list__' + this.state.annotationTarget.source}
							user={this.props.user} //current user
							project={this.state.activeProject} //selected via ProjectSelector
							activeAnnotation={this.state.activeAnnotation} //the active annotation
							annotationTarget={this.state.annotationTarget} //the current annotation target (later this can be also an annotation)
						/>
					);
				}

			}
			if(this.props.params.bodyClass !== 'noHeader') {
				resourceListPagingButtons = this.renderResultListPagingButtons();
			}

			const header = !this.props.recipe.ingredients.disableProjects ? this.renderHeader(
                'Resource viewer',
                this.state.activeProject,
                this.state.userProjects,
                this.props.user
            ) : null;

			//render the complete metadata block, which includes unique and basic metadata
			const metadataPanel = (
				<FlexBox title="Metadata">
					<MetadataTable data={this.state.itemData}/>
				</FlexBox>
			);

			//media objects
			if(this.state.itemData.playableContent && this.state.itemData.playableContent.length > 0) {
				mediaPanel = this.getRenderedMediaContent();
			}

			//render the exploration block
            const exploreBlock = this.renderExploreBlock();

            const jsonDataBlock = this.renderJSONDataBlock();

		 	const loadingMessage = this.state.isLoading ? <LoadingSpinner message="Loading results..."/> : null;

			return (
				<div className={IDUtil.cssClassName('item-details-recipe')}>
					{loadingMessage}
					{annotationModal}
					{bookmarkModal}
					{header}
					<div className="row">
						<div className="col-md-12">
                            {resourceListPagingButtons}
							{projectButtons}
							<br/>
							<br/>
							{mediaPanel}
							<br/>
							<div className="row">
								<div className="col-md-7">
									<div className="row">
										{metadataPanel}
									</div>
                                    <div className="row">
                                        {exploreBlock}
                                    </div>
                                    <div className="row">
                                        {jsonDataBlock}
                                    </div>
								</div>
								<div className="col-md-5">
									{annotationList}
								</div>
								<br/>
							</div>
						</div>
					</div>
				</div>
			)
		}
	}
}

ItemDetailsRecipe.propTypes = {
	clientId : PropTypes.string.isRequired, // Required by __loadCollectionMetadata() in CollectionUtil
    params: PropTypes.shape({
        cid: PropTypes.string.isRequired, // Required by getItemDetails()
        id: PropTypes.string.isRequired, // Required by getItemDetails()
        st: PropTypes.string
    }).isRequired,
    user: PropTypes.shape({
        id: PropTypes.string.isRequired
    }).isRequired,
    recipe: PropTypes.shape({
        description: PropTypes.string,
        id: PropTypes.string,
        inRecipeList: PropTypes.bool,
        name: PropTypes.string.isRequired,
        phase: PropTypes.string,
        recipeDescription: PropTypes.string,
        type: PropTypes.string,
        url: PropTypes.string,
        ingredients: PropTypes.shape({
			useProjects: PropTypes.bool,
			annotationModes: PropTypes.object,
			annotationSupport: PropTypes.object
        }).isRequired
    }).isRequired

};

export default ItemDetailsRecipe;
