var compareDevices = {
  // Assoc array containg all the marked elements
  markedElements: undefined,

  // Assoc array containg cache for matches
  cache: undefined,

  // URL to the PHP page called for receiving data for compare devices
  fUrl: '../../../index.php',
  
  // the postnukemodule and function
  funcUrl: 'module=FreaksDevices&func=ajaxProcess',

  // the devices in the comparelist
  selDevIds: undefined,

  // the querystring for which an HTTP request has been initiated
  httpRequestQueryString: undefined,

  // since the querystring sometimes is different from the string we 
  // want to cache, we have a cachestring for which an HTTP request 
  // has been initiated
  httpRequestCacheString: undefined,

  // used to contain which part of the page is going to be updated, so
  // that we can put everytring in a general updatefunction
  httpRequestType: undefined,

  // initializing compare devices
  init: function() {
    compareDevices.markedElements = new Object();
    compareDevices.cache = new Object();
    compareDevices.selDevIds = new Object;
  },


/*************************************/
/** HTTP REQUEST *********************/
/*************************************/

  // the HTTP request reponse will always go to this function. 
  // Here the result will be cached, and the we will be sent 
  // to showResponse()
  ajaxResponse: function(originalRequest) {
    // add the response to the cache if it ain't there from before
    if (!compareDevices.check_cache(compareDevices.httpRequestCacheString)) {
      compareDevices.cache[compareDevices.httpRequestCacheString] = originalRequest.responseText;
    }

    // show the response
    compareDevices.showResponse(originalRequest.responseText);
  },

  // show the HTTP request reponse according to a given type
  showResponse: function(responseText) {
    var el = undefined;

    switch(compareDevices.httpRequestType) {
      case 'matches':
        $('matches').innerHTML = responseText;
        compareDevices.refreshMarkedMatches();
        break;
        
      case 'details':
        $('details').innerHTML = responseText;
        break;

      case 'comparelist':
        $('compare').innerHTML = responseText;
        break;

      default:
        // Nothing defind for type, so we don't do anything. Return
        // since we don't want an error when trying to set innerHTML on
        // a nonexisting element
        return;
        break;
    }

    // We must re-apply the CSS rules when we alter the DOM
    Behaviour.apply(compareDevicesRules);
  },


/*************************************/
/** LOADER ***************************/
/*************************************/

  showLoader: function(load_id) {
    if ($(load_id) == undefined) {
      return;
    }
    Element.show(load_id);
  },

  hideLoader: function(load_id) {
    if ($(load_id) == undefined) {
      return;
    }
    Element.hide(load_id);
  },


/*************************************/
/** CACHE ****************************/
/*************************************/

  check_cache: function(queryString) {
    return compareDevices.cache[queryString] != undefined && queryString != null;
  },


/*************************************/
/** FEATURES *************************/
/*************************************/

  getSelectedFeatures: function() {
    var selFeatIds = new Object();
    var featureOptions = $('featsel').getElementsByTagName('option');
    for(var i = 0; i < featureOptions.length; i++) {
      var featId = featureOptions[i].value;
      if (featId != 0) {
        selFeatIds[featId] = featId;
      }
    }
    return selFeatIds;
  },


/*************************************/
/** COMPARELIST **********************/
/*************************************/

  refreshComparelist: function() {
    if (compareDevices.selDevIds == undefined) {
      return;
    }

    var queryString = "&open=compare_devices";

    // loop through the selected devices and add them
    // to the querystring
    for(var dev_id in compareDevices.selDevIds) {
      queryString = queryString + "&selDevIds[]=" + dev_id;
    }

    // get the selected features and loop through them and adding
    // them to the querystring
    var selFeatIds = compareDevices.getSelectedFeatures();
    for(var feat_id in selFeatIds) {
      queryString = queryString + "&selFeatureIds[]=" + feat_id;
    }

    // we have to define type here, so that showResponse() know which
    // element is going to be updated
    compareDevices.httpRequestType = 'comparelist';

    // if we are here detailed deviceinfo is not in the cache, 
    // so we have to do a HTTP request to fetch it

    compareDevices.httpRequestQueryString = queryString;
    compareDevices.httpRequestCacheString = null;

    var pars = compareDevices.funcUrl + queryString;

    // do a HTTP request for matches
    var myAjax = new Ajax.Request( 
      compareDevices.fUrl, 
      { 
        method: 'get',
        parameters: pars,
        onCreate: compareDevices.showLoader('load_compare'),
        onComplete: compareDevices.ajaxResponse
      }
    );
  },

  addAllToComparelist: function() {
    var matchesIds = new Object;
    matchesInput = $("matchesIds").value;

    matchesIds = matchesInput.split(",");

    for(i = 0; i < matchesIds.length; i++) 
    {
      compareDevices.addToComparelist(matchesIds[i]);
    }

    compareDevices.refreshMarkedMatches();
    compareDevices.refreshComparelist();
  },

  addToComparelist: function(dev_id) {
    if (dev_id == undefined || compareDevices.selDevIds[dev_id] != undefined) {
      return;
    }

    compareDevices.selDevIds[dev_id] = dev_id;
  },

  removeFromComparelist: function(dev_id) {
    if (dev_id == undefined || compareDevices.selDevIds[dev_id] == undefined) {
      return;
    }

    delete compareDevices.selDevIds[dev_id];
  },


/*************************************/
/** MATCHES **************************/
/*************************************/

  refreshMarkedMatches: function() {
    for(var dev_id in compareDevices.selDevIds) {
      // the HTML and bgcolor should be moved to the top, since we
      // need them on atleast two places
      var dev_html = '<a href="#" id="remove' + dev_id + '"><img src="modules/FreaksDevices/pnimages/remove.gif" alt="Remove device from comparelist" /></a>'
      var dev_bgColor = '#e2e2e2';
      var match_id = 'add' + dev_id;
      var match_el = $(match_id);

      // since we can remove matches, we must look up for marked 
      // matches for the categories which are not shown
      if (match_el != undefined) {
        compareDevices.setMatchesStyle(match_el, dev_bgColor, dev_html);
      }
    }
  },

  getAllDevIds: function() {
    return;
  },

  // Add or remove device from comparetable
  addOrRemoveDev: function(element) {
    var match_id = element.getAttribute("id");

    if (match_id.match(/add\d+/)) {
      // since it matched, add is clicked in the matcheslist

      // fetch the id, set the html and set the bgcolor
      var dev_id = match_id.substring(3, match_id.length);
      var dev_html = '<a href="#" id="remove' + dev_id + '"><img src="modules/FreaksDevices/pnimages/remove.gif" alt="Remove device from comparelist" /></a>'
      var dev_bgColor = '#e2e2e2';

      // add the device to the comparelist
      compareDevices.addToComparelist(dev_id);

      // we just need to refresh here, since we only alter the DOM 
      // when we remove devices
      compareDevices.refreshComparelist();
    } else if (match_id.match(/remove\d+/)) {
      // since it matched, remove is clicked in the matcheslist

      // fetch the id, set the html and set the bgcolor
      var dev_id = match_id.substring(6, match_id.length);
      var dev_html = '<a href="#" id="add' + dev_id + '"><img src="modules/FreaksDevices/pnimages/add.gif" alt="Add device to comparelist" /></a>';
      var dev_bgColor = '';

      // remove the device from the comparelist
      compareDevices.removeFromComparelist(dev_id);

      // remove the line with the deviceinfo from the comparelist
      var comp_el = $('compare' + dev_id);
      comp_el.parentNode.parentNode.parentNode.removeChild(comp_el.parentNode.parentNode);
    } else if (match_id.match(/compare\d+/)) {
      // since it matched, remove is clicked in the comparelist

      // fetch the id, set the html and set the bgcolor
      var dev_id = match_id.substring(7, match_id.length);
      var rem_id = 'remove' + dev_id;
      var dev_html = '<a href="#" id="add' + dev_id + '"><img src="modules/FreaksDevices/pnimages/add.gif" alt="Add device to comparelist" /></a>';
      var dev_bgColor = '';

      // remove the device from the comparelist
      compareDevices.removeFromComparelist(dev_id);

      // remove the line with the deviceinfo from the comparelist
      element.parentNode.parentNode.parentNode.removeChild(element.parentNode.parentNode);

      // change the element since we want to work with the element 
      // in the matchestable
      element = $(rem_id);
    } else {
      // did not match, so we return
      return;
    }

    compareDevices.setMatchesStyle(element, dev_bgColor, dev_html);
  },

  setMatchesStyle: function(element, bgColor, html) {
    // return if an error occurs
    if (element == undefined) {
      return;
    }
    if (element.parentNode == undefined) {
      return;
    }
    if (element.parentNode.parentNode == undefined) {
      return;
    }

    // set the bgcolor to the tr
    element.parentNode.parentNode.style.backgroundColor = bgColor;

    // set the html to the td
    element.parentNode.innerHTML = html;

    // We must re-apply the CSS rules when we alter the DOM
    Behaviour.apply(compareDevicesRules);
  },

  deviceDetails: function(element) {
    var dev_id = element.getAttribute("id");

    // we have to define type here, so that showResponse() know which
    // element is going to be updated
    compareDevices.httpRequestType = 'details';

    // if already in cache, show the cached version and return
    if(compareDevices.check_cache(dev_id)) {
      compareDevices.showResponse(compareDevices.cache[dev_id]);
      return;  
    }

    // if we are here detailed deviceinfo is not in the cache, 
    // so we have to do a HTTP request to fetch it

    // build the querystring
		var queryString = '&open=device_details';
		queryString = queryString + "&devId=" + dev_id.substring(7,dev_id.length);

    // if the querystring equals the last HTTP request querystring, 
    // we return since we don't want to do a double HTTP request
    if (compareDevices.httpRequestQueryString == queryString) {
      return;
    }

    compareDevices.httpRequestQueryString = queryString;
    compareDevices.httpRequestCacheString = dev_id;

    var pars = compareDevices.funcUrl + queryString;

    // do a HTTP request for matches
    var myAjax = new Ajax.Request( 
      compareDevices.fUrl, 
      { 
        method: 'get', 
        parameters: pars, 
        onCreate: compareDevices.showLoader('load_details'),
        onComplete: compareDevices.ajaxResponse
      }
    );
  },

  // refresh the matches according to the marked elements
  refreshMatches: function() {
    // go through the marked elements, and check if their result
    // is in the cache. We build the query on the same time, so
    // that in the case of no cache, we don't need to go through
    // the loop once more
    var cache_q = "";
    var matches_q = "";

    for (var feat_type in compareDevices.markedElements) {
      var feat_ids = "";
      for(var feat_id in compareDevices.markedElements[feat_type]) {
        cache_q = cache_q + "-" + compareDevices.markedElements[feat_type][feat_id];
        matches_q = matches_q + "&menu[]=" + compareDevices.markedElements[feat_type][feat_id].replace("|", "-");
      }
    }
    // remove first letter, which is -
    cache_q = cache_q.substring(1, cache_q.length);

    // we have to define type here, so that showResponse() know which
    // element is going to be updated
    compareDevices.httpRequestType = 'matches';

    // if already in cache, show the cached version and return
    if (compareDevices.check_cache(cache_q)) {
      compareDevices.showResponse(compareDevices.cache[cache_q]);
      return;  
    }

    // if we are here the marked elements are not in the cache, so we 
    // have to do a HTTP request to fetch the matches

    // build the querystring
		var queryString = '&open=show_matches';
		queryString += matches_q;

    // if the querystring equals the last HTTP request querystring, 
    // we return since we don't want to do a double HTTP request
    if (compareDevices.httpRequestQueryString == queryString) {
      return;
    }

    compareDevices.httpRequestQueryString = queryString;
    compareDevices.httpRequestCacheString = cache_q;

    var pars = compareDevices.funcUrl + queryString;

    // do a HTTP request for matches
    var myAjax = new Ajax.Request( 
      compareDevices.fUrl, 
      { 
        method: 'get', 
        parameters: pars, 
        onCreate: compareDevices.showLoader('load_matches'),
        onComplete: compareDevices.ajaxResponse
      }
    );
  },


/*************************************/
/** FEATURES MENU ********************/
/*************************************/

  // the function invoked when the user clicks on an element. 
  matchquery: function(element) {
    // Fetch the id attribute
    var att_id = element.getAttribute("id");

    // split the id attribute into type and id
    var feat = compareDevices.splitId(att_id);

    if (feat == undefined) {
      return;
    }

    // if the element is marked, we remove it
    // if the element is not marked, we add it
    if(compareDevices.isMarked(feat['type'], feat['id'])) {
      compareDevices.removeMarking(feat['type'], feat['id']);
    } else {
      compareDevices.addMarking(feat['type'], feat['id']);
    }

    // update matches according to marked elements
    compareDevices.refreshMatches();
  },

  // The function splits the id-attribute, so that we get the type 
  // and id
  splitId: function(att_id) {
    var index_of_seperator = att_id.indexOf('|')

    if (index_of_seperator < 0) {
      return;
    }

    var feat_type = att_id.substr(0, index_of_seperator);
    var feat_id = att_id.substr(index_of_seperator + 1, att_id.length);

    var feat = new Object();
    feat['type'] = feat_type;
    feat['id'] = feat_id;
    return feat;
  },

  // handles remove marking of elements
  removeMarking: function(feat_type, feat_id) {
    // if we have not marked any elements, they cannot be deleted
    if (compareDevices.markedElements[feat_type] == undefined) {
      return;
    }

    // if we have not marked any elements, they cannot be deleted
    if (compareDevices.markedElements[feat_type][feat_id] == undefined) {
      return;
    }

    var dev_family = feat_type == 0;

    var count = compareDevices.getCountMarkedElements(compareDevices.markedElements[feat_type]);

    // if we are working with the device family we only want to remove
    // the clicked element, and not all elements as we are with the
    // other types
    if (dev_family) {
      compareDevices.removeFromMarkedElements(feat_type, feat_id);
      return;
    }

    // we delete all markings for the specified type
    compareDevices.removeAllMarkedElements(feat_type);

    // if more than one element was marked, we want to add marking to 
    // the that was clicked
    if (count > 1) {
      compareDevices.addMarking(feat_type, feat_id);
    }
  },

  // How many elements does an object contain?
  getCountMarkedElements: function(arr) {
    var countNested = 0;
    for (var i in arr) {
      countNested++;
    }
    return countNested;
  },

  // Remove all marked elements from a specified type
  removeAllMarkedElements: function(feat_type) {
    for (var feat_id in compareDevices.markedElements[feat_type]) {
      compareDevices.removeFromMarkedElements(feat_type, feat_id);
    }
  },

  addMarking: function(feat_type, feat_id) {
    // if there does not exist any childnodes in feat_type,
    // we create it
    if (compareDevices.markedElements[feat_type] == undefined) {
      compareDevices.markedElements[feat_type] = new Object();
    }

    var dev_family = feat_type == 0;

    count = compareDevices.getCountMarkedElements(compareDevices.markedElements[feat_type]);

    // if count is more than 1, it means that at least two elements 
    // are marked now, therefore we delete all markings. This is only
    // when the we are NOT working with the devicefamily
    if (count > 1 && !dev_family) {
      compareDevices.removeAllMarkedElements(feat_type);
    }

    // add the element to the marked elements
    compareDevices.addToMarkedElements(feat_type, feat_id);

    // if the count was 1, we now have two elements, and want to mark 
    // all the elements between these two, but NOT when we working 
    // with the devicefamily
    if (count == 1 && !dev_family) {
      // get UL element
      var ul = $(compareDevices.markedElements[feat_type][feat_id]).parentNode.parentNode;
      var lis = ul.childNodes;

      var markedEls = 0;

      // this loop goes through all the lis, and add marking to 
      // those elements which are between two marked elements
      for (var i = 0; i < lis.length; i++) {
        // if nodetype is 1, we are dealing with an LI
        if (lis[i].nodeType == 1) {
          // get the anchor in li and find its type and id
          var a = lis[i].firstChild;
          var a_id = a.getAttribute("id");
          var feat = compareDevices.splitId(a_id);

          // if feat is undefined it means that the id was not on the
          // wanted form
          if (feat == undefined) {
            return;
          }

          if (compareDevices.isMarked(feat['type'], feat['id'])) {
            // if the element is marked we add 1 to markedEls, this way
            // it is wasy for us to know which elements to mark
            markedEls++;
          } else {
            // We only want to add the element when there is one 
            // element marked. This way we will get marking between
            // the two elements
            if (markedEls == 1) {
              // add the element to the marked elements
              compareDevices.addToMarkedElements(feat['type'], feat['id']);
            }
          }
        }
      }
    }
  },

  addToMarkedElements: function(feat_type, feat_id) {
    // add to marked elements object
    compareDevices.markedElements[feat_type][feat_id] = feat_type + "|" + feat_id;

    // set marked style
    compareDevices.setMarkedStyle(feat_type, feat_id);
  },

  removeFromMarkedElements: function(feat_type, feat_id) {
    delete compareDevices.markedElements[feat_type][feat_id];
    var att_id = feat_type + "|" + feat_id;
    compareDevices.setUnMarkedStyle(att_id);
  },

  isMarked: function(feat_type, feat_id) {
    // cannot be marked if it does not exist
    if (compareDevices.markedElements[feat_type] == undefined) {
      return false;
    }

    return compareDevices.markedElements[feat_type][feat_id] != undefined;
  },

  setMarkedStyle: function(feat_type, feat_id) {
    var att_id = feat_type + "|" + feat_id;

    // Defines the style when the element is marked
    var el = $(att_id);

    el.style.backgroundColor = "#cccccc";
  },

  setUnMarkedStyle: function(att_id) {
    // Defines the style when the element is not marked
    var el = $(att_id);

    el.style.backgroundColor = "";
  }
}

