/*
 *  Copyright 2013 Anyware Services
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/**
 * This helper provides a dialog box to edit the dublin core metadata of a resource.
 * See #open method.
 */

Ext.define('Ametys.explorer.resources.helper.DublinCore', {
	singleton: true,
	
	/**
	 * @property {Boolean} _initialized True if the dialog box has been initialized.
	 * @private
	 */
	/**
	 * @property {Ext.form.Basic} _form The inner basic form.
	 * @private
	 */
	/**
	 * @property {Ametys.window.DialogBox} _box The dialog box
	 * @private
	 */
	
	/**
	 * Opens dialog box to edit the Dublin Core metadata of a resource.
	 * @param {String} id The id of the resource to edit.
	 * @param {Function} callback The callback function called when the resource was uploaded. Has the following parameters
	 * @param {String} callback.id The id of updated resource
	 * @param {Object} scope the callback scope
	 */
	open: function (id, callback, scope)
	{
        this._formReady = false;
        this._dirty = false;
        
		this._cbFn = callback || Ext.emptyFn
		this._cbScope = scope;
		
		this._delayedInitialize ();
		this._box.show();
		
		this._initForm (id);
	},
	
	/**
	 * @private
	 * Initialize the dialog box
	 * @return {Boolean} true if the box has been initialized (or if it was already initialized).
	 */
	_delayedInitialize: function()
	{
		if (this._initialized)
		{
			return true;
		}
		
		this._box = Ext.create('Ametys.window.DialogBox', {
			title :"{{i18n PLUGINS_EXPLORER_DC_DIALOG_TITLE}}",
			icon : Ametys.getPluginResourcesPrefix('explorer') + "/img/resources/dublincore_16.png",
			
			layout: 'fit',
			width: 530,
			maxHeight: 540,
			
			items: this._createFormPanel(),
			
			defaultFocus: 'dc_title',
			closeAction: 'hide',
			
            referenceHolder: true,
            defaultButton: 'validate',
			
			buttons: [{
				reference: 'validate',
				text: "{{i18n PLUGINS_EXPLORER_DC_DIALOG_BUTTON_OK}}",
				handler: this._ok,
				scope: this
			}, {
				text: "{{i18n PLUGINS_EXPLORER_DC_DIALOG_BUTTON_CANCEL}}",
				handler: this._cancel,
				scope: this
			}]
		});
		
		this._initialized = true;
		return true;
	},
	
	/**
	 * Get the data store for DCMI types
	 * @private
	 */
	_getDCMITypeStore: function ()
	{
		return Ext.create('Ext.data.Store', {
			model: 'Ametys.explorer.resources.helper.DublinCore.DCMITypeEntry',
			proxy: {
				type: 'ametys',
				plugin: 'explorer',
				url: 'dublincore/dcmitypes.xml',
				reader: {
					type: 'xml',
					record: 'type',
					rootProperty: 'types'
				}
			 },
			 
			 sortOnLoad: true,
			 sorters: [{property: 'label', direction:'ASC'}]
		});
	},
	
	/**
	 * Get the store for given metadata
	 * @param {String} metadataName The DC metadata name
	 * @private
	 */
	_getMetadataStore: function (metadataName)
	{
		return Ext.create('Ext.data.Store', {
			model: 'Ametys.explorer.resources.helper.DublinCore.MetadataEntry',
			proxy: {
				type: 'ametys',
				plugin: 'explorer',
				url: 'dublincore/enumeration',
				reader: {
					type: 'xml',
					record: 'entry',
					rootProperty: 'enumeration'
				}
			},
			
			sortOnLoad: true,
			sorters: [{property: 'label', direction:'ASC'}],
			
			listeners: {
			    beforeload: function (store, operation) {
                    operation.setParams(Ext.apply(operation.getParams() || {}, {
                        metadataName: metadataName
                    }));
			    }
			}
		});
	},
	
	/**
	 * Creates and return the form panel
	 * @return {Ext.form.Panel} The form panel
	 * @private
	 */
	_createFormPanel: function ()
	{
		this._enumeratedMetadataNames = this._getEnumeratedMetadataNames();
		
		return Ext.create('Ext.form.Panel', {
			border: false, 
			scrollable: true,
			defaults: {
				cls: 'ametys',
				anchor: '95%',
				xtype: 'textfield',
                listeners: {
                    'change': {fn: this._onFieldChange, scope: this}
                }
			},
			fieldDefaults : {
				labelAlign: 'right',
				labelSeparator: '',
				labelWidth: 100
			},
			
			items: [{
					// id
					xtype: 'hiddenfield',
					name: 'id'
				}, {
					// title
					xtype: 'textfield',
					name: 'dc_title',
					itemId: 'dc_title',
					fieldLabel: "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_TITLE_LABEL}}",
					ametysDescription: "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_TITLE_DESC}}"
				}, {
				    // creator
				    xtype: 'textfield',
				    name: 'dc_creator',
				    fieldLabel: "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_CREATOR_LABEL}}",
				    ametysDescription: "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_CREATOR_DESC}}"
				}, {
					// subject
					name: 'dc_subject',
					xtype: 'edition.textfield',
					fieldLabel: "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_SUBJECT_LABEL}}",
					ametysDescription: "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_SUBJECT_DESC}}",
					multiple: true
				}, {
					// description
					name: 'dc_description',
					fieldLabel: "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_DESCRIPTION_LABEL}}",
					ametysDescription: "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_DESCRIPTION_DESC}}"
				},
                // Publisher
                this._getItem ('dc_publisher', "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_PUBLISHER_LABEL}}", "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_PUBLISHER_DESC}}"),
				{
                    // Contributor
                    name: 'dc_contributor',
                    fieldLabel: "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_CONTRIBUTOR_LABEL}}",
                    ametysDescription: "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_CONTRIBUTOR_DESC}}"
                }, {
                    // Date
                    xtype: 'edition.date',
                    name: 'dc_date',
                    fieldLabel: "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_DATE_LABEL}}",
                    ametysDescription: "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_DATE_DESC}}"
                }, {
                    // DCMI types
                    xtype: 'combobox',
                    name: 'dc_type',
                    fieldLabel: "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_TYPE_LABEL}}",
                    ametysDescription: "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_TYPE_DESC}}",
                    
                    store: this._getDCMITypeStore(),
                    valueField: 'value',
                    displayField: 'label',
                    
                    editable: false,
                    forceSelection: true
                }, {
                    // Format
                    xtype: 'textfield',
                    name: 'dc_format',
                    fieldLabel: "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_FORMAT_LABEL}}",
                    ametysDescription: "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_FORMAT_DESC}}",
                    disabled: true
                }, {
                    // Identifier
                    xtype: 'textfield',
                    name: 'dc_identifier',
                    fieldLabel: "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_IDENTIFIER_LABEL}}",
                    ametysDescription: "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_IDENTIFIER_DESC}}",
                    disabled: true
                },
				// Source
				this._getItem ('dc_source', "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_SOURCE_LABEL}}", "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_SOURCE_DESC}}"),
				// Language
				this._getItem ('dc_language', "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_LANGUAGE_LABEL}}", "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_LANGUAGE_DESC}}"),
				{
					// Relation
					name: 'dc_relation',
					fieldLabel: "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_RELATION_LABEL}}",
					ametysDescription: "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_RELATION_DESC}}"
				}, {
                    // Coverage
                    name: 'dc_coverage',
                    fieldLabel: "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_COVERAGE_LABEL}}",
                    ametysDescription: "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_COVERAGE_DESC}}"
                },
				// Rights
				this._getItem ('dc_rights', "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_RIGHTS_LABEL}}", "{{i18n PLUGINS_EXPLORER_DC_DIALOG_META_RIGHTS_DESC}}")
			]
		});
	},
    
    /**
     * Callback when any field from the form has changed
     * @param {Ext.form.Field} field The field that has changed
     * @param {Object} newValue The value new field
     */
    _onFieldChange: function(field, newValue)
    {
        if (this._formReady)
        {
	        this._dirty = true;
        }
    },
	
	/**
	 * Get the list of enumerated DublinCore metadata
	 * @return {String[]} A array of metadata names
	 * @private
	 */
	_getEnumeratedMetadataNames: function ()
	{
		var metadataNames = [];
		
		var response = Ametys.data.ServerComm.send({
			plugin: 'explorer', 
			url: 'dublincore/enumerated-metadata', 
			priority: Ametys.data.ServerComm.PRIORITY_SYNCHRONOUS 
		});
		
		// TODO Handler error 
		
		var nodes = Ext.dom.Query.select('enumerations/metadata', response);
		for (var i=0; i < nodes.length; i++)
		{
			metadataNames.push(Ext.dom.Query.selectValue('', nodes[i]));
		}
		
		return metadataNames;
	},
	
	/**
	 * Get the item configuration
	 * @param {String} name The field name
	 * @param {String} label The field label
	 * @param {String} description The field description
	 * @private
	 */
	_getItem: function (name, label, description)
	{
		if (Ext.Array.contains(this._enumeratedMetadataNames, name))
		{
			return {
				xtype: 'combobox',
				name: name,
				fieldLabel: label,
				ametysDescription: description,
				
				store: this._getMetadataStore(name),
				valueField: 'value',
				displayField: 'label',
				
				editable: false,
				forceSelection: true
			};
		}
		else
		{
			return {
				xtype: 'textfield',
				name: name,
				fieldLabel: label,
				ametysDescription: description
			};
		}
	},
	
	/**
	 * @private
	 * Load the stores and initialize the form with the appropriate values.
	 * @param {String} id The id of resource.
	 */
	_initForm: function(id)
	{
		var form = this._box.down('form').getForm();
		form.reset();
		
		form.findField('id').setValue(id);
		
		// Load stores
		this._loadStores (id);
	},
	
	/**
	 * @private
	 * Load the stores before initializing the form with the appropriate values.
	 * @param {String} id The id of resource.
	 */
	_loadStores: function (id)
	{
		this._storecount = this._enumeratedMetadataNames.length + 1; // +1 form dc_type
		
		var form = this._box.down('form').getForm();
		form.findField('dc_type').getStore().load({callback: Ext.bind(this._loadCb, this, [id], false)});
		
		if (Ext.Array.contains(this._enumeratedMetadataNames, 'dc_publisher'))
		{
			form.findField('dc_publisher').getStore().load({callback: Ext.bind(this._loadCb, this, [id], false)});
		}
		
		if (Ext.Array.contains(this._enumeratedMetadataNames, 'dc_source'))
		{
			form.findField('dc_source').getStore().load({callback: Ext.bind(this._loadCb, this, [id], false)});
		}
		
		if (Ext.Array.contains(this._enumeratedMetadataNames, 'dc_language'))
		{
			form.findField('dc_language').getStore().load({callback: Ext.bind(this._loadCb, this, [id], false)});
		}
		
		if (Ext.Array.contains(this._enumeratedMetadataNames, 'dc_rights'))
		{
			form.findField('dc_rights').getStore().load({callback: Ext.bind(this._loadCb, this, [id], false)});
		}
	},
	
	/**
	 * Function called after a store was loaded.<br>
	 * Initialize the form when all stores were loaded.
	 * @param {String} id The id of resource.
	 */
	_loadCb: function (id)
	{
		// Wait for loading
		if (--this._storecount == 0)
		{
			Ametys.data.ServerComm.callMethod({
				role: 'org.ametys.plugins.explorer.resources.actions.ExplorerResourcesDAO', 
				methodName: 'getDCMetadata',
				parameters: [id],
				callback: {
					handler: this._fillForm,
					scope: this
				},
				errorMessage: "{{i18n PLUGINS_EXPLORER_DC_RESOURCE_META_ERROR}}",
				waitMessage: {
					target: this._box
				}
			});
		}
	},
	
	/**
	 * Callback function invoked after retrieving DC metadata values
	 * @param {Object} response the server response
	 * @param {String} response.id The id of DC object
	 * @param {Object} response.values The DC values
	 * @private
	 */
	_fillForm: function (response)
	{
		var form = this._box.down('form').getForm();
		
		form.findField('id').setValue(response.id);
		
		var values = response.values;
		Ext.Object.each(values, function(name, value) {
			field = form.findField(name);
			if (field != null)
			{
				field.setValue(value);
			}
		});
        
        this._formReady = true;
	},
	
	/**
	 * Function invoked when validating the dialog box
	 * @private
	 */
	_ok: function()
	{
        if (!this._dirty)
        {
            // no changes
	        this._box.hide();
            return;
        }
        
		var form = this._box.down('form').getForm();
		if (!form.isValid())
		{
			return;
		}
		
		Ametys.data.ServerComm.callMethod({
			role: 'org.ametys.plugins.explorer.resources.actions.ExplorerResourcesDAO', 
			methodName: 'setDCMetadata',
			parameters: [form.findField('id').getValue(), form.getFieldValues()],
			callback: {
				handler: this._saveCb,
				scope: this,
				arguments: {
					id: form.findField('id').getValue()
				}
			},
			waitMessage: {
				target: this._box
			},
			errorMessage: "{{i18n PLUGINS_EXPLORER_DC_RESOURCE_SAVE_META_ERROR}}"
		});
	},
	
	/**
	 * Callback function called when the DC metadata was edited
	 * @param {Object} response The server response
	 * @param {Object} args The callback arguments
	 * @private
	 */
	_saveCb: function (response, args)
	{
		if (this._cbFn)
		{
			Ext.Function.defer (this._cbFn, 0, this._cbScope, [args.id]);
		}
		
		this._box.hide();
	},
	
	/**
	 * Function invoked when cancelling the dialog box
	 * @private
	 */
	_cancel: function()
	{
		this._box.hide();
	}
});