// ==UserScript==
// @name           Facebook MobWars Helper
// @namespace      http://userscripts.org/users/49912
// @description    Helps and improves the playing experience of the Facebook application MobWars with automatic functions.
// @source         http://userscripts.org/scripts/show/29870
// @identifier     http://userscripts.org/scripts/source/29870.user.js
// @version        0.1-alpha2
// @date           2008-07-09
// @creator        Piotr P. Karwasz
// @include        http://apps.facebook.com/mobwars/*
// ==/UserScript==
/*jsl:option explicit*/
/* $Revision: 93 $ */
var VERSION_TIMESTAMP = 1215651002891;
/***
 * Function: Script Update Checker
 *
 * Description:
 * Script Update Checker (http://userscripts.org/scripts/show/20145)
 * written by Jarett (http://userscripts.org/users/38602).
 */
var version_scriptNum = 29870;
var version_timestamp = VERSION_TIMESTAMP;
function updateCheck(forced){if((forced)||(parseInt(GM_getValue("lastUpdate","0"))+86400000<=(new Date().getTime()))){try{GM_xmlhttpRequest({method:"GET",url:"http://userscripts.org/scripts/review/"+version_scriptNum+"?"+new Date().getTime(),headers:{'Cache-Control':'no-cache'},onload:function(xhrResponse){GM_setValue("lastUpdate",new Date().getTime()+"");var rt=xhrResponse.responseText.replace(/&nbsp;?/gm," ").replace(/<li>/gm,"\n").replace(/<[^>]*>/gm,"");var scriptName=(/@name\s*(.*?)\s*$/m.exec(rt))[1];GM_setValue("targetScriptName",scriptName);if(parseInt(/version_timestamp\s*=\s*([0-9]+)/.exec(rt)[1])>version_timestamp){if(confirm("There is an update available for the Greasemonkey script \""+scriptName+".\"\nWould you like to go to the install page now?")){GM_openInTab("http://userscripts.org/scripts/show/"+version_scriptNum);}}else if(forced){alert("No update is available for \""+scriptName+".\"");}}});}catch(err){if(forced){alert("An error occurred while checking for updates:\n"+err);}}}}GM_registerMenuCommand(GM_getValue("targetScriptName","???")+" - Manual Update Check",function(){updateCheck(true);});updateCheck(false);



var Page = new Object();

Page.init = function() {
  /* Now */
  Page.now = Math.floor(new Date().getTime() / 1000);
  /* user */
  var div = document.getElementById('navigator');
  div = div.getElementsByTagName('a')[0];
  Page.c_user = div.href.match(/id=([0-9]+)/)[1];
  var url = location.href;
  var parts = url.split('/');
  /* type */
  Page.c_page = parts[4];
  /* params */
  Page.c_params = new Object();
  url = parts[5];
  if (!url) return;
  url = url.split('?');
  if (!url[1]) return;
  url = url[1];
  parts = url.split('&');
  for (var i = 0; i < parts.length; i++) {
    var param = parts[i].split('=');
    Page.c_params[param[0]] = param[1];
  }
}

Page.init();
/* vim:set tw=80 sts=2 et ft=javascript: */



var Menu = new Object();

Menu.init = function() {
  var menuCode = new Array();
  menuCode.push('<div class="scriptStatusHeader">-Script status-</div>');
  menuCode.push('<div class="scriptStatusContent">Status:<br /><span id="scriptstatus">Resting...</span><br /><span id="scripttimer"></span></div>');

  var menu = document.createElement('div');
  menu.id = 'ScriptStatus';
  menu.innerHTML = menuCode.join('');
  menuCode.length = 0;

  menuCode.push("#ScriptStatus { position:fixed; bottom:27px; left:2px; border:2px solid #6D84B4; background:#EEEEEE; color:#3B5998; padding:2px; font-weight:bold; width: 200px; height: 100px;}");
  menuCode.push("#ScriptStatus div.scriptStatusHeader { text-align:center; background: #6D84B4; color: #FFFFFF; }");
  menuCode.push("#ScriptStatus div.scriptStatusContent { border-width: 1px 1px 1px 1px; padding: 2px 2px 2px 2px; border-style: solid solid solid solid; height: 75px;}");

  var style = document.createElement('style');
  style.type = 'text/css';
  style.innerHTML = menuCode.join('');

  try { document.getElementsByTagName('head')[0].appendChild(style); }
  catch(e) {}
  document.body.insertBefore(menu, document.body.lastChild);
}
/* vim:set tw=80 sts=2 et ft=javascript: */