var compareDevicesRules = {
  '#search a': function( element ) {
    element.onclick = function() {
      compareDevices.matchquery(this);
      return false;
    }
    element = null;
  },

  '#matches .dev_details a': function( element ) {
    element.onclick = function() {
      compareDevices.deviceDetails(this);
      return false;
    }
    element = null;
  },

  '#matches .dev_addremove a': function( element ) {
    element.onclick = function() {
      compareDevices.addOrRemoveDev(this);
      return false;
    }
    element = null; 
  },

  '#add_all_to_comparelist': function( element ) {
    element.onclick = function() {
      compareDevices.addAllToComparelist();
      return false;
    }
    element = null; 
  },

  '#removefeature': function( element ) {
    element.onclick = function() {
      move_selected('featsel', 'featavail');
      compareDevices.refreshComparelist(); 
      return false;
    }
    element.onmouseover = function() {
      MM_swapImage('rem_avr','','/modules/FreaksDevices/pnimages/remove_but_on.gif',1);
    }
    element.onmouseout = function() {
      MM_swapImgRestore();
    }
    element = null;
  },

  '#addfeature': function( element ) {
    element.onclick = function() {
      move_selected('featavail', 'featsel');
      compareDevices.refreshComparelist(); 
      return false;
    }
    element.onmouseover = function() {
      MM_swapImage('add_avr','','/modules/FreaksDevices/pnimages/add_but_on.gif',1);
    }
    element.onmouseout = function() {
      MM_swapImgRestore();
    }
    element = null;
  },
  
  '#compare .delDev a': function( element ) {
    element.onclick = function() {
      compareDevices.addOrRemoveDev(this);
      return false;
    }
    element = null; 
  },
  
  '#compare .delFeat a': function( element ) {
    element.onclick = function() {
      var f_id = this.getAttribute("id");
      f_id = f_id.substring(7, f_id.length);

      rem_feat('featsel', 'featavail', f_id);
      compareDevices.refreshComparelist();

      return false;
    }
    element = null;
  }
}

Behaviour.register(compareDevicesRules);
register_onload_function(compareDevices.init);
