/*
 Copyright 2008 Lee S. Barney
 
 
 This file is part of QuickConnectiPhone.
 
 QuickConnectiPhone is free software: you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 QuickConnectiPhone is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License
 along with QuickConnectiPhone.  If not, see <http://www.gnu.org/licenses/>.
 
 
 */
//the acceleration function call that is made from the underlying Objective-C framework when the device experiences acceleration
function accelerate(x, y, z){
    
	var accelObject = new AccelerationObject();
	accelObject.x = x;
	accelObject.y = y;
	accelObject.z = z;
	handleRequest('accel', accelObject);
	return true;
}
//an object to hold acceleration values in all three dimensions.
function AccelerationObject(){
	this.x = 0;
	this.y = 0;
	this.z = 0;
}

function stopBounce(){
    var height = document.body.scrollHeight;
    if(height > 480){
        document.ontouchmove = function(event){
            event.preventDefault();
        }
    }
}
//this function will scroll the current view by the x and y amount.  This function is ususally called by the Objective-C portion of an application.
function customScroll(xAmount, yAmount){
	window.scrollBy(xAmount,yAmount);
	return 'done';
}

//remove white space from the beginning and end of a string
function trim(aString){
    return aString.replace(/^\s+|\s+$/g, '');
}

//replace all occurances of a string with another
function replaceAll(aString, replacedString, newSubString){
    while(aString.indexOf(replacedString) > -1){
        aString = aString.replace(replacedString, newSubString);
    }
    return aString;
} 
//stop an event from doing its' default behavior
function stopDefault(event){
    if(event){
        event.preventDefault();
    }
}

function makeScrollable(anElement, startDragCmd, dragCmd, dropCmd){
    anElement.ontouchstart = prepareDrag;
    anElement.isScrollable = true;
    anElement.lastY = 0;
    anElement.lastX = 0;
    //do not set/reset the commands if none are passed in.
    if(startDragCmd){
        anElement.startDragCmd = startDragCmd;
    }
    if(dragCmd){
        anElement.dragCmd = dragCmd;
    }
    if(dropCmd){
        anElement.dropCmd = dropCmd;
    }
}

function clearDraggingInformation(anElement){
    anElement.lastY = 0;
    anElement.lastX = 0;
}

/*
 * Pass any DOM element to this function to use it for drag and drop
 * The *Cmd parameters are optional commands to be handled for 
 * drag/drop events.
 */
function makeDraggable(anElement, startDragCmd, dragCmd, dropCmd){
    anElement.ontouchstart = prepareDrag;
    anElement.isDraggable = true;
    anElement.lastY = 0;
    anElement.lastX = 0;
    //do not set/reset the commands if none are passed in.
    if(startDragCmd){
        anElement.startDragCmd = startDragCmd;
    }
    if(dragCmd){
        anElement.dragCmd = dragCmd;
    }
    if(dropCmd){
        anElement.dropCmd = dropCmd;
    }
}

function resetScrolling(anElement){


}

/*
 * This function is triggered each time an ontouchmove event is 
 * fired for an element that has been passed to makeDraggable.
 */
function dragIt(event)
{
    stopDefault(event);
    
    this.dragging = true;
    
    this.x = event.targetTouches[0].clientX - this.offsetX;
    this.y = event.targetTouches[0].clientY - this.offsetY;
    
    
    if(this.lastX || this.lastY){
        this.x += this.lastX;
        this.y += this.lastY;
    }
    var modString2 = ''
    if(this.isChangeable){
        if(this.rotation){
            //modString2 += ' rotateZ('+this.rotation+'deg)';
            modString2 += ' rotateZ('+this.oldRotation+'deg)';
        }
        if(this.oldScale){
            modString2 += ' scale('+this.oldScale+')';
            var multValue = 1;
            if(this.oldScale > 0){
                multValue = 1/this.oldScale;
            }
            else if(this.oldScale < 0){
                multValue = -1 * this.oldScale;
            }
        }
        
    }
    if(this.isScrollable){
        this.x = 0;
        if(this.y > 0){
            this.y = 0;
        }
    }
    var modString = 'translate(' + this.x + 'px, ' + this.y + 'px)'+modString2;
    this.style.webkitTransform = modString;
    if(this.dragCmd){
        var params = new Array();
        params.push(event);
        params.push(this);
        handleRequest(this.dragCmd, params);
    }
    
}

/*
 * This function is triggered every time an ontouchstart event is 
 * fired for an element that has been passed to makeDraggable.
 */
function prepareDrag(event)
{
    stopDefault(event);
    this.touches = event.targetTouches;
    var self = this;
    this.timeOut = setTimeout(function(){
            if(self.changing){
                return;
            }
            self.ontouchmove = dragIt;
            self.ontouchend = dragDone;
            self.offsetX = event.targetTouches[0].clientX;
            self.offsetY = event.targetTouches[0].clientY;
            if(!self.isScrollable){
                self.oldZIndex = self.style.zIndex;
                self.style.zIndex = 50;
            }
            if(self.startDragCmd){
                var params = new Array();
                params.push(event);
                params.push(self);
                handleRequest(self.startDragCmd, params);
            }
    }, 75);
}

/*
 * This function is triggered every time an ontouchend event is 
 * fired for an element that has been passed to makeDraggable.
 */
function dragDone(event)
{
    this.dragging = false;
    this.ontouchmove = null;
    this.ontouchend = null;
    this.lastX = this.x;
    this.lastY = this.y;
    this.style.zIndex = this.oldZIndex;
    
    if(this.dropCmd){
        var params = new Array();
        params.push(event);
        params.push(this);
        handleRequest(this.dropCmd, params);
    }
    
}

