/**
  Define Javascript modules and functions for the
  Tokheim Stoneware shopping cart.

  @file    cart/cart.js
  @author  Luke Tokheim
  @version 1.0
*/


/**
  Generic asynchronous remote procedure call object.

  Psuedocode class interface:
  \code
  class TS_RemoteProcedureCall {
  public:
    // Constructor. Intialize XMLHttpRequest object.
    TS_RemoteProcedureCall();
    // Read remote XML document (asynchronous).
    void get_xml(location, xml_callback)
  };
  \endcode
*/
function TS_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.
*/
TS_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.
*/
TS_RemoteProcedureCall.prototype.error = function(message)
{
  if (this.notify) {
    this.notify(message.toString());
  }
  
  if (null !== this.request) {
    this.request.abort();
    this.request = null;
  }
}
// END class TS_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 TS_Cart = function() {
  var _in_update = false;
  var _empty_cart_html = null;
  var _cart_uri = '/cart/pp/'; 
  var _cart_element_id = 'cart'; 
  var _cart_data = [];

  return {
    set_cart_uri : function(uri)
    {
      _cart_uri = uri;
    },
    set_cart_data : function(cart)
    {
      _cart_data = cart;
    },
    /**
      Units in the cart have changed. Re-compute.
    */
    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 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].stock) && (value > parseInt(cart[i].stock))) {
              value = parseInt(cart[i].stock);
            }
          }

          cart[i].quantity = value;
          cart[i].amount = cart[i].price * value;
          element.value = value;
        }

        // Display the subtotal for this item.
        var element = document.getElementById(cart[i].id + '_amount');
        if (element) {
          element.innerHTML = format_usd(cart[i].amount);
        }
 
        subtotal += cart[i].amount;
      }
 
      {
        var element = document.getElementById('subtotal');
        if (element) {
          element.innerHTML = '$' + format_usd(subtotal);
        }
      }
      
      if (false === _in_update) {
        if (null !== _empty_cart_html) {
          var element = document.getElementById(_cart_element_id);
          if (element) {
            element.innerHTML = _empty_cart_html;
          }
        }
        TS_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].quantity;
            sep = '&';
          }

          var rpc = new TS_RemoteProcedureCall(TS_Cart.notify, TS_Cart.update_callback);
          rpc.get_xml(uri, TS_Cart.update_callback); 
        } catch (e) {
          TS_Cart.update_callback();
          throw e;
        }
      } else {
        TS_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;
        }
      }
      
      TS_Cart.notify();
      
      // Reset the update flag.
      _in_update = false;
    },
    schedule_update : function()
    {
      // Schedule an update 1 second from now.
      window.setTimeout(TS_Cart.update, 1000); 
    }, 
    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 TS_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()
{
  TS_Cart.update();

  var input = document.getElementsByName('input');
  if (input && (input.length > 0)) {
    input.item(0).focus();
  }
}

// END cart/cart.js