/***
 * Object: Utils
 *
 * Description: contains some utilities functions.
 */
Utils = new Object();

/***
 * Method: Element.getElementsByClassName(name, node)
 *
 * Description:
 * Gets a list of elements with a give className.
 *
 * @param name        -- the classname to look for.
 * @param node        -- node on which we start the search.
 * @return array      -- an array of nodes matching the classname.
 */
if (document.getElementsByClassName) {
  /* Firefox 3: native implementation */
  Utils.getElementsByClassName = function(classname, node) {
    if (!node) node = document;
    return node.getElementsByClassName(classname);
  }
} else {
  Utils.getElementsByClassName = function(classname, node) {
    var xpathExpression;
    var returnElements = new Array();
    xpathExpression = ".//*[contains(concat(' ', @class, ' '), ' " + classname + " ')]";
    var xpathResult = document.evaluate(xpathExpression, node, null, XPathResult.ANY_TYPE, null);

    while (node = xpathResult.iterateNext()) {
      returnElements.push(node);
    }
  }
}

/***
 * Function: Utils.getElementsByXPath(expression, node)
 *
 * Description:
 * Returns an array of elements obtained from evaluating the XPath expression on
 * the node.
 *
 * @param expression         -- the expression to evaluate.
 * @param node               -- context node, defaults to document.
 * @return array             -- an array of elements matching the expression
 */
Utils.getElementsByXPath = function(expression, node) {
  if (!node) node = document;
  var result = new Array();
  var xpathResult;
  xpathResult = document.evaluate(expression, node, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);

  var node;
  while (node = xpathResult.iterateNext()) {
    result.push(node);
  }

  return result;
}
/* vim:set tw=80 sts=2 et ft=javascript: */

function Boss() {
  this.name = 'Anonymous';
  this.cash = 0;
  this.health = 100;
  this.max_health = 100;
  this.energy = 10;
  this.max_energy = 10;
  this.stamina = 3;
  this.max_stamina = 3;
  this.exp = 0;
  this.level = 1;
  this.type = 'Unknown';
  this.mobsters = 0;
  var obj = eval(GM_getValue('boss','({})'));
  for (var i in obj) {
    this[i] = obj[i];
  }
}

Boss.prototype = new Object();

Boss.prototype.save = function() {
  GM_setValue('boss', this.toSource());
}

Boss.prototype.updateData = function() {
  /* Mobsters except myself */
  var header = document.getElementById('app8743457343_header');
  var str = header.getElementsByTagName('a')[0].innerHTML;
  this.mobsters = parseInt(str.match(/\d+/)[0]) - 1;
  /* Other data */
  header = document.getElementById('app8743457343_statusMenu');
  var divs = Utils.getElementsByClassName('wrap3outer', header);
  for (var i = 0; i < divs.length; i++) {
    str = divs[i].innerHTML;
    str = str.replace(/,/,'');
    var re = /(Cash|Health|Energy|Stamina|Exp|Level): \$?([0-9]+)\/?([0-9]+)?/;
    var result = str.match(re);
    if (!result) continue;

    switch (result[1]) {
      case 'Cash':
        this.cash = parseInt(result[2]);
        break;
      case 'Health':
        this.health = parseInt(result[2]);
        this.max_health = parseInt(result[3]);
        break;
      case 'Energy':
        this.energy = parseInt(result[2]);
        this.max_energy = parseInt(result[3]);
        break;
      case 'Stamina':
        this.stamina = parseInt(result[2]);
        this.max_stamina = parseInt(result[3]);
        break;
      case 'Exp':
        this.exp = parseInt(result[2]);
        break;
      case 'Level':
        this.level = parseInt(result[2]);
        break;
      default:
        alert("Unknown field: " + str);
    }
    if (Page.c_page == 'boss') {
      header = document.getElementById('app8743457343_content');
      divs = header.getElementsByTagName('tr');
      this.attack_strength = parseInt(divs[1].getElementsByTagName('td')[1].innerHTML);
      this.defense_strength = parseInt(divs[2].getElementsByTagName('td')[1].innerHTML);
    } else if (Page.c_page == 'profile' && Page.c_params.user_id == Page.c_user) {
      header = document.getElementById('app8743457343_content');
      header = header.getElementsByTagName('h1')[0].innerHTML;
      var result = header.match(/"(.*)", Level [0-9]+ (\w+)/);
      this.name = result[1];
      this.type = result[2];
    }
  }
  this.save();
}
/* vim:set tw=80 sts=2 et ft=javascript: */





