Controller.Action.FadeOverlay = Class.create();
Object.extend(Object.extend(Controller.Action.FadeOverlay.prototype, Controller.Action.Base.prototype), {

	initialize: function(elem) {
		this.base_element = $(elem);
		this.base_element.makePositioned();
		this.base_element.cleanWhitespace();
		// create inner element (element_two) for placing the optional second image (of a pair)
		// a pair consists of: [{ src: 'images/a.png', type: 'a' }, { src: 'images/b.png', type: 'b' }]
		// where [0] is mapped to base_element and [1] to element_two
		// the type is mapped to a pair of position co-ordinates; see positionmap
		this.elements = [this.base_element];
		// create the overlay image - for the fade effect						
		if(arguments[3]) {
			var layer_id = arguments[3];
		} else if(this.base_element['id']) {
			var layer_id = this.base_element['id'] + '_layer';
		} else {
			var layer_id = '__fade_layer__';
		}
		this.layer_element = $(layer_id);
		if(this.layer_element == undefined) {
			var dim = Element.getDimensions(this.base_element);
			var layer = this.createInnerElement(layer_id, arguments[2] || 'div', dim);
			layer.style.zIndex = 1000;				
			this.layer_element = this.base_element.appendChild(layer);	
		}												
		this.setOptions(arguments[1] || {});
		this.addMapping('default', 0, 0);
		this.event('initialized');
	},
	
	setOptions: function(options) {		
		this.options = Object.extend({
			direction: 'n',
			duration: 1.0,
			delay: 0.0,
			transition: Effect.Transitions.sinoidal,
			overflow: false
		}, options || {});
		if(!this.options.overflow) this.base_element.style.overflow = 'hidden';
		if(!this.options.overflow) this.layer_element.style.overflow = 'hidden';		
		this.effect_options = this._prepareEffectOptions();
	},
	
	addMapping: function(type, x, y) {
		if(!this.positionmap) this.positionmap = { };
		this.positionmap[type] = [x, y];
	},
	
	addLayers: function() {
		var self = this;
		var dim = Element.getDimensions(self.base_element);
		$A(arguments).each(function(inner_id, index) {
			var inner_element = self.createInnerElement(inner_id, self.base_element.tagName, dim);
			inner_element.style.zIndex = index;
			self.elements.push(self.base_element.appendChild(inner_element));
		});
	},
	
	createInnerElement: function(id, tag, dimensions) {
		var inner_element = $(document.createElement(this.base_element.tagName));
		inner_element.id = id;
		var repeat = this.base_element.backgroundRepeat || 'no-repeat';
		inner_element.setStyle({ left: 0, top: 0, position: 'absolute', backgroundRepeat: repeat, width: dimensions.width + 'px', height: dimensions.height + 'px' });					
		return inner_element;
	},
	
	getPosition: function(type) {
		if(this.positionmap && this.positionmap[type]) {
			return [ this.positionmap[type][0], this.positionmap[type][1] ];
		} else {	
			return this.positionmap['default'];
		}
	},
	
	getElement: function(element_index) {
		return this.elements[element_index] || this.elements[0];
	},
	
	setImage: function(element_index, value) {
		if(!Var.is_string(value.src) || value.src.blank()) { return this.clearImage(element_index); }
		Element.Style.setBackgroundImage(this.getElement(element_index), value.src, { left: value.position[0], top: value.position[1] });		
	},
	
	clearImage: function(element_index) {
		Element.Style.clearBackgroundImage(this.getElement(element_index));
	},
	
	switchImages: function(sources) {
		var self = this;
		sources.each(function(image) {
			image.clear ? self.clearImage(image.idx) : self.setImage(image.idx, image);
		});
	},
		
	update: function(index, images) {		
		var preloader = new Preload.ImagesSync(images, {
			error: this.event.bind(this, 'loadError'),	
			completed: this.ready.bind(this, images, arguments[2])
		});
	},

	ready: function(sources) {
		// prepare options
		var self = this;
		options = this._prepareEffectOptions();		
		if(arguments[1] == true) { // skip in-transition
			// assign new sources
			self.switchImages(sources);						
			// reveal new image(s)
			self.reveal(options);
		} else {
			this.cover(Object.extend(Var.clone(options), { afterFinish: function(effect) {
				// assign new sources
				self.switchImages(sources);						
				// reveal new image(s)					
				self.reveal(options);						
			} }));
		}
	},
	
	cover: function() {
		this.cover_effect = new Effect.SlideBackgroundImage(this.layer_element, Object.extend(arguments[0] || {}, {
			fade: 'in', delay: 0.0,
			afterUpdate: function(effect) {
				effect.element.setOpacity(effect.position);
			}
		}));
	},
	
	reveal: function() {
		var self = this;
		this.reveal_effect = new Effect.SlideBackgroundImage(this.layer_element, Object.extend(arguments[0] || {}, { 
			fade: 'out',
			afterUpdate: function(effect) {
				if(effect.position > 0.25) effect.element.setOpacity(1-effect.position);
			},				
			afterFinish: function(effect) {
				// call controller to iterate next
				self.controller.ready();
			}
		}));
	},
	
	_prepareEffectOptions: function() {		
		// use a clone for options
		var options = Var.clone(this.options);
	
		// two-step effect: duration in half
		options.duration = (options.duration / 2);
	
		// use play direction
		if(Var.is_array(this.options.direction)) {
			options.direction = this.controller.playdir == 'bw' ? this.options.direction[1] : this.options.direction[0];
		}
	
		// don't pass eventhandlers
		options.beforeStart = null;			
		options.beforeSetup = null;
		options.afterSetup = null;
		options.beforeUpdate = null;
		options.afterUpdate = null;
		options.beforeFinish = null;	
		options.afterFinish = null;
	
		return options;
	}

});