////////////////////////////////////////////////////////////////////////////////
//
//  ADOBE SYSTEMS INCORPORATED
//  Copyright 2009 Adobe Systems Incorporated
//  All Rights Reserved.
//
//  NOTICE: Adobe permits you to use, modify, and distribute this file
//  in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////

package mx.styles
{
    
import flash.display.DisplayObject;
import flash.events.EventDispatcher;
import flash.utils.Dictionary;

import mx.core.Singleton;
import mx.core.mx_internal;
import mx.events.FlexChangeEvent;
import mx.managers.ISystemManager;
import mx.managers.SystemManagerGlobals;
import mx.utils.ObjectUtil;
import flash.events.Event;

use namespace mx_internal;

[ExcludeClass]

/**
 *  The CSSStyleDeclaration class represents a set of CSS style rules.
 *  The MXML compiler automatically generates one CSSStyleDeclaration object
 *  for each selector in the CSS files associated with a Flex application.
 *  
 *  <p>A CSS rule such as
 *  <pre>
 *      Button { color: #FF0000 }
 *  </pre>
 *  affects every instance of the Button class;
 *  a selector like <code>Button</code> is called a type selector
 *  and must not start with a dot.</p>
 *
 *  <p>A CSS rule such as
 *  <pre>
 *      .redButton { color: #FF0000 }
 *  </pre>
 *  affects only components whose <code>styleName</code> property
 *  is set to <code>"redButton"</code>;
 *  a selector like <code>.redButton</code> is called a class selector
 *  and must start with a dot.</p>
 *
 *  <p>You can access the autogenerated CSSStyleDeclaration objects
 *  using the <code>StyleManager.getStyleDeclaration()</code> method,
 *  passing it either a type selector
 *  <pre>
 *  var buttonDeclaration:CSSStyleDeclaration =
 *      StyleManager.getStyleDeclaration("Button");
 *  </pre>
 *  or a class selector
 *  <pre>
 *  var redButtonStyleDeclaration:CSSStyleDeclaration =
 *      StyleManager.getStyleDeclaration(".redButton");
 *  </pre>
 *  </p>
 *
 *  <p>You can use the <code>getStyle()</code>, <code>setStyle()</code>,
 *  and <code>clearStyle()</code> methods to get, set, and clear 
 *  style properties on a CSSStyleDeclaration.</p>
 *
 *  <p>You can also create and install a CSSStyleDeclaration at run time
 *  using the <code>StyleManager.setStyleDeclaration()</code> method:
 *  <pre>
 *  var newStyleDeclaration:CSSStyleDeclaration = new CSSStyleDeclaration(".bigMargins");
 *  newStyleDeclaration.defaultFactory = function():void
 *  {
 *      leftMargin = 50;
 *      rightMargin = 50;
 *  }
 *  StyleManager.setStyleDeclaration(".bigMargins", newStyleDeclaration, true);
 *  </pre>
 *  </p>
 *
 *  @see mx.core.UIComponent
 *  @see mx.styles.StyleManager
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
public class CSSMergedStyleDeclaration extends CSSStyleDeclaration
{
    include "../core/Version.as";
    

    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------
    
    /**
     *  @private
     * 
     *  A CSSStyleDeclaration that is a merger between two styles. The styles are for the same
     *  selector. One style is from the local style manager and the other style is from the style manager's
     *  parent.
     * 
     *  @param style - A style from the local style manager. May be null.
     * 
     *  @param parentStyle - The style in the parent style manager for the same selector. May be null.
     *
     *  @param selector - If the selector is a CSSSelector then advanced
     *  CSS selectors are supported. If a String is used for the selector then
     *  only simple CSS selectors are supported. If the String starts with a
     *  dot it is interpreted as a universal class selector, otherwise it must
     *  represent a simple type selector. If not null, this CSSStyleDeclaration
     *  will be registered with StyleManager. 
     *  
     *  @param styleManager - The style manager to set this declaration into. If the
     *  styleManager is null the top-level style manager will be used.
     * 
     *  @param setSelector - If true set the selector in the styleManager. If setSelector
     *  is false this style declaration can be set in the styleManager at a later time
     *  by calling <code>styleManager.setStyleDeclaration(styleDeclaration.selectorString, styleDeclaration, false);
     *  </code>
     *
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 4
     * 
     */ 
    public function CSSMergedStyleDeclaration(style:CSSStyleDeclaration, parentStyle:CSSStyleDeclaration,
                        selector:Object=null, styleManager:IStyleManager2=null, setSelector:Boolean = false)
    {
        super(selector, styleManager, setSelector);
        
        this.style = style;
        this.parentStyle = parentStyle;

        var i:uint;
        var n:uint;
        var effectsArray:Array;
        
        // combine effects child and parent effects array
        if (style && style.effects)
        {
            effects = [];
            
            effectsArray = style.effects;
            n = effectsArray.length;
            for (i = 0; i < n; i++)
                effects[i] = effectsArray[i];
        }
        
        if (parentStyle && parentStyle.effects)
        {
            if (!effects)
                effects = [];

            effectsArray = parentStyle.effects;
            n = effectsArray.length;
            for (i = 0; i < n; i++)
            {
                effects[i] = effectsArray[i];
                if (effects.indexOf(effectsArray[i]) == -1)
                    effects[i] = effectsArray[i];
            }
        }
        
        updateOverrides = true;
    }

    //--------------------------------------------------------------------------
    //
    //  Variables
    //
    //--------------------------------------------------------------------------
    
    /**
     *  @private
     * 
     *  Local storage for the style in the local style manager.
     */
    private var style:CSSStyleDeclaration;
    
    /**
     *  @private
     * 
     *  Local storage for the style in the parent style manager.
     */
    private var parentStyle:CSSStyleDeclaration;

    /**
     *  @private
     * 
     *  If true then update the overrides array from the style and
     *  parentStyle.
     */
    private var updateOverrides:Boolean;
    
    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------
    
    //----------------------------------
    //  defaultFactory
    //----------------------------------
    
    /**
     *  @private
     */
    private var _defaultFactory:Function;
    
    
    [Inspectable(environment="none")]
    
    /**
     *  @private
     */
    override public function get defaultFactory():Function
    {
        if (_defaultFactory != null)
            return _defaultFactory;
        
        if ((style != null && style.defaultFactory != null) || 
           (parentStyle != null && parentStyle.defaultFactory != null))
        {
            _defaultFactory = function():void
            {
                if (parentStyle && parentStyle.defaultFactory != null)
                    parentStyle.defaultFactory.apply(this);
                
                if (style && style.defaultFactory != null)
                    style.defaultFactory.apply(this);
            };            
        }
        
        return _defaultFactory;
    }
    
    /**
     *  @private
     */
    override public function set defaultFactory(f:Function):void
    {
        // not supported
    }
    
    //----------------------------------
    //  factory
    //----------------------------------
    
    /**
     *  @private
     */
    private var _factory:Function;
    
    [Inspectable(environment="none")]
    
    /**
     *  @private
     */
    override public function get factory():Function
    {
        if (_factory != null)
            return _factory;
        
        if ((style != null && style.factory != null) || 
            (parentStyle != null && parentStyle.factory != null))
        {
            _factory = function():void
            {
                if (parentStyle && parentStyle.factory != null)
                    parentStyle.factory.apply(this);
                
                if (style && style.factory != null)
                    style.factory.apply(this);
            };            
        }     
        
        return _factory;
    }

    /**
     *  @private
     */
    override public function set factory(f:Function):void
    {
        // not supported
    }
    
    /**
     *  @private
     */
    override public function get overrides():Object
    {
        if (!updateOverrides)
            return super.overrides;
        
        var obj:Object;
        var mergedOverrides:Object = null;
        
        if (style && style.overrides)
        {
            mergedOverrides = [];
            
            var childOverrides:Object = style.overrides;
            for (obj in childOverrides)
                mergedOverrides[obj] = childOverrides[obj];            
        }
        
        if (parentStyle && parentStyle.overrides)
        {
            if (!mergedOverrides)
                mergedOverrides = [];
                
            var parentOverrides:Object = parentStyle.overrides;
            for (obj in parentOverrides)
            {
                if (mergedOverrides[obj] === undefined)
                    mergedOverrides[obj] = parentOverrides[obj];
            }
        }
        
        super.overrides = mergedOverrides;        
        updateOverrides = false;
        
        return mergedOverrides;
    }
    
    /**
     *  @private
     */
    override public function set overrides(o:Object):void
    {
        // not supported
    }
 
    /**
     *  @private
     */
    override public function setStyle(styleProp:String, newValue:*):void
    {
        // not supported
    }
 
    /**
     *  @private
     */
    override mx_internal function addStyleToProtoChain(chain:Object,
                                              target:DisplayObject,
                                              filterMap:Object = null):Object
    {
        // If we have a local style, then add only it to the chain. It will
        // take are of adding its parent to the chain.
        // If then is no style, but a parentStyle, then add the parent Style
        // to the chain.
        if (style)
            return style.addStyleToProtoChain(chain, target, filterMap);
        else if (parentStyle)
            return parentStyle.addStyleToProtoChain(chain, target, filterMap);
        else
            return chain;
    }    
    
}
}