// all_lists is a list of all, well, lists.
var g_all_items = [];
var g_all_item_lists = [];
var g_list_id_now = 1;
var g_lists_transactions = [];
var g_item_ids_uis = [];

var runaway = 0;

var g_nln = 0;
var g_in_nl = 0;

// pass in an array like this: item_id_list[234] = 1, item_id_list[456] = 1
function redraw_items_in_list(item_id_list) {
   for (var x in item_id_list) {
      if (typeof g_item_ids_uis[x] != 'undefined') {
         for (var j = 0 ; j < g_item_ids_uis[x].length; j++) {
            g_item_ids_uis[x][j].draw();
            g_item_ids_uis[x][j].check_order();
         }
      }
   }
}


// if we have the item st red, find it, else make a new 'stub' one
function find_item(id) {

   if (typeof g_all_items[id] == 'undefined') {

      spec = {};
      spec[K.TAG_ITEM_ID] = id;

      var new_item = item(spec);
      g_all_items[id] = new_item;
   }
   return g_all_items[id];
}

var item = function(spec, my) {

   // object I'll return
   var that = {};

   my   = my   || {};
   spec = spec || {};
   // private variables
   var descrip = my.descript || "generic item";

   var picked  = false;
   var editing = false;
   var tmp_add_id;

   var id            = spec[K.TAG_ITEM_ID]       || 0;
   var parent_id     = spec[K.TAG_PARENT_ID]     || 0;
   var version       = spec[K.TAG_VERSION]       || 0;
   var type          = spec[K.TAG_TYPE_ID]       || null;
   var read_privacy  = spec[K.TAG_READ_PRIVACY]  || null;
   var write_privacy = spec[K.TAG_WRITE_PRIVACY] || null;
   var owner_user_id = spec[K.TAG_OWNER_USER_ID] || null;
   var search_hits   = spec[K.TAG_SEARCH_HITS]   || null;
   var payload       = spec[K.TAG_PAYLOAD]       || "";
   var load_state    = spec[K.TAG_LOAD_STATE]    || K.LOAD_STATE_NONE;
   var child_count   = spec[K.TAG_CHILD_COUNT]   || 0;
   var item_order    = spec[K.TAG_ITEM_ORDER];
   var user_string   = spec[K.TAG_USER_STRING]   || null;

   // if I don't have my attributes yet, assume I'm hidden
   var attributes;
   if (spec[K.TAG_ATTRIBUTES] != undefined) {
      attributes = spec[K.TAG_ATTRIBUTES];
   } else {
      attributes = K.ITEM_ATTRIB_HIDE;
   }

   if (item_order == undefined) item_order = null;

   var children_loaded = false; // improve this   

   var child_version_hash = spec[K.TAG_CHILD_VERSION_HASH] || null;
   var child_attrib_hash  = spec[K.TAG_CHILD_ATTRIB_HASH]  || null;

   var children = [];
   // reflects whether the client wants this to be loaded 
   //  THINK - should this stick, once the UI has set it?  Or 
   //   toggle out?
   var ui_wants_it = false;

   // true if I have the fully loaded state and have never heard of
   //  a newer version than I am
   var valid_payload = false;

   // my payload is valid if I have gotten a full load
   if (load_state == K.LOAD_STATE_COMPLETE) {  
      valid_payload = true;
   }

   that.add_child = function(child_id) {
      if (arrayIndexOf(children, child_id) == -1) {
         children.push(child_id);
         child_count = children.length;
      }
   }

   that.remove_child = function(child_id) {
      var idx = arrayIndexOf(children, child_id);
      if (idx != -1) {
         children.splice(idx,1);
         child_count = children.length;
      }
   }

   that.remove  = function() {
      if (g_all_items[parent_id]) {
         g_all_items[parent_id].remove_child(id);
      }
   }

   that.update  = function(spec_in) {
   // some of these are unnecessary to check, (for instance, how would
   //  the id change?) but for symmetery's sake we just repliacte the xtor

      var version_in  = spec_in[K.TAG_VERSION];
      var version_old = version;

      id            = spec_in[K.TAG_ITEM_ID]       || id;
      parent_id     = spec_in[K.TAG_PARENT_ID]     || parent_id;
      version       = spec_in[K.TAG_VERSION]       || version;
      type          = spec_in[K.TAG_TYPE_ID]       || type;
      read_privacy  = spec_in[K.TAG_READ_PRIVACY]  || read_privacy;
      write_privacy = spec_in[K.TAG_WRITE_PRIVACY] || write_privacy;
      owner_user_id = spec_in[K.TAG_OWNER_USER_ID] || owner_user_id;
      search_hits   = spec_in[K.TAG_SEARCH_HITS]   || search_hits;
      payload       = spec_in[K.TAG_PAYLOAD]       || payload;
      user_string   = spec_in[K.TAG_USER_STRING]   || null;

      if (spec_in[K.TAG_ATTRIBUTES] != undefined) {
         attributes = spec_in[K.TAG_ATTRIBUTES];
      }

      child_count   = spec_in[K.TAG_CHILD_COUNT]      || child_count;
      item_order    = spec_in[K.TAG_ITEM_ORDER]       || item_order;

      child_version_hash = spec[K.TAG_CHILD_VERSION_HASH] || child_version_hash;
      child_attrib_hash  = spec[K.TAG_CHILD_ATTRIB_HASH]  || child_attrib_hash;

   // if the new version is the same as the old one, and we are already
   //  in 'complete' stage, don't go down to a 'lower' load state.  This
   //  happens when search results come in for items we already have!
      if (version_in == version_old && load_state == K.LOAD_STATE_COMPLETE) {
         valid_payload = true;
      } else {
         valid_payload = false;
         load_state = spec_in[K.TAG_LOAD_STATE]    || K.LOAD_STATE_NONE;
      }
  }

   that.dump = function() { return "id: " + id + 
      " parent_id: " + parent_id + 
      " type: " + type+ 
      " # children: " + child_count + 
      " child hashes: " + child_version_hash + " / " + child_attrib_hash +
      " load state: " + load_state + 
      " UI wants load: " + (ui_wants_it ? "true" : "false") + 
      " ver: " + version + " attr: " + attributes +
      " perms: " + read_privacy + "/" + write_privacy + 
      " p/l (" + (valid_payload ? "valid" : "invalid") + "): " + payload;
   }

   // getters
   that.id             = function() { return id; }
   that.parent_id      = function() { return parent_id; }
   that.payload        = function() { return payload; }
   that.type           = function() { return type; }
   that.version        = function() { return version; }
   that.is_loaded      = function() { return version != null; }
   that.load_state     = function() { return load_state; }
   that.search_hits    = function() { return search_hits; }
   that.owner_user_id  = function() { return owner_user_id; } 
   that.editing        = function() { return editing; } 
   that.children       = function() { return children; }
   that.children_loaded = function() { return children_loaded; }
   that.tmp_add_id     = function() { return tmp_add_id;} // KILLME

   // this is the number of children I have.  Calculating this is
   //  a bit tricky.  At first I get it from the server.  Later I
   //  update it 'locally' only for add and delete
   that.child_count    = function() { 
      return child_count;
   }
   that.item_order     = function() { 
      if (item_order === null) return -1;
      else return parseInt(item_order);
   }
   that.attributes = function() { return attributes;}
   that.is_code = function() { return attributes & K.ITEM_ATTRIB_CODE; }
   that.is_hidden  = function() { 
//      dBug("NOW @ " + attributes);
      return (attributes & K.ITEM_ATTRIB_HIDE) == K.ITEM_ATTRIB_HIDE; 
   }

   that.children_recur = function() {
      var found_list = [];

      found_list.push(that.id());

      for (var i = 0; i < that.children().length; i++) {
         var child_id = that.children()[i];
         var child = g_all_items[child_id];
         if (child) {
            var gotback = child.children_recur();
            found_list = found_list.concat(gotback);
         }
      }
      return found_list;
   }

   that.set_attribute = function(attrib, is_set) {
      if (is_set) {
         attributes |=  attrib;
      } else {
         attributes &= ~attrib;
      }
   }

   // load an item if the UI wants it and it's invalid
   that.needs_load     = function() { 
      return ui_wants_it && !valid_payload;
   }

   // setters
   that.set_tmp_add_id = function(id) { tmp_add_id = id;}
   that.set_children_loaded = function(are_loaded) { 
      children_loaded = are_loaded; 
   }
   that.set_payload = function(txt) { payload = txt; }

   // sets that this item should be loaded if it isn't already 
   that.flag_ui_wants_it = function() {  
         ui_wants_it = true;
   }

   that.user_string   = function() { return user_string;  }
   that.read_privacy  = function() { return read_privacy;  }
   that.write_privacy = function() { return write_privacy; }
   that.set_privacy   = function(is_read, mode) {
      if (is_read) {
         read_privacy  = mode;
      } else {
         write_privacy = mode;
      }
   }

   that.send_set_attribute = function(which, tf, is_recur) {
      is_recur = is_recur || false;

   // perhaps lame, but the backend only wants 1, 0 - not true/false, on/off
      tf = (tf ? K.ITEM_ATTRIB_SET : K.ITEM_ATTRIB_CLEAR);
      var xml = _tag_(K.TAG_WHICH_ATTRIB, which);
      xml += _tag_(K.TAG_ITEM_ID, id);
      xml += _tag_(K.TAG_MODE, tf);
      
      if (is_recur) {
         xml += _tag_(K.TAG_RECURSIVE, "true");
      }
      do_ajax("/item_backend.php", process_attr_change, 
         K.TAG_CMD_EDIT_ATTRIB, xml);
   }
   that.hide = function(is_recur) {
      is_recur = is_recur || false;
      that.send_set_attribute(K.ITEM_ATTRIB_HIDE, !that.is_hidden(), is_recur);
   }

   // return the object I made
   return that;
};