function Stockitem() {
  this.attack = 0;
  this.defense = 0;
  this.upkeep = 0;
  this.price = 0;
}

function Stockpile() {
  this.size = 0;
  var obj = eval(GM_getValue('stockpile','({})'));
  for (var i in obj) {
    this[i] = obj[i];
  }
}

Stockpile.prototype = new Object();

Stockpile.prototype.save = function() {
  GM_setValue('stockpile', this.toSource());
}

Stockpile.prototype.updateData = function() {
  if (Page.c_page == 'stockpile') {
    var header = document.getElementById('app8743457343_content');
    var divs = header.getElementsByTagName('tr');
    for (var i = 1; i < divs.length; i++) {
      var div = divs[i];
      var item_id;
      item_id = div.getElementsByTagName('a')[0];
      /* Half of the <tr> tags are just for buying/selling stuff */
      if (!item_id) continue;
      item_id = item_id.name;
      var item = new Stockitem();
      /* The name of the item */
      item.name = div.getElementsByTagName('img')[0].title;
      /* The items data */
      var td = div.getElementsByTagName('td')[1];
      var tmp = td.getElementsByTagName('span')[0];
      if (tmp) {
        tmp = tmp.innerHTML;
        tmp = tmp.replace(/[\$,]/g,'');
        item.upkeep = parseInt(tmp);
      }
      tmp = td.innerHTML.match(/Attack: (\d+)/);
      if (tmp) item.attack = tmp[1];
      tmp = td.innerHTML.match(/Defense: (\d+)/);
      if (tmp) item.defense = tmp[1];
      /* Price and quantity owned */
      td = div.getElementsByTagName('td')[2];
      td = td.getElementsByTagName('span');
      tmp = td[0].innerHTML;
      tmp = tmp.replace(/[\$,]/g,'');
      item.price = parseInt(tmp);
      tmp = td[1].getElementsByTagName('b')[0];
      tmp = tmp.innerHTML;
      item.quantity = parseInt(tmp);
      /* Add to the stockpile */
      if (!this[item_id]) this.size++;
      this[item_id] = item;
    }
    this.save();
  }
}

/* vim:set tw=80 sts=2 et ft=javascript: */

/* Modules */
var modules = ['jobs'];





Timer = new Object();

Timer.start = function(next_page, message, timer) {
  var span = document.getElementById('scriptstatus');
  span.innerHTML = message;
  span = document.getElementById('scripttimer');
  /* Convert seconds to string */
  var sec = timer;
  var str = (sec % 60) + ' s';
  sec = Math.floor(sec / 60);
  if (sec) {
    str = (sec % 60) + ' m ' + str;
    sec = Math.floor(sec / 60);
    if (sec) str = sec + ' h ' + str;
  }
  span.innerHTML = 'in ' + str;

  if (timer <= 0) {
    if (typeof next_page == 'object') {
      next_page.click();
    } else {
      location.href = next_page;
    }
  } else {
    timer--;
    setTimeout(Timer.start, 1000, next_page, message, timer);
  }
}
/* vim:set tw=80 sts=2 et ft=javascript: */
function Job() {
  this.payout_min = 0;
  this.payout_max = 0;
  this.exp = 0;
  this.mobsters = 0;
  this.items = new Object();
  this.prep_items = new Object();
  this.prep_payout = new Object();
}

function Joblist() {
  var obj = eval(GM_getValue('jobs','({})'));
  for (var i in obj) {
    this[i] = obj[i];
  }
}

Joblist.prototype = new Object();

Joblist.prototype.save = function() {
  GM_setValue('jobs', this.toSource());
}

