/**********************************************************************
* 
*
*
**********************************************************************/

define(function(require){ var module = {}

//var DEBUG = DEBUG != null ? DEBUG : true


/*********************************************************************/

// convert JS arguments to Array...
var args2array =
module.args2array =
function(args){
	//return Array.apply(null, args)
	return [].slice.call(args)
}

// Quote a string and convert to RegExp to match self literally.
var quoteRegExp =
module.quoteRegExp =
function(str){
	return str.replace(/([\.\\\/\(\)\[\]\$\*\+\-\{\}\@\^\&\?\<\>])/g, '\\$1')
}


// XXX do we need to quote anything else???
var path2url =
module.path2url =
function(path){
	// test if we have a schema, and if yes return as-is...
	if(/^(http|https|file|[\w-]*):[\\\/]{2}/.test(path)){
		return path
	}
	// skip encoding windows drives...
	var drive = path.split(/^([a-z]:[\\\/])/i)
	path = drive.pop()
	drive = drive.pop() || ''
	return drive + (path
		.split(/[\\\/]/g)
		// XXX these are too aggressive...
		//.map(encodeURI)
		//.map(encodeURIComponent)
		.join('/')
		// NOTE: keep '%' the first...
		//.replace(/%/g, '%25')
		.replace(/#/g, '%23')
		.replace(/&/g, '%26'))
}


// NOTE: we are not using node's path module as we need this to work in
// 		all contexts, not only node... (???)
var normalizePath = 
module.normalizePath =
function(path){
	return typeof(path) == typeof('str') ? path
			// normalize the slashes...
			.replace(/(\/)/g, '/')
			// remove duplicate '/'
			.replace(/(\/)\1+/g, '/')
			// remove trailing '/'
			.replace(/\/+$/, '')
			// take care of .
			.replace(/\/\.\//g, '/')
			.replace(/\/\.$/, '')
			// take care of ..
			.replace(/\/[^\/]+\/\.\.\//g, '/')
			.replace(/\/[^\/]+\/\.\.$/, '')
		: path
}



/*********************************************************************/

var _transform_parse = {
	// 2d transforms:
	//martix: [],

	translate: ['left|0', 'top|0'],
	translateX: ['left'],
	translateY: ['top'],

	scale: [
		['scale'],
		['scaleX|scale', 'scaleY|scale'],
	],
	scaleX: ['scaleX'],
	scaleY: ['scaleY'],

	rotate: ['rotate'],
	
	skew: ['skewX', 'skewY'],
	skewX: ['skewX'],
	skewY: ['skewY'],

	// 3d transforms:
	//martix3d: [],

	translate3d: ['x|0', 'y|0', 'z|0'],
	translateZ: ['z'],

	scale3d: ['scaleX', 'scaleY', 'scaleZ'],
	scaleZ: ['scaleZ'],

	// XXX
	//rotate3d: [x, y, z, angle],
	// 	rotateX
	// 	rotateY
	// 	rotateZ
	
	perspective: ['perspective'],
}
var _transform_parse_rev = {}
Object.keys(_transform_parse).forEach(function(func){
	var args = _transform_parse[func]

	// we got multiple signatures == merge...
	if(!(args[0] instanceof Array)){
		args = [args]
	}

	args
		// merge lists of args...
		.reduce(function(a, b){ return [].concat.call(a, b) })
		.unique()
		// split alternatives...
		.map(function(a){ return a.split(/\|/g) })
		.forEach(function(a){
			var arg = a[0]
			var alt = a.slice(1)

			var e = _transform_parse_rev[arg] = _transform_parse_rev[arg] || {}

			e.funcs = e.funcs || []
			e.funcs.indexOf(func) < 0 && e.funcs.push(func)

			e.alt = e.alt || []
			// XXX we explicitly support only one alternative now...
			e.alt = e.alt.concat(alt).unique()
		})
})


// XXX get vendor...

// 
// 	Set element transform...
// 	.transform({..})
// 		-> element
//
// 	Get element transform...
// 	.transform()
// 	.transform(<attr>[, ...])
// 	.transform([<attr>, ...])
// 		-> data
//
// Supported transformations:
// 	x/y
// 	scale
// 	scaleX/scaleY
// 	origin
// 	originX/originY
//
// NOTE: pixel values are converted to numbers and back by default...
//
// XXX this will get/set values only on the first element, is this correct???
// XXX how do we combine translate(..) and translate3d(..)???
jQuery.fn.transform = function(){
	var that = this
	var args = args2array(arguments)

	// XXX get the browser prefix...
	var prefix = ''

	// normalize...
	args = args.length == 0 
			|| typeof(args[0]) == typeof('str') ? args
		: args[0].constructor === Array 
			|| args.length == 1 ? args[0]
		: args

	var elem = $(this)[0]
	var origin_str = elem.style[prefix + 'transformOrigin']
	var transform_str = elem.style[prefix + 'transform']

	// build the current state...
	// NOTE: we'll need this for both fetching (parsing) and writing 
	// 		(checking)...
	var transform = {}
	var functions = {}

	// origin...
	var origin = origin_str
		.split(/\s+/)
		// XXX add this to transform...

	// transform...
	transform_str
		// split functions...
		.split(/(\w+\([^\)]*)\)/)
		// remove empty strings...
		.filter(function(e){ return e.trim().length > 0})
		// split each function...
		.map(function(e){ return e
			// split args...
			.split(/\s*[\(,\s]\s*/)
			// cleanup...
			.filter(function(e){ return e.trim().length > 0 }) })
		// build the structure...
		.forEach(function(data){
			var func = data.shift() 
			var args = data

			// XXX do we care about function vendor tags here???
			var spec = _transform_parse[func]

			functions[func] = args

			// we do not know this function...
			if(spec == null){
				transform[func] = args

			} else {
				spec = spec[0] instanceof Array ? spec : [spec]
				spec.forEach(function(s){ 
					// skip non-matching signatures...
					// XXX is this correct???
					if(s.length != args.length){
						return
					}
					s.forEach(function(e, i){ 
						// this is for things that have optional arguments
						// like scale(..)
						// XXX should we treat this in some special way???
						if(args[i] == null){
							return
						}

						var alternatives = e.split(/\|/g)
						var k = alternatives.shift().trim()

						transform[k] = args[i].slice(-2) == 'px' 
								|| /[0-9\.]+/.test(args[i]) ?
							parseFloat(args[i]) 
							: args[i] 
					})
				})
			}
		})


	// get data...
	if(args.constructor === Array){
		var res = {}

		// return the full transform...
		if(args.length == 0){
			return transform
		}

		args.forEach(function(arg){
			// direct match in shorthand data...
			if(arg in transform){
				res[arg] = transform[arg]

			// try and find an alias...
			} else if(arg in _transform_parse_rev){
				var funcs = _transform_parse_rev[arg].funcs
				var alt = _transform_parse_rev[arg].alt[0]

				// no alternatives...
				if(!alt){
					res[arg] = ''

				// explicit number value...
				} else if(/^[0-9\.]+$/.test(alt)){
					res[arg] = parseFloat(alt)

				// explicit string value...
				} else if(/^(['"]).*\1$/.test(alt)){
					res[arg] = alt.slice(1, -1)

				} else {
					var v = $(that).transform(alt)
					res[arg] = v == '' ? alt : v
				}


			// collect from function...
			} else if(arg in _transform_parse){
				var v = res[arg] = {}
				_transform_parse[arg].forEach(function(e){
					var alternatives = e.split(/\|/g)
					var k = alternatives.shift().trim()

					v[k] = transform[k] != null ? transform[k] : ''
				})

			// don't know about this attr...
			} else {
				res[arg] = ''
			}
		})

		// special case: we asked for a single value...
		if(args.length == 1){
			return res[args[0]]
		}
		return res
	
	// set data...
	} else {
		transform = Object.create(transform)
		Object.keys(args).forEach(function(key){
			// the changing value already exists...
			if(key in transform
					// get one of the shorthand keys...
					// NOTE: we might need to pack or re-pack the date but we 
					// 		can't decide here...
					|| key in _transform_parse_rev
					// got one of the standard keys...
					|| key in _transform_parse){
				transform[key] = args[key]

			// key undefined...
			} else {
				console.warn('Ignoring key "%s".', key)
				transform[key] = args[key]
			}
		})


		console.log('>>>>', transform)

		// XXX set new values and resolve new functions...
		// XXX
		

		// build the value string...
		var transform_str = ''
		for(var f in functions){
			transform_str += f +'('
				+(functions[f]
					// XXX test if px value...
					.map(function(e){ return typeof(e) == typeof(123) ? e + 'px' : e })
					.join(', '))+') '
		}

		console.log(transform_str)

		// XXX STUB
		return functions

		// set the values...
		elem.style.transform = transform_str
		elem.style.transformOrigin = origin_str
	}

	return $(this)
}

// shorthands...
jQuery.fn.scale = function(value){
	if(value){
		return $(this).transform({scale: value})
	} else {
		return $(this).transform('scale')
	}
}
jQuery.fn.origin = function(value){
	if(value){
		return $(this).transform({origin: value})
	} else {
		return $(this).transform('origin')
	}
}


/**********************************************************************
* vim:set ts=4 sw=4 :                                                */
return module })
