/* ============================================================================
 *
 * (c) Copyright SXOOP Technologies Ltd. 2005-2007
 * All rights reserved.
 *
 * This file contains code which handles AJAX / JSON requests
 *
 */

var PXN8 = PXN8 || {};
PXN8.ajax = {};
/***************************************************************************

PXN8.ajax.createRequest
=======================
Create a XMLHttpRequest Object.

Returns
-------
A new XMLHttpRequest Object.

***/
PXN8.ajax.createRequest = function(){

	if (typeof XMLHttpRequest != 'undefined') {
   	 return new XMLHttpRequest();
   }	
   try 	{
       return new ActiveXObject("Msxml2.XMLHTTP");
   } catch (e) {
       try {
           return new ActiveXObject("Microsoft.XMLHTTP");
       } catch (e) { }
   }
   return false;
};
    
/***************************************************************************

PXN8.ajax.submitScript
======================
Submit a series of image-manipulation commands to the server. This is the end-point 
through which all image manipulation commands are passed to the server.

Parameters
----------
* script : An array containing a series of operations to be performed on the photo.
* callback  
A function which will be called when the server has completed the supplied series of operations and returns with *JSON* response.
The callback function should accept a single parameter of type object. 
The object supplied to the callback will have the following important properties...
    * status : A string value that can be eiterh "OK" or "ERROR" (if an error occurred while the server was processing the script.
    * errorMessage : A string value. Blank if no error occurred.
    * image : A relative (to PXN8.root) path to the compressed (bandwidth-friendly) image. This will be empty if an error has occurred.
    * uncompressed : A relative (to PXN8.root) path to the uncompressed (100% quality) image. This will be empty if an error has occurred.

Example
-------

    var script = PXN8.getScript();
    PXN8.ajax.submitScript(script,function(jsonResponse){
        if (jsonResponse.status == "OK"){
           var image = jsonResponse.image;
           // do something with the image
        }else{
           alert(jsonResponse.errorMessage);
        }
    });
***/
PXN8.ajax.submitScript = function(script, callback)
{
    // wph 20070226: 
    // optimize the script.
    //
    // [1] if there are 2 or more sequential resizes, then only use the last resize.
    // [2] if the user is rotating (without flipping ) then aggregate each rotation into 
    // a single operation (modulus 360).
    //
    script = PXN8.optimizeScript(script);
    
    var scriptToText = PXN8.objectToString(script);
    
    var cachedJSON = PXN8.json.scriptCache[scriptToText];
    
    if (cachedJSON){
        //
        // call immediately without going to the server
        //
        callback(cachedJSON);
        return;
    }

    //     
    // ======================================================================
        
    var req = PXN8.ajax.createRequest();
    
    var onJSONerror = function(r){
        alert(unescape(PXN8.strings.WEB_SERVER_ERROR) + "\n" + r.statusText + "\n" + r.responseText) ;
        var timer = document.getElementById("pxn8_timer");
        if (timer){
            timer.style.display = "none";
        }
        PXN8.updating = false;
    };
    
    //PXN8.json.bind(req,callback,onJSONerror);
    // wph 20070131 : use the caching binder instead
    //
    PXN8.json.bindScriptToResponse(req,callback,scriptToText,onJSONerror);
    
    req.open("POST", PXN8.root + "/pxn8.pl", true);
    req.setRequestHeader('Content-Type', 
                         'application/x-www-form-urlencoded');
 
    var submission = "script=" + scriptToText;
    
    req.send(submission);

    //
    
};

/**
 * Perform a series of optimizations on the script
 */
PXN8.optimizeScript = function(script)
{
    var self = PXN8;
    
    for (var i = 0; i < self.optimizations.length; i++){
        var optimize = self.optimizations[i];
        script = optimize(script);
    }
    return script;
};

