/**
  Define Javascript modules and functions for the
  MotionNode Google shopping cart.

  @file    cart/cart.js
  @author  Luke Tokheim, luke@motionnode.com
  @version 1.0
*/


/**
  Generic asynchronous remote procedure call object.

  Psuedocode class interface:
  \code
  class GLI_RemoteProcedureCall {
  public:
    // Constructor. Intialize XMLHttpRequest object.
    GLI_RemoteProcedureCall();
    // Read remote XML document (asynchronous).
    void get_xml(location, xml_callback)
  };
  \endcode
*/
function GLI_RemoteProcedureCall(notify, command_finished)
{
  this.request = null;
  this.id = null;
  this.notify = notify;
  this.callback = command_finished;
  
  if (window.XMLHttpRequest) {
    this.request = new XMLHttpRequest();
  } else if (window.ActiveXObject) {
    this.request = new ActiveXObject("Microsoft.XMLHttp");
  } else {
    throw "failed to instantiate XMLHttpRequest, missing implementation";
  }
}

/**
  Execute an asynchronous XML file read. GET the requested location
  and call the input callback function 'xml_callback' with the string
  valued response, i.e. the XML file contents.
*/
GLI_RemoteProcedureCall.prototype.get_xml = function(location, xml_callback)
{
  if (null !== this.request) {
    var rpc = this;
    this.request.onreadystatechange = function()
    {
      if (4 === rpc.request.readyState) {
        if (200 === rpc.request.status) {
          xml_callback(rpc.request.responseText, rpc.request.responseXML);
        } else {
          //if (rpc.callback) {
          //  rpc.callback(rpc.request.statusText, rpc.request.responseXML);
          //}
          rpc.error(rpc.request.statusText);
        }
      }
    }

    this.request.open('GET', location, true);
    this.request.send(null);
  } 
}

/**
  Error handler for the RPC class. Clean up the request and
  notify the client of an error.
*/
GLI_RemoteProcedureCall.prototype.error = function(message)
{
  if (this.notify) {
    this.notify(message.toString());
  }
  
  if (null !== this.request) {
    this.request.abort();
    this.request = null;
  }
}
// END class GLI_RemoteProcedureCall


