dojo.provide("dojox.fx.flip");
dojo.experimental("dojox.fx.flip");
dojo.require("dojo.fx");
(function(){
	
	// because ShrinkSafe will eat this up: 
	var borderConst = "border",
		widthConst = "Width",
		heightConst = "Height",
		topConst = "Top",
		rightConst = "Right",
		leftConst = "Left",
		bottomConst = "Bottom"
	;

	dojox.fx.flip = function(/*Object*/ args){
		// summary: Animate a node flipping following a specific direction
		//	
		// description:
		//		Returns an animation that will flip the
		//		node around a central axis:
		//		if args.dir is "left" or "right" --> y axis
		//		if args.dir is "top" or "bottom" --> x axis
		//
		//		This effect is obtained using a border distorsion applied to a helper node.
		//
		//		The user can specify three background colors for the helper node:
		//		darkColor: the darkest color reached during the animation 
		//		lightColor: the brightest color
		//		endColor: the final backgroundColor for the node
        //
        //		depth: Float
		//			 0 <= depth <= 1 overrides the computed "depth"
        //          (0: min distorsion, 1: max distorsion)
        //
        //      whichAnim: String
        //          "first"          : the first half animation
        //          "last"           : the second one
        //          "both" (default) : both
        //
        //      axis: String
        //          "center" (default)    : the node is flipped around his center
        //          "shortside"           : the node is flipped around his "short" (in perspective) side
        //          "longside"            : the node is flipped around his "long" (in perspective) side
        //          "cube"                : the node flips around the central axis of the cube
        //
        //      shift: Integer
        //          node translation, perpendicular to the rotation axis
		//
		//	example:
		//	|	var anim = dojox.fx.flip({ 
		//	|		node: dojo.byId("nodeId"),
		//	|		dir: "top",
		//	|		darkColor: "#555555",
		//	|		lightColor: "#dddddd",
		//	|		endColor: "#666666",
		//	|		depth: .5,
		//	|		shift: 50,
		//	|		duration:300
		//	|	  });

		var helperNode = dojo.create("div"),
			node = args.node = dojo.byId(args.node), 
			s = node.style,
			dims = null, 
			hs = null, 
			pn = null,
			lightColor = args.lightColor || "#dddddd", 
			darkColor = args.darkColor || "#555555",
			bgColor = dojo.style(node, "backgroundColor"), 
			endColor = args.endColor || bgColor,
			staticProps = {}, 
			anims = [],
			duration = args.duration ? args.duration / 2 : 250,
			dir = args.dir || "left", 
			pConst = .9,
			transparentColor = "transparent",
			whichAnim = args.whichAnim,
			axis = args.axis || "center",
			depth = args.depth
		;
		// IE6 workaround: IE6 doesn't support transparent borders
		var convertColor = function(color){
			return ((new dojo.Color(color)).toHex() === "#000000") ? "#000001" : color;
		};

		if(dojo.isIE < 7){
			endColor = convertColor(endColor);
			lightColor = convertColor(lightColor);
			darkColor = convertColor(darkColor);
			bgColor = convertColor(bgColor);
			transparentColor = "black";
			helperNode.style.filter = "chroma(color='#000000')";
		}

		var init = (function(n){
			return function(){
				var ret = dojo.coords(n, true);
				dims = {
					top: ret.y,
					left: ret.x,
					width: ret.w,
					height: ret.h
				};
			}
		})(node);
		init();
		// helperNode initialization
		hs = {
			position: "absolute",
			top: dims["top"] + "px",
			left: dims["left"] + "px",
			height: "0",
			width: "0",
			zIndex: args.zIndex || (s.zIndex || 0),
			border: "0 solid " + transparentColor,
			fontSize: "0",
			visibility: "hidden"
		};
		var props = [ {}, 
			{
				top: dims["top"],
				left: dims["left"]
			}
		];
		var dynProperties = {
			left: [leftConst, rightConst, topConst, bottomConst, widthConst, heightConst, "end" + heightConst + "Min", leftConst, "end" + heightConst + "Max"],
			right: [rightConst, leftConst, topConst, bottomConst, widthConst, heightConst, "end" + heightConst + "Min", leftConst, "end" + heightConst + "Max"],
			top: [topConst, bottomConst, leftConst, rightConst, heightConst, widthConst, "end" + widthConst + "Min", topConst, "end" + widthConst + "Max"],
			bottom: [bottomConst, topConst, leftConst, rightConst, heightConst, widthConst, "end" + widthConst + "Min", topConst, "end" + widthConst + "Max"] 
		};
		// property names
		pn = dynProperties[dir];

		// .4 <= pConst <= .9
		if(typeof depth != "undefined"){
			depth = Math.max(0, Math.min(1, depth)) / 2;
			pConst = .4 + (.5 - depth);
		}else{
			pConst = Math.min(.9, Math.max(.4, dims[pn[5].toLowerCase()] / dims[pn[4].toLowerCase()]));
		}
		var p0 = props[0];
		for(var i = 4; i < 6; i++){
			if(axis == "center" || axis == "cube"){ // find a better name for "cube" 
				dims["end" + pn[i] + "Min"] = dims[pn[i].toLowerCase()] * pConst; 
				dims["end" + pn[i] + "Max"] = dims[pn[i].toLowerCase()] / pConst; 
			}else if(axis == "shortside"){
				dims["end" + pn[i] + "Min"] = dims[pn[i].toLowerCase()];
				dims["end" + pn[i] + "Max"] = dims[pn[i].toLowerCase()] / pConst; 
			}else if(axis == "longside"){
				dims["end" + pn[i] + "Min"] = dims[pn[i].toLowerCase()] * pConst;
				dims["end" + pn[i] + "Max"] = dims[pn[i].toLowerCase()];  
			}
		}
		if(axis == "center"){
			p0[pn[2].toLowerCase()] = dims[pn[2].toLowerCase()] - (dims[pn[8]] - dims[pn[6]]) / 4;
		}else if(axis == "shortside"){
			p0[pn[2].toLowerCase()] = dims[pn[2].toLowerCase()] - (dims[pn[8]] - dims[pn[6]]) / 2;
		}

		staticProps[pn[5].toLowerCase()] = dims[pn[5].toLowerCase()] + "px";
		staticProps[pn[4].toLowerCase()] = "0";
		staticProps[borderConst + pn[1] + widthConst] = dims[pn[4].toLowerCase()] + "px";
		staticProps[borderConst + pn[1] + "Color"] = bgColor;

		p0[borderConst + pn[1] + widthConst] = 0; 
		p0[borderConst + pn[1] + "Color"] = darkColor; 
		p0[borderConst + pn[2] + widthConst] = p0[borderConst + pn[3] + widthConst] = axis != "cube" 
			? (dims["end" + pn[5] +  "Max"] - dims["end" + pn[5] + "Min"]) / 2
			: dims[pn[6]] / 2
		;
		p0[pn[7].toLowerCase()] = dims[pn[7].toLowerCase()] + dims[pn[4].toLowerCase()] / 2 + (args.shift || 0);
		p0[pn[5].toLowerCase()] = dims[pn[6]];

		var p1 = props[1];
		p1[borderConst + pn[0] + "Color"] = { start: lightColor, end: endColor };
		p1[borderConst + pn[0] + widthConst] = dims[pn[4].toLowerCase()];
		p1[borderConst + pn[2] + widthConst] = 0;
		p1[borderConst + pn[3] + widthConst] = 0;
		p1[pn[5].toLowerCase()] = { start: dims[pn[6]], end: dims[pn[5].toLowerCase()] };

		dojo.mixin(hs, staticProps);
		dojo.style(helperNode, hs);
		dojo.body().appendChild(helperNode);

		var finalize = function(){
//			helperNode.parentNode.removeChild(helperNode);
			dojo.destroy(helperNode);
			// fixes a flicker when the animation ends
			s.backgroundColor = endColor;
			s.visibility = "visible";
		};
		if(whichAnim == "last"){
			for(i in p0){
				p0[i] = { start: p0[i] };
			}
			p0[borderConst + pn[1] + "Color"] = { start: darkColor, end: endColor }; 
			p1 = p0;
		}
		if(!whichAnim || whichAnim == "first"){
			anims.push(dojo.animateProperty({
				node: helperNode, 
				duration: duration,
				properties: p0
			}));
		}
		if(!whichAnim || whichAnim == "last"){
			anims.push(dojo.animateProperty({
				node: helperNode, 
				duration: duration,
				properties: p1,
				onEnd: finalize
			}));
		}

		// hide the original node
		dojo.connect(anims[0], "play", function(){
			helperNode.style.visibility = "visible";
			s.visibility = "hidden"; 
		});

		return dojo.fx.chain(anims); // dojo.Animation

	}

	dojox.fx.flipCube = function(/*Object*/ args){
		// summary: An extension to `dojox.fx.flip` providing a more 3d-like rotation
		//
		// description:
		//		An extension to `dojox.fx.flip` providing a more 3d-like rotation. 
		//		Behaves the same as `dojox.fx.flip`, using the same attributes and 
		//		other standard `dojo.Animation` properties.
		//
		//	example:
		//		See `dojox.fx.flip`
		var anims = [],
			mb = dojo.marginBox(args.node),
			shiftX = mb.w / 2,
			shiftY = mb.h / 2,
			dims = {
				top: {
					pName: "height",
					args:[
						{
							whichAnim: "first",
							dir: "top",
							shift: -shiftY
						},
						{
							whichAnim: "last",
							dir: "bottom",
							shift: shiftY
						}
					]
				},
				right: {
					pName: "width",
					args:[
						{
							whichAnim: "first",
							dir: "right",
							shift: shiftX
						},
						{
							whichAnim: "last",
							dir: "left",
							shift: -shiftX
						}
					]
				},
				bottom: {
					pName: "height",
					args:[
						{
							whichAnim: "first",
							dir: "bottom",
							shift: shiftY
						},
						{
							whichAnim: "last",
							dir: "top",
							shift: -shiftY
						}
					]
				},
				left: {
					pName: "width",
					args:[
						{
							whichAnim: "first",
							dir: "left",
							shift: -shiftX
						},
						{
							whichAnim: "last",
							dir: "right",
							shift: shiftX
						}
					]
				}
			}
		;
		var d = dims[args.dir || "left"],
			p = d.args
		;
		args.duration = args.duration ? args.duration * 2 : 500; 
		args.depth = .8;
		args.axis = "cube";
		for(var i = p.length - 1; i >= 0; i--){
			dojo.mixin(args, p[i]);
			anims.push(dojox.fx.flip(args));
		}
		return dojo.fx.combine(anims);
	};
	
	dojox.fx.flipPage = function(/*Object*/ args){
		// summary: An extension to `dojox.fx.flip` providing a page flip like animation.
		//
		// description:
		//		An extension to `dojox.fx.flip` providing a page flip effect.
		//		Behaves the same as `dojox.fx.flip`, using the same attributes and
		//		other standard `dojo.Animation` properties.
		//
		//	example:
		//		See `dojox.fx.flip`
		var n = args.node,
			coords = dojo.coords(n, true),
			x = coords.x,
			y = coords.y,
			w = coords.w,
			h = coords.h,
			bgColor = dojo.style(n, "backgroundColor"), 
			lightColor = args.lightColor || "#dddddd",
			darkColor = args.darkColor, 
			helperNode = dojo.create("div"),
			anims = [],
			hn = [],
			dir = args.dir || "right",
			pn = {
				left: ["left", "right", "x", "w"],
				top: ["top", "bottom", "y", "h"],
				right: ["left", "left", "x", "w"],
				bottom: ["top", "top", "y", "h"]
			},
			shiftMultiplier = {
				right: [1, -1],
				left: [-1, 1],
				top: [-1, 1],
				bottom: [1, -1]
			}
		;
		dojo.style(helperNode, {
			position: "absolute",
			width  : w + "px",
			height : h + "px",
			top    : y + "px",
			left   : x + "px",
			visibility: "hidden"
		});
		var hs = [];
		for(var i = 0; i < 2; i++){
			var r = i % 2,
				d = r ? pn[dir][1] : dir,
				wa = r ? "last" : "first",
				endColor = r ? bgColor : lightColor,
				startColor = r ? endColor : args.startColor || n.style.backgroundColor
			;
			hn[i] = dojo.clone(helperNode);
			var	finalize = function(x){
					return function(){
						dojo.destroy(hn[x]);
					}
				}(i)
			;
			dojo.body().appendChild(hn[i]);
			hs[i] = {
				backgroundColor: r ? startColor : bgColor
			};
			
			hs[i][pn[dir][0]] = coords[pn[dir][2]] + shiftMultiplier[dir][0] * i * coords[pn[dir][3]] + "px";
			dojo.style(hn[i], hs[i]);
			anims.push(dojox.fx.flip({
			    node: hn[i],
			    dir: d,
			    axis: "shortside",
			    depth: args.depth,
			    duration: args.duration / 2,
			    shift: shiftMultiplier[dir][i] * coords[pn[dir][3]] / 2,
				darkColor: darkColor,
				lightColor: lightColor,
			    whichAnim: wa,
			    endColor: endColor
			}));
			dojo.connect(anims[i], "onEnd", finalize);
		}
		return dojo.fx.chain(anims);
	};
	
	
	dojox.fx.flipGrid = function(/*Object*/ args){
		// summary: An extension to `dojox.fx.flip` providing a decomposition in rows * cols flipping elements
		//
		// description:
		//		An extension to `dojox.fx.flip` providing a page flip effect.
		//		Behaves the same as `dojox.fx.flip`, using the same attributes and
		//		other standard `dojo.Animation` properties and
		//
        //      cols: Integer columns
        //      rows: Integer rows
		//
		//      duration: the single flip duration
		//
		//	example:
		//		See `dojox.fx.flip`
		var rows = args.rows || 4,
			cols = args.cols || 4,
			anims = [],
			helperNode = dojo.create("div"),
			n = args.node,
			coords = dojo.coords(n, true),
			x = coords.x,
			y = coords.y,
			nw = coords.w,
			nh = coords.h,
			w = coords.w / cols,
			h = coords.h / rows,
			cAnims = []
		;
		dojo.style(helperNode, {
			position: "absolute",
			width: w + "px",
			height: h + "px",
			backgroundColor: dojo.style(n, "backgroundColor")
		});
		for(var i = 0; i < rows; i++){
			var r = i % 2,
				d = r ? "right" : "left",
				signum = r ? 1 : -1
			;
			// cloning
			var cn = dojo.clone(n);
			dojo.style(cn, {
				position: "absolute",
				width: nw + "px",
				height: nh + "px",
				top: y + "px",
				left: x + "px",
				clip: "rect(" + i * h + "px," + nw + "px," + nh + "px,0)"	
			});
	     	dojo.body().appendChild(cn);
			anims[i] = [];
			for(var j = 0; j < cols; j++){
				var hn = dojo.clone(helperNode),
					l = r ? j : cols - (j + 1)
				; 
				var adjustClip = function(xn, yCounter, xCounter){
					return function(){
						if(!(yCounter % 2)){
							dojo.style(xn, {
								clip: "rect(" + yCounter * h + "px," + (nw - (xCounter + 1) * w ) + "px," + ((yCounter + 1) * h) + "px,0px)"
							});
						}else{
							dojo.style(xn, {
								clip: "rect(" + yCounter * h + "px," + nw + "px," + ((yCounter + 1) * h) + "px," + ((xCounter + 1) * w) + "px)"
							});
						}
					}
				}(cn, i, j);
	     		dojo.body().appendChild(hn);
	     		dojo.style(hn, {
	     		    left: x + l * w + "px",
	     		    top: y + i * h + "px",
					visibility: "hidden"
	     		});
				var a = dojox.fx.flipPage({
				   node: hn,
				   dir: d,
				   duration: args.duration || 900,
				   shift: signum * w/2,
				   depth: .2,
				   darkColor: args.darkColor,
				   lightColor: args.lightColor,
				   startColor: args.startColor || args.node.style.backgroundColor
				}),
				removeHelper = function(xn){
					return function(){
						dojo.destroy(xn);
					}
				}(hn)
				;
				dojo.connect(a, "play", this, adjustClip);
				dojo.connect(a, "play", this, removeHelper);
				anims[i].push(a);
			}
			cAnims.push(dojo.fx.chain(anims[i]));
			
		}
		dojo.connect(cAnims[0], "play", function(){
			dojo.style(n, {visibility: "hidden"});
		});
		return dojo.fx.combine(cAnims);
	};
})();