PXN8.optimizations = [ function(script){
    /**
     * flatten resize ops
     */
    var result = [];
    
    for (var i = 0;i < script.length; i++){
        var op = script[i];
        var nextop = false;
        if (i+1 < script.length){
            nextop = script[i+1];
        }
        if (nextop && op.operation == 'resize' && nextop.operation == 'resize'){
            // 
            // do nothing - skip this operation
            //
        }else{
            result.push(op);
        }
    }
    return result;
},
function(script)
{
    /**
     * remove 2nd + more consecutive normalize operations.
     * (normalize - unlike enhance is not progressive)
     */
    var result = [];
    for (var i =0; i < script.length;i++){
        var op = script[i];
        var nextop = false;
        if (i+1 < script.length){
            nextop = script[i+1];
        }
        if (nextop && (nextop.operation == 'normalize') && (op.operation == 'normalize')){
        }else{
            result.push(op);
        }
    }
    return result;
},
function(script)
{
    var result = [];

    var colorsOp = null;
    //
    // optimizations for consecutive 'colors' operations.
    //
    for (var i = 0; i < script.length; i++){
        var op = script[i];
        if (op.operation != "colors"){
            if (colorsOp != null){
                result.push(colorsOp);
            }
            result.push(op);
            colorsOp = null;
        }else{
            if (colorsOp != null){
                //
                // saturation, brightness and hue are multiplicative
                //
                colorsOp.saturation = ((colorsOp.saturation / 100) * (op.saturation /100)) * 100;
                colorsOp.brightness = ((colorsOp.brightness / 100) * (op.brightness /100)) * 100;
                colorsOp.hue = ((colorsOp.hue / 100) * (op.hue /100)) * 100;
                //
                // contrast is additive
                //
                colorsOp.contrast = colorsOp.contrast  + op.contrast ;
            }else{
                colorsOp = op;
            }
        }
    }
    if (colorsOp != null){
        result.push(colorsOp);
    }

    return result;
    
},

function(script){
    /**
     * modulus 360 all consecutive rotate ops
     */
    var result = [];
    for (var i = 0;i < script.length; i++){
        var op = script[i];
        var nextop = false;
        if (i+1 < script.length){
            nextop = script[i+1];
        }
        if (nextop && (nextop.operation == 'rotate') && (op.operation == 'rotate')){
            //
            //
            //
            var flipping = (op.flipvt || op.fliphz || nextop.flipvt || nextop.fliphz);
            if (!flipping) {
                nextop.angle = (op.angle + nextop.angle) % 360;
            }else{
                //
                // it's a flip
                // is it the same type of flip as next op and are angles 0 in both cases ?
                if ((op.angle == 0 && nextop.angle == 0) && ((op.flipvt == nextop.flipvt) && (op.fliphz == nextop.fliphz))){
                    //
                    // it's two flipvts in a row or two fliphzs in a row
                    //
                    i += 1;
                }else{
                    result.push(op);
                }
                
            }
        }else{
            if (op.operation == 'rotate'){
                var flipping = (op.flipvt || op.fliphz || nextop.flipvt || nextop.fliphz);
                if (!flipping && op.angle == 0){
                    // skip operation - it's effectively a NOP
                }else{
                    // it's a straight rotation with an angle > 0
                    result.push(op);
                }
            }else{
                result.push(op);
            }
        }
    }
    return result;
}];

PXN8.json = {};

PXN8.json.bind = function(request,callback,onerror)
{
    request.onreadystatechange = function(){
        if (request.readyState == 4) {
            
            if (request.status == 200) {
                var json ;
                
                try{
                    var jsonText = request.responseText;
                    json  = eval('('+ jsonText + ')');
                }catch (e){
                    alert("An exception occured tring to evaluate server response:\n" +
                          request.responseText);
                }
                callback(json);
            } else {
                if (onerror){
                    onerror(request);
                }else{
                    alert(unescape(PXN8.strings.WEB_SERVER_ERROR) + "\n" + request.statusText + "\n" + request.responseText) ;
                }
            }
        }
    };
};
/**
 * wph 20070131
 * Store scriptText/responseText pairings in a cache
 * avoid unnecessary calls to the server
 */
PXN8.json.bindScriptToResponse = function(request,callback,scriptAsString,onerror)
{
    request.onreadystatechange = function(){
        if (request.readyState == 4) {
            
            if (request.status == 200) {
                var json ;
                try{
                    json  = eval('('+ request.responseText + ')');
                    //
                    // store request/response pairing in the cache
                    //
                    PXN8.json.scriptCache[scriptAsString] = json;
                }catch (e){
                    alert("An exception occured tring to evaluate server response:\n" +
                          request.responseText + "\nException: " + e);
                }
                callback(json);
            } else {
                if (onerror){
                    onerror(request);
                }else{
                    alert(unescape(PXN8.strings.WEB_SERVER_ERROR) + "\n" + request.statusText + "\n" + request.responseText) ;
                }
            }
        }
    };
};
/**
 * An associative array of scriptText / responseText pairings.
 */
PXN8.json.scriptCache = [];

    