Joblist.prototype.updateData = function() {
  if (Page.c_page == 'jobs') {
    var header = document.getElementById('app8743457343_content');
    var divs = Utils.getElementsByClassName('rowData', header);
    for (var i = 0; i < divs.length; i++) {
      var div = divs[i];
      var job = new Job();
      /* Name */
      var tmp = div.getElementsByTagName('b')[0];
      job.name = tmp.innerHTML;
      /* Payout */
      tmp = div.innerHTML.match(/Payout:.*\$([0-9,]+) - \$([0-9,]+)/);
      if (tmp) {
        job.payout_min = parseInt(tmp[1].replace(/,/g,''));
        job.payout_max = parseInt(tmp[2].replace(/,/g,''));
      }
      /* Experience */
      tmp = div.innerHTML.match(/Experience: \+(\d+)/);
      if (tmp) job.exp = parseInt(tmp[1]);
      /* Mobsters */
      tmp = div.innerHTML.match(/Mobsters:\&nbsp;(\d+)/);
      if (tmp) job.mobsters = parseInt(tmp[1]);
      /* Energy */
      tmp = div.innerHTML.match(/Energy:\&nbsp;(\d+)/);
      if (tmp) job.energy = parseInt(tmp[1]);
      /* Requirements */
      var items = Utils.getElementsByXPath('.//a[contains(@href,"#item")]', div);
      for (var j = 0; j < items.length; j++) {
        /* Item */
        tmp = items[j].href;
        tmp = tmp.match(/#(item_\d+)/);
        var item_id = tmp[1];
        /* Quantity */
        tmp = items[j].nextSibling;
        if (tmp.tagName == 'SPAN' && (tmp = tmp.innerHTML.match(/\((use |\+)?(\d+)\)/))) {
          if (tmp[1] == 'use ') job.prep_items[item_id] = parseInt(tmp[2]);
          else if (tmp[1] == '+') {
            job.prep_payout[item_id] = parseInt(tmp[2]);
            /* Update the prep stockpile */
            var prep_stock = eval(GM_getValue('prep_stockpile','({})'));
            tmp = div.innerHTML.match(/Owned: (\d+)/);
            if (tmp) prep_stock[item_id] = parseInt(tmp[1]);
            GM_setValue('prep_stockpile', prep_stock.toSource());
          } else job.items[item_id] = parseInt(tmp[2]);
        } else {
          job.items[item_id] = 1;
        }
      }
      /* Id */
      tmp = Utils.getElementsByXPath('.//input[@name="jobid"]', div)[0];
      this[tmp.value] = job;
    }
  }
}

function jobs_exec() {
  var jobs = new Joblist();
  if (Page.c_page == 'jobs') {
    jobs.updateData();
    var pay_per_energy = 0;
    var action = 0;
    var tmp;
    jobs:
    for (var i in jobs) {
      var job = jobs[i];
      /* Check items */
      for (var j in job.items) {
        if (job.items[j] > stock[j].quantity) {
          GM_log("Not enough items for " + job.name);
          continue jobs;
        }
      }
      /* Check mobsters */
      if (job.mobsters > boss.mobsters) {
        GM_log("Not enough mobsters for " + job.name);
        continue jobs;
      }
      /* Check maximum energy */
      if (job.energy > boss.energy_max) {
        GM_log("Will never have enough energy for " + job.name);
        continue jobs;
      }
      tmp = (jobs[i].payout_min + 0.0) / jobs[i].energy;
      if (tmp > pay_per_energy) {
        action = i;
        pay_per_energy = tmp;
      }
    }
    job = jobs[action];
    /* We have a candidate job. We can do it if we have enough energy or wait
 * until later */
    /* First we check for the prep work */
    var prep_stock = eval(GM_getValue('prep_stockpile','({})'));
    prep:
    for (var item in job.prep_items) {
      if (prep_stock[item] < job.prep_items[item]) {
      /* We select a prep work instead */
        for (var j in jobs) {
          if (jobs[j].prep_payout[item]) {
            action = j;
            job = jobs[action];
            break prep;
          }
        }
      }
    }
    /* Then if we have enough energy */
    if (boss.energy >= job.energy) {
      var submit = Utils.getElementsByXPath('//input[@name="jobid" and @value="' + action + '"]/../input[@type="submit"]');
      Timer.start(submit[0], "Doing " + job.name + "...", 10);
      return;
    } else {
      boss.next_action = Page.now + (job.energy - boss.energy) * 300;
      boss.next_action_message = "Doing " + job.name + "...";
      boss.next_action_page = "http://apps.facebook.com/mobwars/jobs/";
      boss.save();
    }
  }
  Timer.start(boss.next_action_page, boss.next_action_message, boss.next_action - Page.now);
}
/* vim:set tw=80 sts=2 et ft=javascript: */

/* Create and update our boss and the stockpile */
var boss = new Boss();
var stock = new Stockpile();
boss.updateData();
stock.updateData();

/* If the stockpile does not have any items, it means that we just installed the
 * script */
if (!stock.item_14) location.href = 'http://apps.facebook.com/mobwars/stockpile/';

/* Init the menu */
Menu.init();

for (var i = 0; i < modules.length; i++) {
  if (this[modules[i] + '_exec']) this[modules[i] + '_exec']();
}
/* vim:set tw=80 sts=2 et ft=javascript: */
