define(["dojo/_base/array","dojo/_base/lang", "dojo/_base/fx", "dojo/fx", "dojo/dom", "dojo/dom-style",
	    "dojo/dom-geometry", "dojo/_base/connect", "dojo/_base/html"],
	function(arrayUtil, lang, baseFx, coreFx, dom, domStyle, domGeom, connectUtil, htmlUtil){

/*=====
return {
	// summary:
	//		Experimental and extended Animations beyond Dojo Core / Base functionality.
	//		Provides advanced Lines, Animations, and convenience aliases.
};
=====*/

var dojoxFx = lang.getObject("dojox.fx", true);

lang.mixin(dojoxFx, {

	// anim: Function
	//		Alias of `dojo.anim` - the shorthand `dojo.animateProperty` with auto-play
	anim: baseFx.anim,

	// animateProperty: Function
	//		Alias of `dojo.animateProperty` - animate any CSS property
	animateProperty: baseFx.animateProperty,

	// fadeTo: Function
	//		Fade an element from an opacity to an opacity.
	//		Omit `start:` property to detect. `end:` property is required.
	//		Ultimately an alias to `dojo._fade`
	fadeTo: baseFx._fade,

	// fadeIn: Function
	//		Alias of `dojo.fadeIn` - Fade a node in.
	fadeIn: baseFx.fadeIn,
	
	// fadeOut: Function
	//		Alias of `dojo.fadeOut` - Fades a node out.
	fadeOut: baseFx.fadeOut,

	// combine: Function
	//		Alias of `dojo.fx.combine` - Run an array of animations in parallel
	combine: coreFx.combine,

	// chain: Function
	//		Alias of `dojo.fx.chain` - Run an array of animations in sequence
	chain: coreFx.chain,

	// slideTo: Function
	//		Alias of `dojo.fx.slideTo` - Slide a node to a defined top/left coordinate
	slideTo: coreFx.slideTo,

	// wipeIn: Function
	//		Alias of `dojo.fx.wipeIn` - Wipe a node to visible
	wipeIn: coreFx.wipeIn,

	// wipeOut: Function
	//		Alias of `dojo.fx.wipeOut` - Wipe a node to non-visible
	wipeOut: coreFx.wipeOut
});


dojoxFx.sizeTo = function(/* Object */args){
	// summary:
	//		Creates an animation that will size a node
	//
	// description:
	//		Returns an animation that will size the target node
	//		defined in args Object about it's center to
	//		a width and height defined by (args.width, args.height),
	//		supporting an optional method: chain||combine mixin
	//		(defaults to chain).
	//
	//	- works best on absolutely or relatively positioned elements
	//
	// example:
	//	|	// size #myNode to 400px x 200px over 1 second
	//	|	dojo.fx.sizeTo({
	//	|		node:'myNode',
	//	|		duration: 1000,
	//	|		width: 400,
	//	|		height: 200,
	//	|		method: "combine"
	//	|	}).play();
	//

	var node = args.node = dom.byId(args.node),
		abs = "absolute";

	var method = args.method || "chain";
	if(!args.duration){ args.duration = 500; } // default duration needed
	if(method == "chain"){ args.duration = Math.floor(args.duration / 2); }
	
	var top, newTop, left, newLeft, width, height = null;

	var init = (function(n){
		return function(){
			var cs = domStyle.getComputedStyle(n),
				pos = cs.position,
				w = cs.width,
				h = cs.height
			;
			
			top = (pos == abs ? n.offsetTop : parseInt(cs.top) || 0);
			left = (pos == abs ? n.offsetLeft : parseInt(cs.left) || 0);
			width = (w == "auto" ? 0 : parseInt(w));
			height = (h == "auto" ? 0 : parseInt(h));
			
			newLeft = left - Math.floor((args.width - width) / 2);
			newTop = top - Math.floor((args.height - height) / 2);

			if(pos != abs && pos != 'relative'){
				var ret = domStyle.coords(n, true);
				top = ret.y;
				left = ret.x;
				n.style.position = abs;
				n.style.top = top + "px";
				n.style.left = left + "px";
			}
		}
	})(node);

	var anim1 = baseFx.animateProperty(lang.mixin({
		properties: {
			height: function(){
				init();
				return { end: args.height || 0, start: height };
			},
			top: function(){
				return { start: top, end: newTop };
			}
		}
	}, args));
	var anim2 = baseFx.animateProperty(lang.mixin({
		properties: {
			width: function(){
				return { start: width, end: args.width || 0 }
			},
			left: function(){
				return { start: left, end: newLeft }
			}
		}
	}, args));

	var anim = coreFx[(args.method == "combine" ? "combine" : "chain")]([anim1, anim2]);
	return anim; // dojo.Animation

};

dojoxFx.slideBy = function(/* Object */args){
	// summary:
	//		Returns an animation to slide a node by a defined offset.
	//
	// description:
	//		Returns an animation that will slide a node (args.node) from it's
	//		current position to it's current posision plus the numbers defined
	//		in args.top and args.left. standard dojo.fx mixin's apply.
	//
	// example:
	//	|	// slide domNode 50px down, and 22px left
	//	|	dojox.fx.slideBy({
	//	|		node: domNode, duration:400,
	//	|		top: 50, left: -22
	//	|	}).play();

	var node = args.node = dom.byId(args.node),
		top, left;

	var init = (function(n){
		return function(){
			var cs = domStyle.getComputedStyle(n);
			var pos = cs.position;
			top = (pos == 'absolute' ? n.offsetTop : parseInt(cs.top) || 0);
			left = (pos == 'absolute' ? n.offsetLeft : parseInt(cs.left) || 0);
			if(pos != 'absolute' && pos != 'relative'){
				var ret = domGeom.coords(n, true);
				top = ret.y;
				left = ret.x;
				n.style.position = "absolute";
				n.style.top = top + "px";
				n.style.left = left + "px";
			}
		}
	})(node);
	init();
	
	var _anim = baseFx.animateProperty(lang.mixin({
		properties: {
			// FIXME: is there a way to update the _Line after creation?
			// null start values allow chaining to work, animateProperty will
			// determine them for us (except in ie6? -- ugh)
			top: top + (args.top || 0),
			left: left + (args.left || 0)
		}
	}, args));
	connectUtil.connect(_anim, "beforeBegin", _anim, init);
	return _anim; // dojo.Animation
};

dojoxFx.crossFade = function(/* Object */args){
	// summary:
	//		Returns an animation cross fading two element simultaneously
	// args:
	//		- args.nodes: Array - two element array of domNodes, or id's
	//
	//		all other standard animation args mixins apply. args.node ignored.

	// simple check for which node is visible, maybe too simple?
	var node1 = args.nodes[0] = dom.byId(args.nodes[0]),
		op1 = htmlUtil.style(node1,"opacity"),
		node2 = args.nodes[1] = dom.byId(args.nodes[1]),
		op2 = htmlUtil.style(node2, "opacity")
	;
	
	var _anim = coreFx.combine([
		baseFx[(op1 == 0 ? "fadeIn" : "fadeOut")](lang.mixin({
			node: node1
		},args)),
		baseFx[(op1 == 0 ? "fadeOut" : "fadeIn")](lang.mixin({
			node: node2
		},args))
	]);
	return _anim; // dojo.Animation
};

dojoxFx.highlight = function(/*Object*/ args){
	// summary:
	//		Highlight a node
	//
	// description:
	//		Returns an animation that sets the node background to args.color
	//		then gradually fades back the original node background color
	//
	// example:
	//	|	dojox.fx.highlight({ node:"foo" }).play();

	var node = args.node = dom.byId(args.node);

	args.duration = args.duration || 400;
	
	// Assign default color light yellow
	var startColor = args.color || '#ffff99',
		endColor = htmlUtil.style(node, "backgroundColor")
	;

	// safari "fix"
	// safari reports rgba(0, 0, 0, 0) (black) as transparent color, while
	// other browsers return "transparent", rendered as white by default by
	// dojo.Color; now dojo.Color maps "transparent" to
	// djConfig.transparentColor ([r, g, b]), if present; so we can use
	// the color behind the effect node
	if(endColor == "rgba(0, 0, 0, 0)"){
		endColor = "transparent";
	}

	var anim = baseFx.animateProperty(lang.mixin({
		properties: {
			backgroundColor: { start: startColor, end: endColor }
		}
	}, args));

	if(endColor == "transparent"){
		connectUtil.connect(anim, "onEnd", anim, function(){
			node.style.backgroundColor = endColor;
		});
	}

	return anim; // dojo.Animation
};

 
dojoxFx.wipeTo = function(/*Object*/ args){
	// summary:
	//		Animate a node wiping to a specific width or height
	//
	// description:
	//		Returns an animation that will expand the
	//		node defined in 'args' object from it's current to
	//		the height or width value given by the args object.
	//
	//		default to height:, so leave height null and specify width:
	//		to wipeTo a width. note: this may be deprecated by a
	//
	//		Note that the final value should not include
	//		units and should be an integer.  Thus a valid args object
	//		would look something like this:
	//
	//		|	dojox.fx.wipeTo({ node: "nodeId", height: 200 }).play();
	//
	//		Node must have no margin/border/padding, so put another
	//		node inside your target node for additional styling.

	args.node = dom.byId(args.node);
	var node = args.node, s = node.style;

	var dir = (args.width ? "width" : "height"),
		endVal = args[dir],
		props = {}
	;

	props[dir] = {
		// wrapped in functions so we wait till the last second to query (in case value has changed)
		start: function(){
			// start at current [computed] height, but use 1px rather than 0
			// because 0 causes IE to display the whole panel
			s.overflow = "hidden";
			if(s.visibility == "hidden" || s.display == "none"){
				s[dir] = "1px";
				s.display = "";
				s.visibility = "";
				return 1;
			}else{
				var now = htmlUtil.style(node,dir);
				return Math.max(now, 1);
			}
		},
		end: endVal
	};

	var anim = baseFx.animateProperty(lang.mixin({ properties: props }, args));
	return anim; // dojo.Animation
};

return dojoxFx;
});