/**
  Module to manage a Google Checkout cart. Compute the subtotal
  locally, call our cart generator in the background to create
  a signed cart for Google and return the neccessay HTML snippet.
*/
var GLI_Cart = function() {
  var _in_update = false;
  var _in_rate_update = false;
  var _unit_limit = 29;
  var _empty_cart_html = null;
  var _cart_uri = '/cart/gc/'; 
  var _cart_element_id = 'cart'; 
  var _rate_element_id = 'rate'; 
  var _cart_data = [];
  var _rate_data = [];

  return {
    set_cart_uri : function(uri)
    {
      _cart_uri = uri;
    },
    set_cart_data : function(data)
    {
      _cart_data = data;
    },
    set_rate_data : function(data)
    {
      _rate_data = data;
    },
    /**
      Units in the cart have changed. Re-compute the 
    */
    update : function()
    {
      var element = document.getElementById(_cart_element_id);
      if (!element) {
        return;
      } else if (!(_cart_data.length > 0)) {
        return;
      }

      var cart = _cart_data;
      
      // Track order totals here.
      var discount = 0;
      var subtotal = 0;
      
      // Compute unit totals here.
      for (var i in cart) {
        var element = document.getElementById(cart[i].id);
        if (element) {
          var value = 0;
          if (is_uint(element.value)) {
            value = parseInt(element.value);
            if (is_uint(cart[i].limit) && (value > parseInt(cart[i].limit))) {
              value = parseInt(cart[i].limit);
            }
          }

          cart[i].unit = value;
          cart[i].subtotal = value * cart[i].unit_price;
          element.value = value;
        }
        
        // Display the subtotal for this item.
        var element_subtotal = document.getElementById(cart[i].id + '_subtotal');
        if (element_subtotal) {
          element_subtotal.innerHTML = format_usd(cart[i].subtotal);
        }
        
        subtotal += cart[i].subtotal;
        if (cart[i].unit >= 20) {
          discount -= cart[i].unit * cart[i].unit_price * 0.15;
        } else if (cart[i].unit >= 10) {
          discount -= cart[i].unit * cart[i].unit_price * 0.10;
        }
      }
      
      subtotal += discount;
      
      {
        var element = document.getElementById('discount_subtotal');
        if (element) {
          element.innerHTML = format_usd(discount);
        }
        
        var element = document.getElementById('subtotal');
        if (element) {
          element.innerHTML = '$' + format_usd(subtotal);
        }
      }

      var service = null;
      {
        var element = document.getElementById('service');
        if (element) {
          service = element.value;
          element.disabled = true;
        }
      }

      var postalcode = null;
      {
        var element = document.getElementById('postalcode');
        if (element) {
          postalcode = element.value;
        }
      }

      var country = null;
      {
        var element = document.getElementById('country');
        if (element) {
          country = element.value;
        }
      }

      
      if (false === _in_update) {
        if (null !== _empty_cart_html) {
          var element = document.getElementById(_cart_element_id);
          if (element) {
            element.innerHTML = _empty_cart_html;
          }
        }

        GLI_Cart.notify('Updating order...');
 
        _in_update = true;
        try {
          // Build URI to access our Google signed cart generator.
          var uri = _cart_uri;
          var sep = '?';
          for (var i in cart) {
            uri = uri + sep + cart[i].id + '=' + cart[i].unit;
            sep = '&';
          }

          if (null !== service) {
            uri = uri + sep + 'service=' + service;
            sep = '&';
          }
          if (null !== postalcode) {
            uri = uri + sep + 'postalcode=' + postalcode;
            sep = '&';
          }
          if (null !== country) {
            uri = uri + sep + 'country=' + country;
            sep = '&';
          }

          var rpc = new GLI_RemoteProcedureCall(GLI_Cart.notify, GLI_Cart.update_callback);
          rpc.get_xml(uri, GLI_Cart.update_callback); 
        } catch (e) {
          GLI_Cart.update_callback();
          throw e;
        }
      } else {
        GLI_Cart.schedule_update();
      }

    },
    update_callback : function(text, xml)
    {
      var element = document.getElementById(_cart_element_id);
      if (element) {
        if (null === _empty_cart_html) {
          _empty_cart_html = element.innerHTML;
        }
        
        if (text) {
          element.innerHTML = text;
        }
      }

      GLI_Cart.notify();
 
      // Reset the update flag.
      _in_update = false;

      GLI_Cart.rate_update();
    },
    schedule_update : function()
    {
      // Schedule an update 1 second from now.
      window.setTimeout(GLI_Cart.update, 1000); 
    },
    schedule_rate_update : function()
    {
      // Schedule an update 1 second from now.
      window.setTimeout(GLI_Cart.rate_update, 1000);
    },
    rate_update : function()
    {
      var element = document.getElementById(_rate_element_id);
      if (!element) {
        return;
      }

      if (false === _in_rate_update) {
        GLI_Cart.notify('Updating shipping...');
 
        _in_rate_update = true;
        try {
          // Build URI to access our shipping option HTML generator.
          var uri = _cart_uri + '?rate=1';

          var rpc = new GLI_RemoteProcedureCall(GLI_Cart.notify, GLI_Cart.rate_update_callback);
          rpc.get_xml(uri, GLI_Cart.rate_update_callback); 
        } catch (e) {
          GLI_Cart.rate_update_callback();
          throw e;
        }
      } else {
        GLI_Cart.schedule_rate_update();
      }
    },
    rate_update_callback : function(text, xml)
    {
      var element = document.getElementById(_rate_element_id);
      if (element) {
        if (text) {
          element.innerHTML = text;
        }
      }

      GLI_Cart.notify();

      // Reset the update flag.
      _in_rate_update = false;
    },
    notify : function(message)
    {
      var element = document.getElementById('notify');
      if (element) {
        if (message) {
          element.innerHTML = message;
          element.style.display = 'block';
        } else {
          element.innerHTML = '';
          element.style.display = 'none';
        }
      }
    }
  }
}();
// End module GLI_Cart

/**
  Returns true if the string input value is an unsigned
  integer. Checks for valid characters [0-9].
*/
function is_uint(value)
{
  value = value.toString();

  for(var i=0; i<value.length; i++) {
    if ((value[i] < '0') ||  (value[i] > '9')) {
      return false;
    }
  }

  return (value.length > 0);
}

/**
  Format an integer as currency.
  10 => 10.00
  1000 => 1,000.00
*/
function format_usd(value)
{ 
  var working = value.toFixed(2).toString();

  if (Math.abs(value) >= 1000) {

    if (working.length > 3) {
      var comma = working.substr(working.length - 3);
      
      // Take the sign out of the mix.
      var sign = '';
      if (working[0] == '-') {
        sign = '-';
        working = working.substr(1);
      }
  
      for (var i=working.length-3; i>0; i-=3) {
        if (i <= 3) {
          comma = working.substr(0, i) + comma;
        } else {
          comma = ',' + working.substr(i-3, 3) + comma;
        }
      }

      working = sign + comma;
    }
 
  }
  
  return working;
}

function body_onload()
{
  GLI_Cart.update();

  var input = document.getElementsByName('input');
  if (input && (input.length > 0)) {
    input.item(0).focus();
  }
}

// END cart/cart.js