var item_list = function(spec, my) {

   // object I'll return
   var that = {};

   my   = my   || {};
   spec = spec || {};

   // I am given this el, but I do not do anything to it
   //  other than add my DIV and manipulate that.  
   //  That way I am a good citizen.
   spec.parent_el = spec.parent_el || null;
   spec.list_id   = spec.list_id   || null;
   that.seeds     = spec.seeds    || [];
   that.picked_items = new Array();

   // make my toolbar
   that.toolbar_el = document.createElement('DIV');
   spec.parent_el.appendChild(that.toolbar_el);

   // shift buttons
   that.shift_el = document.createElement('SPAN');
   that.shift_el.style.display = "none";
   that.shift_el.style.marginRight = "5px";
   that.toolbar_el.appendChild(that.shift_el);

   var shift = document.createElement('A');
   var shift_img = document.createElement('IMG');
   shift_img.src = K.ICON_URL + '2up_arrow.png';
   shift_img.title= "Shift item to top";
   shift.appendChild(shift_img);
   shift.onclick = function() { 
      var items = that.get_picked_items();
      if (items.length != 1) return;
      var xml = _tag_(K.TAG_ITEM_ID, items[0]);
      xml += _tag_(K.TAG_SHIFT_DIRECTION, K.TAG_SHIFT_TOP);
      do_ajax("/item_backend.php", process_shift_item, K.TAG_CMD_SHIFT, xml);
   }
   that.shift_el.appendChild(shift);

   shift = document.createElement('A');
   shift_img = document.createElement('IMG');
   shift_img.src = K.ICON_URL + 'up_arrow.png';
   shift_img.title= "Shift item up";
   shift.appendChild(shift_img);
   shift.onclick = function() { 
      var items = that.get_picked_items();
      if (items.length != 1) return;
      var xml = _tag_(K.TAG_ITEM_ID, items[0]);
      xml += _tag_(K.TAG_SHIFT_DIRECTION, K.TAG_SHIFT_UP);
      do_ajax("/item_backend.php", process_shift_item, K.TAG_CMD_SHIFT, xml);
   }
   that.shift_el.appendChild(shift);

   shift = document.createElement('A');
   shift_img = document.createElement('IMG');
   shift_img.src = K.ICON_URL + 'down_arrow.png';
   shift_img.title= "Shift item down";
   shift.appendChild(shift_img);
   shift.onclick = function() { 
      var items = that.get_picked_items();
      if (items.length != 1) return;
      var xml = _tag_(K.TAG_ITEM_ID, items[0]);
      xml += _tag_(K.TAG_SHIFT_DIRECTION, K.TAG_SHIFT_DOWN);
      do_ajax("/item_backend.php", process_shift_item, K.TAG_CMD_SHIFT, xml);
   }
   that.shift_el.appendChild(shift);

   shift = document.createElement('A');
   shift_img = document.createElement('IMG');
   shift_img.src = K.ICON_URL + '2down_arrow.png';
   shift_img.title= "Shift item botton";
   shift.appendChild(shift_img);
   shift.onclick = function() { 
      var items = that.get_picked_items();
      if (items.length != 1) return;
      var xml = _tag_(K.TAG_ITEM_ID, items[0]);
      xml += _tag_(K.TAG_SHIFT_DIRECTION, K.TAG_SHIFT_BOTTOM);
      do_ajax("/item_backend.php", process_shift_item, K.TAG_CMD_SHIFT, xml);
   }
   that.shift_el.appendChild(shift);
 
//         t += _add_tab_item("shift_item(" + item.id() + ", true, true);",
//            null, '2up_arrow.png');

   // bullet button 
//   that.bullet_button = document.createElement('INPUT');
//   that.bullet_button.type = "button";
//   that.bullet_button.value = "Bullets";
//   that.bullet_button.disabled = true;
//   that.bullet_button.onclick = function() {
///*      var items = that.get_picked_items();
//      if (items.length < 1) return;
//
//      var xml = _tag(K.TAG_WRAP_ITEMS);
//      for (var i = 0; i < items.length; i++) {
//         xml += _tag_(K.TAG_ITEM_ID, items[i]);
//      }
//      xml += tag_(K.TAG_WRAP_ITEMS);
//      do_ajax("/item_backend.php", process_delete_item, K.TAG_CMD_DELETE, xml);
//*/
//   }

//   that.toolbar_el.appendChild(that.bullet_button);
  
   // cut button
   that.cut_button = document.createElement('INPUT');
   that.cut_button.type = "button";
   that.cut_button.value = "Cut";
   that.cut_button.disabled = true;
   that.toolbar_el.appendChild(that.cut_button);

   // delete button 
   that.del_button = document.createElement('INPUT');
   that.del_button.type = "button";
   that.del_button.value = "Delete";
   that.del_button.disabled = true;
   that.del_button.onclick = function() {
      var items = that.get_picked_items();
      if (items.length < 1) return;

      var xml = _tag(K.TAG_WRAP_ITEMS);
      for (var i = 0; i < items.length; i++) {
         xml += _tag_(K.TAG_ITEM_ID, items[i]);
      }
      xml += tag_(K.TAG_WRAP_ITEMS);
      do_ajax("/item_backend.php", process_delete_item, K.TAG_CMD_DELETE, xml);
   }
   that.toolbar_el.appendChild(that.del_button);

   // "show to" section
   that.show_to_div = document.createElement('SPAN');
   that.show_to_div.style.display = "none";
   that.show_to_div.style.marginLeft = "10px";
   that.show_to_div.appendChild(document.createTextNode("Show to:"));
   that.toolbar_el.appendChild(that.show_to_div);

   // "show to" pulldown
   that.show_to_sel = document.createElement('SELECT');
   that.show_to_div.appendChild(that.show_to_sel);
   that.show_to_sel.onchange = function(e) { 
      var new_priv_mode = that.show_to_sel.value;
      if (new_priv_mode != -1) {
         var picked = that.get_picked_items();
         var xml  = _tag_(K.TAG_RECURSIVE, false);
         xml     += _tag_(K.TAG_IS_READ, true);
         xml     += _tag_(K.TAG_PRIVACY, new_priv_mode);

         xml += _tag(K.TAG_WRAP_ITEMS);
         for (var i = 0; i < picked.length; i++) {
            xml += _tag_(K.TAG_ITEM_ID, picked[i]);
         }
         xml += tag_(K.TAG_WRAP_ITEMS);
         do_ajax("/item_backend.php", process_privacy, 
            K.TAG_CMD_EDIT_PRIV, xml);
      }
   }

   // "others can" section
   that.others_can_div = document.createElement('SPAN');
   that.others_can_div.style.display = "none";
   that.others_can_div.style.marginLeft = "10px";
   that.others_can_div.appendChild(document.createTextNode("Others can:"));
   that.toolbar_el.appendChild(that.others_can_div);

   // "others can" pulldown
   that.others_can_sel = document.createElement('SELECT');
   that.others_can_div.appendChild(that.others_can_sel);

   option = document.createElement('OPTION');
   option.text = "Just Look";
   option.value = K.ITEM_PRIVACY_WRITE_NONE;
   that.others_can_sel.options[that.others_can_sel.options.length] = option;

   option = document.createElement('OPTION');
   option.text = "Add Things";
   option.value = K.ITEM_PRIVACY_WRITE_ADD;;
   that.others_can_sel.options[that.others_can_sel.options.length] = option;

   // create my initial render el and add it to my parent
   var render_el = document.createElement('DIV');
   spec.parent_el.appendChild(render_el);
 
   // getters
   that.list_id   = function() { return spec.list_id;  }
   that.trans_id  = function() { return spec.trans_id;  }
   that.parent_el = function() { return spec.parent_el; }
//   that.seeds = function() { return spec.seeds; }

   // this is called ONCE, the first time we set a new set of seeds
   //  but we don't call it externally; we let set_seeds call it
   that.render = function() {
      remove_all_children(render_el);

      for (var i = 0; i < that.seeds.length; i++) {
         var item = find_item(that.seeds[i]);

         var new_item_ui =
            item_ui({root_el:render_el, item_list: that, 
            item: item, is_root:true});

         new_item_ui.draw();
      }
   }

   that.get_picked_items = function() {
      var picked = [];
      for (var i in that.picked_items) {
         if (that.picked_items[i] == 1) {
            picked.push(i);
         }
      }
      return picked;
   }

   that.handle_pick_item = function(item_id, is_on) {
      if (is_on) {
         that.picked_items[item_id] = 1;
      } else {
         if (that.picked_items[item_id] != undefined) {
            that.picked_items[item_id] = 0;
         }
      }

      var picked = that.get_picked_items();

      that.shift_el.style.display = (picked.length == 1 ? "inline" : "none");

      // update the toolbar
      that.del_button.disabled = (picked.length <= 0);
      that.cut_button.disabled = (picked.length != 1);
      
      that.show_to_div.style.display    = (picked.length > 0 ? "inline": "none");
      that.others_can_div.style.display = (picked.length > 0 ? "inline": "none");
      if (picked.length > 0) {

         var combine_read  = null;
         for (var p in picked) {
            var this_mode = g_all_items[picked[p]].read_privacy();
            if (combine_read == null) {
               combine_read = this_mode;
            } else if (combine_read != this_mode) {
               combine_read = -1;
            } 
         }

         var opt_data = [];

         if (combine_read == -1 || combine_read === null) {
            opt_data.push({text: "",  value: -1});
         }
         opt_data.push({text: "Anyone",  value: K.ITEM_PRIVACY_READ_PUBLIC});
         opt_data.push({text: "Just Me", value: K.ITEM_PRIVACY_READ_PRIVATE});
 
         set_options_for_select(that.show_to_sel, opt_data);
         that.show_to_sel.value = combine_read;
      }
   }


   that.set_seeds = function(seeds) {
      if (seeds instanceof Array) {
         that.seeds = seeds;
      } else if (seeds === null) {
         that.seeds = [];
      } else {
         that.seeds = [];
         that.seeds.push(seeds);
      }
      that.render();
      next_load();
   }
   return that;
}

