/*
 *  Copyright 2012 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 class is the main class to register and retrieve widgets for edition.
 * It associates types to widgets.
 * See constants for the lists of tabs.
 */
Ext.define('Ametys.form.WidgetManager', {
	singleton: true,
	
	/**
	 * @property {String} TYPE_STRING The type 'string'. A widget of such type should get/set values using the js type "string"
	 * @readonly 
	 */
	TYPE_STRING: 'string',
    /**
     * @property {String} TYPE_MULTILINGUAL_STRING The type 'multilingual-string'. A widget of such type should get/set values using the js type "object" with a entry for each language with non-empty value
     * @readonly 
     */
    TYPE_MULTILINGUAL_STRING: 'multilingual-string',
    /**
     * @property {String} TYPE_PASSWORD The type 'password'. A widget of such type should get/set values using the js type "string". Note that the current real value should not be set and that getValue should return null to specify an unchanged password (while "" means a new empty password).
     * @readonly 
     */
    TYPE_PASSWORD: 'password',
	/**
	 * @property {String} TYPE_GEOCODE The type 'geocode'. A widget of such type should get/set values using the js type "object" with the following properties:
     * 
     * * {Number} latitude The latitude value of the geocode
     * * {Number} longitude The longitude value of the geocode
     * 
	 * @readonly 
	 */
	TYPE_GEOCODE: 'geocode',
	/**
	 * @property {String} TYPE_DATE The type 'date'. A widget of such type should get/set values using the js type "date"
	 * @readonly 
	 */
	TYPE_DATE: 'date',
	/**
	 * @property {String} TYPE_DATETIME The type 'datetime'. A widget of such type should get/set values using the js type "date"
	 * @readonly 
	 */
	TYPE_DATETIME: 'datetime',
	/**
	 * @property {String} TYPE_LONG The type 'long'. A widget of such type should get/set values using the js type "number"
	 * @readonly 
	 */
	TYPE_LONG: 'long',	
	/**
	 * @property {String} TYPE_DOUBLE The type 'double'. A widget of such type should get/set values using the js type "number"
	 * @readonly 
	 */
	TYPE_DOUBLE: 'double',	
	/**
	 * @property {String} TYPE_BOOLEAN The type 'boolean'. A widget of such type should get/set values using the js type "boolean"
	 * @readonly 
	 */
	TYPE_BOOLEAN: 'boolean',
	/**
	 * @property {String} TYPE_RICH_TEXT The type 'rich-text'. A widget of such type should get/set values using the js type "string" representing HTML code. See default implementation for details on how to store local files (images...) and awaited format for all tags.
	 * @readonly 
	 */
	TYPE_RICH_TEXT: 'rich-text',
	/**
	 * @property {String} TYPE_FILE The type 'file'. A widget of such type should get/set values using the js type "object" with the following properties (if no file is selected the value should be {}):
     * 
     * * {String} type The kind of file on the server side. Such as "metadata" for a local file.
     * * {String} [path] The path of the file (related to its type). Such as "attachments/1/attachment".
     * * {String} filename The file name. Such as "image.png".
     * * {String} [mimeType] The file metype. Such as "image/png".
     * * {String} size The size in byte of the file
     * * {String} [lastModified] The date of the last modification using the full ISO8601 format Ext.date#patterns.ISO8601DateTime
     * * {String} id The internal identifier of the new file or "untouched" for an existing file. For example, a just uploaded file will have the temporary value assigned by the server in the upload directory ; whereas for a jcr resource it will be the jcr UUID.
     * * {String} downloadUrl The absolute url to download the file
     * * {String} viewUrl The absolute url to preview the file
     *  
	 * @readonly 
	 */
	TYPE_FILE: 'file',
	/**
	 * @property {String} TYPE_USER The type 'user'. A widget of such type should get/set values using the js type "object" with the following properties:
     * 
     *  * {String} login The user's login
     *  * {String} populationId The user's population
     *  * {String} [populationLabel] The user's population label
     *  * {String} [fullname] The user's full name
     *  * {String} [sortablename] The user's sortable name
	 * @readonly 
	 */
	TYPE_USER: 'user',
    /**
     * @property {String} TYPE_REFERENCE The type 'reference'. A widget of such type should get/set values using the js type "object" with the following properties:
     * 
     * * {String} value The value of the reference. Can be an url or an identifier depending on the type.
     * * {String} type The type of reference.
     * 
     * @readonly 
     */
    TYPE_REFERENCE: 'reference',
    /**
     * @property {String} TYPE_URL The type 'url'. A widget of such type should get/set values using the js type "string" representing the url. 
     * @readonly 
     */
    TYPE_URL: 'url',
	
    /**
     * @property {String} TYPE_CONTENT The type 'content'. A widget of such type should get/set values using the js type "string" representing the content identifier
     * Additionnaly to avoid useless request the setValue *MUST* support the following object
     * 
     * * {String} id The content identifier
     * * {String} title The localized title
     * * {String} iconGlyph The glyph name to render the content
     * * {String} iconDecorator The decorator to add to the glyph
     * * {String} smallIcon The small sized icon to render the content
     * * {String} mediumIcon The medium sized icon to render the content
     * * {String} largeIcon The large sized icon to render the content
     * @readonly 
     */
    TYPE_CONTENT: 'content',
    /**
     * @property {String} TYPE_SUB_CONTENT The type 'sub_content'. A widget of such type should get/set values using the js type "string" representing the content identifier
     * @readonly 
     */
    TYPE_SUB_CONTENT: 'sub_content',
    
    
	/**
	 * @property {Object} _widgets The registered widgets classified
	 * @private
	 */
	_widgets: {
        'enumeration': {
    		'single': {
            },
    		'multiple': {
            }
        },
        'non-enumeration': {
            'single': {
            },
            'multiple': {
            }
        }
	},

	/**
	 * @property {Object} _defaultWidgets The default widgets classified
	 * @readonly
	 * @private
	 */
	_defaultWidgets : {
        'enumeration': {
            'single': {
            },
            'multiple': {
            }
        },
        'non-enumeration': {
            'single': {
            },
            'multiple': {
            }
        }
	},
	
	/**
	 * Register a widget on the manager.
	 * @param {String} xtype (required) The widget xtype. Cannot be null.
	 * @param {String} type (required) The type of widget such as Ametys.form.WidgetManager.TYPE_STRING, Ametys.form.WidgetManager.TYPE_DATE, ... Can not be null.
	 * @param {Boolean} isEnumeration True if the widget is registered for enumeration
	 * @param {Boolean} handleMultiple True if the widget handles multiple values
	 */
	register: function(xtype, type, isEnumeration, handleMultiple) 
	{
        var widgets = this._widgets[isEnumeration ? 'enumeration' : 'non-enumeration'][handleMultiple ? 'multiple' : 'single'];
        
		if (!widgets[type])
		{
			widgets[type] = type == '*' ? [] : Ext.clone(widgets['*'] || []);
		}

		this.getLogger().debug("Adding widget '" + xtype + (isEnumeration ? "' for enumeration" : "'") + " for type '" + type + "'");
		
		widgets[type].push(xtype);
		if (type == '*')
		{
			// loop on all type to add xtype
			Ext.Object.each(widgets, function(key, value) {
				if (key != '*')
				{
					widgets[key].push(xtype);
				}
			})
		}
	},
	
	/**
	 * Creates and returns the registered widget by its xtype and its type
	 * @param {String} xtype The widget xtype
	 * @param {String} type (required) The type of widget such as Ametys.form.WidgetManager.TYPE_STRING, Ametys.form.WidgetManager.TYPE_DATE, ...
	 * @param {Object} config The object configuration to be passed to the widget constructor
	 * @returns {Ext.form.Field} The created field
	 */
	getWidget: function (xtype, type, config)
	{
		var xtype = this.getWidgetXType (xtype, type, config.enumeration !== undefined, config.multiple || false);
		return Ext.ComponentManager.create(Ext.apply (config, {xtype: xtype}));
	},
	
	/**
	 * Get the default widget xtype by type, enumerated and multiple properties
	 * @return {String} The default xtype
	 * @private
	 */
	_getDefaultWidgetXType: function (type, isEnumeration, isMultiple)
	{
        var defaultWidgets = this._defaultWidgets[isEnumeration ? 'enumeration' : 'non-enumeration'][isMultiple ? 'multiple' : 'single'];
		
		if (!defaultWidgets[type])
		{
            var widgets = this._widgets[isEnumeration ? 'enumeration' : 'non-enumeration'][isMultiple ? 'multiple' : 'single'];
			
			if (!widgets[type] || widgets[type].length == 0)
			{
				this.getLogger().warn("There is no default widget nor registered widget for type '" + type + "' (" + (isEnumeration ? 'enumerated': 'non enumerated') + "/" + (isMultiple ? 'multiple': 'single') + "), default widget for type Ametys.form.WidgetManager#TYPE_STRING will be used");
				return defaultWidgets[this.self.TYPE_STRING];
			}
			else
			{
				this.getLogger().warn("There is no default widget for type '" + type + "' (" + (isEnumeration ? 'enumerated': 'non enumerated') + "/" + (isMultiple ? 'multiple': 'single') + "), first widget of this type will be used.");
				return widgets[type][0];
			}
		}
		else
		{
			return defaultWidgets[type];
		}
	},
	
	/**
	 * Get the registered widget xtype by its asked xtype, type, enumerated and multiple properties
	 * @param {String} xtype The asked xtype. Can be null to use the default widget
	 * @param {String} type (required) The type of widget such as Ametys.form.WidgetManager.TYPE_STRING, Ametys.form.WidgetManager.TYPE_DATE, ...
	 * @param {Boolean} isEnumeration True if the handled field is a enumeration
	 * @param {Boolean} isMultiple True if the handled field has multiple values
	 * @returns {String} The matching widget xtype among registered xtypes
	 */
	getWidgetXType: function (xtype, type, isEnumeration, isMultiple)
	{
        var widgets = this._widgets[isEnumeration ? 'enumeration' : 'non-enumeration'][isMultiple ? 'multiple' : 'single'];

		if (!widgets[type])
		{
			this.getLogger().error("Unknown type '" + type + "' for widgets (" + (isEnumeration ? 'enumerated': 'non enumerated') + "/" + (isMultiple ? 'multiple': 'single') + "), Ametys.form.WidgetManager#TYPE_STRING will be used");
			type = Ametys.form.WidgetManager.TYPE_STRING;
		}
		
		if (xtype == null)
		{
			// Use default widget for this type
			return this._getDefaultWidgetXType (type, isEnumeration, isMultiple);
		}
		else
		{
			if (Ext.Array.contains(widgets[type], xtype))
			{
				return xtype;
			}
			else
			{
				this.getLogger().error("There is no registered widget of xtype '" + xtype + "' for type '" + type + "' (" + (isEnumeration ? 'enumerated': 'non enumerated') + "/" + (isMultiple ? 'multiple': 'single') + "). Default widget will be used.");
				return this._getDefaultWidgetXType (type, isEnumeration, isMultiple);
			}
		}
	},
	
    
    /**
     * Has a registered widget xtype by its asked xtype, type, enumerated and multiple properties
     * @param {String} xtype The asked xtype. Can be null to use the default widget
     * @param {String} type (required) The type of widget such as Ametys.form.WidgetManager.TYPE_STRING, Ametys.form.WidgetManager.TYPE_DATE, ...
     * @param {Boolean} isEnumeration True if the handled field is a enumeration
     * @param {Boolean} isMultiple True if the handled field has multiple values
     * @returns {String} The matching widget xtype among registered xtypes
     */
    hasWidgetXType: function (xtype, type, isEnumeration, isMultiple)
    {
        var widgets = this._widgets[isEnumeration ? 'enumeration' : 'non-enumeration'][isMultiple ? 'multiple' : 'single'];

        if (!widgets[type])
        {
            return false;
        }
        
        if (xtype == null)
        {
            return false
        }
        else
        {
            if (Ext.Array.contains(widgets[type], xtype))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    },
    
    /**
     * Retrieves the maxHeight of a widget according to its configuration
     * Allow to put a default maxHeight for widgets in repeaters in mode table
     * @param {Object} widgetConfig The widget's configuration
     * @return {Number} The widget maxHeight, or undefined if any maxHeight has been computed
     */
    getWidgetMaxHeight: function (widgetConfig)
    {
        return widgetConfig.maxHeight
                ? widgetConfig.maxHeight
                : widgetConfig.repeaterMode && widgetConfig.repeaterMode == 'table' && !widgetConfig.multiple && !widgetConfig.resizable 
                    ? Ametys.form.ConfigurableFormPanel.Repeater.SIMPLE_FIELD_MAX_HEIGHT
                    : undefined;
    }
});