/////////////////////////////////////////////////////////////////////////////////
//
// c) 2012 24/7 Real Media - A WPP Company 
// This source code is Confidential and Proprietary information of the company.
//
/////////////////////////////////////////////////////////////////////////////////
package com.panache.core
{
	import com.panache.debug.MediaServicesAdDebug;
	import com.panache.utilities.PanacheCreativeCoreUtilities;
	
	import flash.utils.getQualifiedClassName;
	
	/**
	 * PanacheCreativeObject is a core object in the CreativeTemplates toolkit. It implements the core interface:
	 * <ul><li>IPanacheObject</li>
	 * 
	 * <p>
	 * PanacheCreativeObject may be instantiated on its own. However, most likely it will be extended 
	 * by other classes in the toolkit when they need to implement the core interface.
	 * </p>
	 * 
	 * @see com.panache.core.IPanacheObject				IPanacheObject
	 **/
	public class PanacheCreativeObject implements IPanacheObject
	{
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		// CLASS CONSTANTS
		private static const DEBUG_SOURCE:String			= "PanacheCreativeObject";
		
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		// CLASS VARIABLES
		/**
		 * Object which holds all the named properties and status values for an instance of this class.
		 */
		protected var _properties:Object;
		
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		// CONSTRUCTOR
		/**
		 * constructor which creates an instance of this class. 
		 * 
	 	 * <p>
		 * It creates an Object which holds all properties for the instance.  The properties can be set and 
		 * retrieved by name.
	 	 * </p>
		 * 
		 **/
		public function PanacheCreativeObject()
		{
			_properties = new Object();
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		// IPanacheObject INTERFACE
		/**
		 * properties() returns the set of properties that have been defined.
		 * 
		 * @return A value of type <code>Object</code> which the set of properties that have been
		 * defined; <code>null</code> means that no properties have been defined
		 **/
		 public function get properties() : Object
		 {
		 	return _properties;
		 }
		  
		/**
		 * queries the set of properties to see if a particular property exists.  
		 * This method is defined in the IPanacheObject interface.
		 * 
		 * @param propertyName is a String which specifies the name of the property being sought.
		 * 
		 * @return A value of <code>true</code> means that the named property does exist;
		 * <code>false</code> means that the named property does NOT exist.
		 *  
		 * @see com.panache.core.PanacheObject#lookupPropertyValue() 		lookupPropertyValue
		 * 
		 * @example The following code queries an object if a property named displaySize exists.
		 * It is assumed that myObject is an instance of a class that extends PanacheObject.
		 * <listing version="3.0">
		 * if (myObject.hasProperty("displaySize"))
		 * {
		 * 		// do something here
		 * }
		 * </listing> 
		 **/
		public function hasProperty( propertyName:String ) : Boolean
		{
			return (lookupPropertyValue(propertyName) != undefined);
		}

		/**
		 * retrieves the value for a named property, if it exists, and returns the retrieved value after converting it 
		 * to the type specified.
		 * This method is defined in the IPanacheObject interface.
		 * 
		 * @param propertyName is a String which specifies the name of the property being looked up.
		 * 
		 * @param propertyType is a String which specifies the datatype of the value being returned.  The argument
		 * should match one of the AS3 datatypes.  Currently, PanacheObject supports these datatypes (Other classes
		 * that extend PanacheObject may support additional datatypes):
		 * <ul><li>String</li><li>Number</li><li>Boolean</li><li>Object</li><li>~~</li></ul>
		 * 
		 * @param defaultValue is the value that is returned if the property does not exist.
		 * @default <code>null</code>
		 * 
		 * @return Any value other than <code>undefined</code> represents the value that was stored in the property.
		 * <code>undefined</code> means that the named property does NOT exist or that the properties object is null.
		 * 
		 * @see com.panache.core.PanacheObject#lookupPropertyValue() 		lookupPropertyValue
		 * @see com.panache.core.PanacheObject#coerceValueToType()			coerceValueToType
		 * 
		 * @example The following code requests the value of a property named displaySize.
		 * It is assumed that myObject is an instance of a class that extends PanacheObject.
		 * <listing version="3.0">
		 * var displayRect:Rectangle = (myObject.getProperty("displaySize", "Object", null)) as Rectangle;
		 * </listing> 
		 **/
		public function getProperty( propertyName:String, propertyType:String, defaultValue:* = null ) : *
		{
			MediaServicesAdDebug.reportTrace(propertyName + " type=" + propertyType + " default=" + defaultValue, DEBUG_SOURCE, "getProperty", 
											 MediaServicesAdDebug.DEBUG_LEVEL_DETAIL);
			var answer:* = defaultValue;
			
			if (hasProperty(propertyName))
			{
				var value : * = lookupPropertyValue(propertyName);
				answer = coerceValueToType(value, propertyType, defaultValue);	
			}
			
			MediaServicesAdDebug.reportTrace(propertyName + " value=" + answer, DEBUG_SOURCE, "getProperty", MediaServicesAdDebug.DEBUG_LEVEL_DETAIL);
			return answer;
		}

		/**
		 * stores a value in a named property.
		 * This method is defined in the IPanacheObject interface.
		 * 
		 * @param propertyName is a String which specifies the name of the property being looked up.
		 * 
		 * @param value is the value that is being stored in the named property; it can be of any datatype.
		 * 
		 * @example The following code set the value of a property named displaySize to a rectangle.
		 * It is assumed that myObject is an instance of a class that extends PanacheObject.
		 * <listing version="3.0">
		 * var rect:Rectangle = new Rectangle(0, 0,  200, 400);
		 * myObject.setProperty("displaySize", rect);
		 * </listing> 
		 **/
		public function setProperty( propertyName:String, value:*) : void
		{
			MediaServicesAdDebug.reportTrace(propertyName + " value=" + value, DEBUG_SOURCE, "setProperty", MediaServicesAdDebug.DEBUG_LEVEL_DETAIL);
			if (_properties != null)
			{
				_properties[propertyName] = value;
			}	
		}

		/**
		 * queries the set of properties to see if a named status exists.
		 *  A status is by definition a Boolean trait of the object. It is a special case of a property.
		 * This method is defined in the IPanacheObject interface.
		 * 
		 * @param statusName is a String which specifies the name of the status being sought.
		 * 
		 * @return A value of <code>true</code> means that the named status does exist;
		 * <code>false</code> means that the named status does NOT exist.
		 *  
		 * @see com.panache.core.PanacheObject#hasProperty()		hasProperty 		
		 * 
		 * @example The following code queries an object if a statis named isRunning exists.
		 * It is assumed that myObject is an instance of a class that extends PanacheObject.
		 * <listing version="3.0">
		 * if (myObject.hasStatus("isRunning"))
		 * {
		 * 		// do something here
		 * }
		 * </listing> 
		 **/
		public function hasStatus(statusName:String) : Boolean
		{
			return hasProperty(statusName);	
		}
		
		/**
		 * retrieves the value for a named status, if it exists, and returns it as a Boolean value.
		 *  A status is by definition a Boolean trait of the object. It is a special case of a property.
		 * This method is defined in the IPanacheObject interface.
		 * 
		 * @param statusName is a String which specifies the name of the status being sought.
		 * 
		 * @param defaultValue is the value that is returned if the status does not exist.
		 * @default <code>false</code>
		 * 
		 * @return A Boolean value representing the current state of the named status or the default value if 
		 * the status does not exist.
		 * 
		 * @see com.panache.core.PanacheObject#getProperty()		getProperty 
		 * 
		 * @example The following code requests the value of a status named isRunning.
		 * It is assumed that myObject is an instance of a class that extends PanacheObject.
		 * <listing version="3.0">
		 * var running:Boolean = myObject.getProperty("isRunning", false);
		 * </listing> 
		 **/
		public function getStatus(statusName:String, defaultValue:Boolean = false) : Boolean
		{
			return getProperty(statusName, "Boolean", defaultValue);	
		}
		
		/**
		 * sets the Boolean state of a named status.
		 *  A status is by definition a Boolean trait of the object. It is a special case of a property.
		 * This method is defined in the IPanacheObject interface.
		 * 
		 * @param statusName is a String which specifies the name of the status being set.
		 * 
		 * @param state is the Boolean value that is being stored in the named status.
		 * 
		 * @see com.panache.core.PanacheObject#setProperty()			setProperty 
		 * 
		 * @example The following code set the value of a status named isRunning to true.
		 * It is assumed that myObject is an instance of a class that extends PanacheObject.
		 * <listing version="3.0">
		 * myObject.setStatus("isRunning", true);
		 * </listing> 
		 **/
		public function setStatus(statusName:String, state:Boolean) : void
		{
			setProperty(statusName, state);
		}

		/**
		 * configures the object by setting properties on the object based on the key/value fields
		 * in the config object.
		 * 
		 * @param config is an Object which specifies the properties to be added to the object.
		 * Each key in the object represents a property name and the value associated with 
		 * that key becomes the value of the associated property.
		 * 
		 * @see com.panache.core.PanacheObject#setProperty()			setProperty 
		 * 
		 * @example The following code will add the color and enabled property to the object.
		 * It is assumed that myObject is an instance of a class that extends PanacheObject.
		 * <listing version="3.0">
		 * var config:Object = new Object();
		 * config["color"] = 0xFF00CC;
		 * config["enabled"] = true;
		 * myObject.configureFromObject(config);
		 * </listing> 
		 **/
		public function configureFromObject(config:Object) : void
		{
			if (config != null)
			{
				for (var key:String in config)
				{
					this.setProperty(key, config[key]);
				}	
			}	
		}		
		
		/**
		 * creates a display string representing the object.
		 * This method is defined in the IPanacheObject interface.
		 * 
		 * <p>
		 * It is the responsibility of classes which extend PanacheObject to override this
		 * method to create a String that better describes that particular object.
		 * </p>
		 * 
		 * @return A string which describes the object.
		 * 
		 * @example The following code requests a string description of the object to be used
		 * in a trace.
		 * It is assumed that myObject is an instance of a class that extends PanacheObject.
		 * <listing version="3.0">
		 * trace(myObject.toString());
		 * </listing> 
		 */
		 public function toString() : String
		 {
		 	return getQualifiedClassName(this);
		 }
		 
		/**
		 * instructs the object to clean-up and dispose any references that the object is
		 * holding onto.
		 * This method is defined in the IPanacheObject interface.
		 * 
		 * <p>
		 * It is the responsibility of classes which extend PanacheObject to override this
		 * method to performs its own clean-up.
		 * </p>
		 * 
		 * @example The following code instructs the object to do its clean-up.
		 * It is assumed that myObject is an instance of a class that extends PanacheObject.
		 * <listing version="3.0">
		 * myObject.dispose();
		 * </listing> 
		 **/ 
		public function dispose() : void
		{
			MediaServicesAdDebug.reportTrace("Should be implemented by the subclass", DEBUG_SOURCE, "dispose", MediaServicesAdDebug.DEBUG_LEVEL_DETAIL);
		}

		/**
		 **/ 
		public function clone() : IPanacheObject
		{
			var obj:PanacheCreativeObject = new PanacheCreativeObject();
			obj.configureFromObject(_properties);
			return obj;
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		// IPanacheObject support
		/**
		 * basic property lookup function used to retrieve a 
		 * specific property value out of the properties object.
		 * 
	 	 * <p>
		 * This is a protected function and, as such, cannot be called from outside the instance.
		 * However, it can be overriden in a class that extends PanacheObject.
	 	 * </p>
		 * 
		 * @param propertyName is a String which specifies the name of the property being looked up.
		 * 
		 * @return Any value other than <code>undefined</code> represents the value that was stored in the property.
		 * <code>undefined</code> means that the named property does NOT exist or that the properties object is null. 
		 **/
		protected function lookupPropertyValue(propertyName:String) : *
		{
			if (_properties != null)
				return _properties[propertyName];
			else
				return undefined;
		}
		
		/**
		 * converts an untyped value into a specific data type.
		 * 
	 	 * <p>
		 * This is a protected function and, as such, cannot be called from the outside.
		 * However, it can be overriden in a class that extends PanacheObject.
	 	 * </p>
		 * 
		 * @param value is the raw value that is being converted to the specified datatype.  Typically this 
		 * is a value of one of the properties but subclasses may use this function to do their conversion.
		 * 
		 * @param type is a String which specifies the datatype of the value being returned.  The argument
		 * should match one of the AS3 datatypes.  
		 * Currently, PanacheObject supports the datatypes supported by the coerceValueToType function in the
		 * core utility class PanacheCoreUtilities.
		 * Other classes that extend PanacheObject may support additional datatypes.
		 * 
		 * @param defaultValue is a value that is returned if the value cannot be converted to the specified 
		 * datatype.
		 * @default <code>null</code>
		 * 
		 * @return The coerced value if the conversion was successful; otherwise the default value is returned.
		 * 
		 * @see com.panache.utilities.PanacheCoreUtilities#coerceValueToType()		PanacheCoreUtilities: coerceValueToType
		 **/
		protected function coerceValueToType(value:*, type:String, defaultValue:* = null) : *
		{
			return PanacheCreativeCoreUtilities.coerceValueToType(value, type, defaultValue);
		}
	}
}