/*
 * Pass any DOM element to this function to make it scalable
 * and rotateable
 */
function makeChangeable(anElement, startChangeCmd, changeCmd, doneChangeCmd){
    anElement.ongesturestart = prepareGesture;
    anElement.ongesturechange = changeIt;
    anElement.ongestureend = gestureDone;
    anElement.isChangeable = true;
    anElement.oldRotation = 0;
    anElement.oldScale = 1;
    anElement.startChangeCmd = startChangeCmd;
    anElement.changeCmd = changeCmd;
    anElement.doneChangeCmd = doneChangeCmd;
    
}


/*
 * This function is triggered every time an ongesturechange event is 
 * fired for an element that has been passed to makeChangeable.
 */

function changeIt(event)
{
    stopDefault(event);
    //the user may have only put one finger inside of the target.
    if(this.dragging || (this.touches && this.touches.length < 2)){
        return;
    }
    
    
    this.rotation = event.rotation;
    var rotationValue = this.rotation + this.oldRotation;
    var scaleValue = event.scale * this.oldScale;
    //don't let it get to small to allow two touches
    if(this.offsetWidth * scaleValue < 150){
        scaleValue = 150/this.offsetWidth;
    }
    else if(this.offsetHeight * scaleValue < 150){
        scaleValue = 150/this.offsetHeight;
    }
    else{
        this.scale = event.scale;
    }
    var modString = 'rotateZ('+rotationValue+'deg) scale('+scaleValue+')';
    if(this.lastX || this.lastY){
        modString += ' translate(' + this.lastX + 'px, ' + this.lastY + 'px)';
        //update the center of rotation
        this.xCenterOffset = 50 + (this.lastX/this.offsetWidth) * 100;
        this.yCenterOffset = 50 + (this.lastY/this.offsetHeight) * 100;
        
        this.style.webkitTransformOriginX = (this.xCenterOffset)+'%';
        this.style.webkitTransformOriginY = (this.yCenterOffset)+'%';
    }
    this.style.webkitTransform = modString;
    
    if(this.changeCmd){
        var params = new Array();
        params.push(event);
        params.push(this);
        handleRequest(this.changeCmd, params);
    }
}

function prepareGesture(event){
    stopDefault(event);
    this.changing = true;
    this.oldZIndex = this.style.zIndex;
    this.style.zIndex = 50;
    
    if(this.startChangeCmd){
        var params = new Array();
        params.push(event);
        params.push(this);
        handleRequest(this.startChangeCmd, params);
    }
}

function gestureDone(event){
    this.changing = false;
    this.style.zIndex = this.oldZIndex;
    
    
    //the user may not have done a rotation.
    //if they did not rotation is undefined
    if(this.rotation){
        this.oldRotation += this.rotation;
    }
    //the user may not have done a pinch.
    //if they did not scale is undefined
    if(this.scale){
        this.oldScale = this.scale * this.oldScale;
    }
    
    if(this.doneChangeCmd){
        var params = new Array();
        params.push(event);
        params.push(this);
        handleRequest(this.doneChangeCmd, params);
    }
    this.changing = false;
}


/*
 * These mapping functions require functions for the business rules,
 * view controls, error controls, and security controls NOT the names 
 * of these items as strings.
 */
function mapCommandToBCF(aCmd, aBCF){
    var funcArray = businessMap[aCmd];
	if(funcArray == null){
		funcArray = new Array();
		businessMap[aCmd] = funcArray;
	}
	funcArray.push(aBCF);
}
function mapCommandToVCF(aCmd, aVCF){
    var funcArray = viewMap[aCmd];
	if(funcArray == null){
		funcArray = new Array();
		viewMap[aCmd] = funcArray;
	}
	funcArray.push(aVCF);
}
function mapCommandToECF(anErrorCmd, anECF){
	errorMap[anErrorCmd] = anECF;
}
function mapCommandSCF(aCmd, aSCF){
	var funcArray = securityMap[aCmd];
	if(funcArray == null){
		funcArray = new Array();
		securityMap[aCmd] = funcArray;
	}
	funcArray.push(aSCF);
}

function mapCommandToValCF(aCmd, aValCF){
	var funcArray = validationMap[aCmd];
	if(funcArray == null){
		funcArray = new Array();
		validationMap[aCmd] = funcArray;
	}
	funcArray.push(aValCF);
}


/*
 *  This function supports the passing of the paramters currently being used out of the current thread
 *  of execution and into another.  This can be because of a call to any of the asynchronous data access
 *  methods.  These can be AJAX calls, browser SQLite calls, or device based SQLite calls.
 */
function generatePassThroughParameters(){
    var passThroughParameters = new Array();
    passThroughParameters.push(window.curCmd);
    passThroughParameters.push(window.numFuncsCalled);
    passThroughParameters.push(globalParamArray);
    passThroughParameters.push(window.globalBCFResults);
    return passThroughParameters;
}

/*
 * errorMessage creates a single string message
 * from all of the data available from an error
 * created by a try/catch statement.  If it is 
 * passed something other than an error it 
 * treats it like a string.
 */
function errorMessage(err){
    var errMsg = 'ERROR: ';
    if(err.name && err.message && err.line && err.sourceURL){
        var fileName = err.sourceURL;
        var stringArr = fileName.split('/');
        fileName = stringArr[stringArr.length -1];
        errMsg += err.name+': '+err.message+' '+fileName+' line: '+err.line;
    }
    else{
        errMsg += err;
    }
	return errMsg;
}