/*
  License: Intel Sample Source Code License Agreement
  See : http://software.intel.com/sites/whatif/webapis/license/intel_webapis_license.html
    
  File: IntelCPCIWidget.js
  Author: Andy Idsinga, http://software.intel.com/en-us/blogs/author/andy-idsinga/
  Description: Intel CPU Power Connection Indicator widget JavaScript code
*/

/* 
  IntelCPCIWidget
  drawElId  - REQUIRED - string - user dom element ID that the widget draws into
              this will only be accessed after the document is loaded
  
  extraCfg  - optional - an object with extra configuration properties that
              override default behaviours
  extraCfg  = {
    //the update interval in seconds, 5 seconds is the default
    'updateInt'   : number,
    
    //enables the Intel widget UI with images etc, default = true
    //set to false if you want to have your own UI and logic.
    'uiEnable'    : boolean,
    
    //set this to your function and it will be called every update
    //interval with an Object that contains current CPU,Power,Connection
    //information (see below for the object properties)
    'updateCB'    : function,    
    
    //this element will receive debug messges strings
    'debugElId'   : string
  }
  
  //details for extraCfg.updateCB
  extraCfg.updateCB = function(currInfo)
  currInfo = {
    'cpu'         : Object, // a ref to IntelWebAPIs.cpu
    'power'       : Object, // a ref to IntelWebAPIs.power
    'connection'  : Object, // a ref to IntelWebAPIs.connection
  }
*/
function IntelCPCIWidget(drawElId,extraCfg) {

  /* the version of this object */
  var this_obj_version = '0.1.p0';

  /* containers for public and private data and functions.
     publics is returned to the caller, privates are used internally.*/
  var publics   = {};
  var privates  = {};

  privates.loaded     = false;
  privates.debugging  = false;
  privates.updateInt  = 5000;
  privates.drawElId   = drawElId;
  privates.uiEnable   = true;
  privates.updateCB   = undefined;
  privates.cpu_state = 0;
  privates.power_state = 0;
  privates.connection_state = 0;
  privates.experience_state = 0;
  
  // pull in extra configuraiton 
  if(typeof(extraCfg) != 'undefined') {
    
    if(typeof(extraCfg['debugElId']) != 'undefined' && 
       typeof(extraCfg['debugElId']) == 'string') {
      privates.debugging = true;
      privates.debugElId = extraCfg['debugElId'];
    }
    
    if(typeof(extraCfg['updateInt']) != 'undefined' &&
       typeof(extraCfg['updateInt']) == 'number') {
      privates.updateInt = extraCfg['updateInt'] * 1000;
    }

    if(typeof(extraCfg['uiEnable']) != 'undefined' &&
       typeof(extraCfg['uiEnable']) == 'boolean') {
      privates.uiEnable = extraCfg['uiEnable'];
    }

    if(typeof(extraCfg['updateCB']) != 'undefined' &&
       typeof(extraCfg['updateCB']) == 'function') {
      privates.updateCB = extraCfg['updateCB'];
    }
  }

  publics.get_version = function() {
    return this_obj_version;
  };
  
  publics.pause = function() {
    privates.debug_out("+,pause()");
    clearInterval(privates.intervalId);
    privates.debug_out("-,pause()");
  };
  
  publics.resume = function() {
    privates.debug_out("+,resume()");
    privates.intervalId = setInterval(privates.update,privates.updateInt);
    privates.debug_out("-,resume()");
  };
  
  privates.createUI = function() {
    var urlBase = privates.find_url_base('IntelCPCIWidget.js$','script','src');
    
    /* privates.ui_cfg is the UI configuration data structure
        - setup_ui() knows how to work through this data structure to generate 
          the UI
        - update_ui() knows how to dig into privates.ui_cfg.block_cfg to 
          find UI resources */
    privates.ui_cfg = {
      'base_id':'intelcpciwidget_',
  
      /* container and subcontainer for desired layout
         inside the user's container */
      'container': {
        'id':'hl',
        'style':"position:relative;height:25px;background-image:url(\'" + urlBase + "/blank_bar.png\');background-repeat:repeat-x;margin-left:auto;margin-right:auto;",
        'subcontainer':{'style':"position:relative;width:250px;margin-left:auto;margin-right:auto;"}
      },

      /* block configuration - full config for each widget block
         these are added to the containers defined above*/
      'blocks': {
        /* make sure there is a config block for each value in the list array 
           blocks are added to the UI in this order!
           NOTE: you can't have a block named "uiblklist"*/
        'uiblklist':['logo','cpu','power','connection','experience'],
        'logo': {
          'id':'logo',
          'title':'Intel CPU/Connection/Power Indicator Widget',
          'style':"position:absolute;width:50px;height:25px;top:0px;margin:0px;padding:0px;left:0px",
          'img': {
            'style':"position:absolute;border-width:0px;margin:0px;padding:0px;top:0px;",
            'def':urlBase + '/intel_logo.png'
          },
          //'onclick':'window.intel_cc_cpc_combo.update_ui();'          
        },
        'cpu': {
          'id':'cpu',
          'title':'CPU Load (' + IntelWebAPIs.cpu.name + ')',
          'style':"position:absolute;width:50px;height:25px;top:0px;margin:0px;padding:0px;left:50px",
          'img': {
            'id':'img',
            'style':"position:absolute;border-width:0px;margin:0px;padding:0px;top:0px;",
            'def': urlBase + '/cpu_blank.png',
            'red': urlBase + '/cpu_red.png',
            'yellow': urlBase + '/cpu_yellow.png',
            'green': urlBase + '/cpu_green.png'
          }          
        },
        'power': {
          'id':'pow',
          'title':'power status',
          'style':"position:absolute;width:50px;height:25px;top:0px;margin:0px;padding:0px;left:100px",
          'img': {
            'id':'img',
            'style':"position:absolute;border-width:0px;margin:0px;padding:0px;top:0px;",
            'def': urlBase + '/pow_blank.png',
            'red': urlBase + '/pow_red.png',
            'yellow': urlBase + '/pow_yellow.png',
            'green': urlBase + '/pow_green.png'
          }          
        },
        'connection': {
          'id':'con',
          'title':'connection status',
          'style':"position:absolute;width:50px;height:25px;top:0px;margin:0px;padding:0px;left:150px",
          'img': {
            'id':'img',
            'style':"position:absolute;border-width:0px;margin:0px;padding:0px;top:0px;",
            'def': urlBase + '/conn_blank.png',
            'red': urlBase + '/conn_red.png',
            'yellow':urlBase + '/conn_yellow.png',
            'green':urlBase + '/conn_green.png'
          }          
        },
        'experience': {
          'id':'exp',
          'title':'experience rating',
          'style':"position:absolute;width:50px;height:25px;top:0px;margin:0px;padding:0px;left:200px",
          'img': {
            'id':'img',
            'style':"position:absolute;border-width:0px;margin:0px;padding:0px;top:0px;",
            'def': urlBase + '/blank_bar.png',
            'red': urlBase + '/thumbs_down.png',
            'yellow':urlBase + '/thumbs_up_q.png',
            'green': urlBase + '/thumbs_up.png'
          }          
        }
      }
    };
    
    //debugging - for inspection via DOM tool like firebug
    //publics.ui_cfg = privates.ui_cfg;
    
    img_preload();
    setup_ui();
    
    function img_preload(){
      var preload_list = [
        privates['ui_cfg']['blocks']['power']['img']['def'],
        privates['ui_cfg']['blocks']['power']['img']['red'],
        privates['ui_cfg']['blocks']['power']['img']['yellow'],
        privates['ui_cfg']['blocks']['power']['img']['green'],
        privates['ui_cfg']['blocks']['cpu']['img']['def'],
        privates['ui_cfg']['blocks']['cpu']['img']['red'],
        privates['ui_cfg']['blocks']['cpu']['img']['yellow'],
        privates['ui_cfg']['blocks']['cpu']['img']['green'],
        privates['ui_cfg']['blocks']['connection']['img']['def'],
        privates['ui_cfg']['blocks']['connection']['img']['red'],
        privates['ui_cfg']['blocks']['connection']['img']['yellow'],
        privates['ui_cfg']['blocks']['connection']['img']['green'],
        privates['ui_cfg']['blocks']['experience']['img']['def'],
        privates['ui_cfg']['blocks']['experience']['img']['red'],
        privates['ui_cfg']['blocks']['experience']['img']['yellow'],
        privates['ui_cfg']['blocks']['experience']['img']['green']
      ];
      for( var preimgsrc in preload_list){
        var image = new Image();
        image.src = preload_list[preimgsrc];
      }
    }

    function setup_ui() {
      /* debug */
      privates.debug_out("+,setup_ui");
      var uicontainer = document.getElementById(privates.drawElId);
    
      /* create the high level container and subcontainer*/
      var hlctr = document.createElement("div");
      hlctr.setAttribute("id", privates['ui_cfg']['base_id'] + privates['ui_cfg']['container']['id']);
      hlctr.style.cssText = privates['ui_cfg']['container']['style'];
      uicontainer.appendChild(hlctr);
      if(privates['ui_cfg']['container']['subcontainer'] !== undefined) {
        var subctr = document.createElement("div");
        subctr.setAttribute("id", privates['ui_cfg']['base_id'] + privates['ui_cfg']['container']['id'] + 'sub');
        subctr.style.cssText = privates['ui_cfg']['container']['subcontainer']['style'];
        hlctr.appendChild(subctr);
        hlctr = subctr;
      }
    
      /* now loop through the block configurations and add the blocks */
      for(var x in privates['ui_cfg']['blocks']['uiblklist']) {
        var blockname = privates['ui_cfg']['blocks']['uiblklist'][x];
        var blockcfg = privates['ui_cfg']['blocks'][blockname];
        
        var blkdiv = document.createElement("div");
        blkdiv.setAttribute("id",privates['ui_cfg']['base_id'] + blockcfg['id']);
        blkdiv.style.cssText = blockcfg['style'];
    
        /* add an onclick attribute if configured */
//         if(blockcfg['onclick'] !== undefined) {
//           blkdiv.setAttribute("onclick", blockcfg['onclick']);
//         }
        
        /* create and add an image if configured */
        if(blockcfg['img'] !== undefined) {
          var blkimg = document.createElement("img");
          blkimg.setAttribute("id", privates['ui_cfg']['base_id'] + blockcfg['id'] + blockcfg['img']['id']);
          blkimg.setAttribute("src", blockcfg['img']['def']);
          blkimg.setAttribute("alt", blockcfg['title']);
          blkimg.setAttribute("title", blockcfg['title']);
          blkimg.style.cssText = blockcfg['img']['style'];
          blkdiv.appendChild(blkimg);
        }
            
        hlctr.appendChild(blkdiv);
      }
    }
  
  };
  
  privates.update_cpu = function() {
    var cpu_ui_img = document.getElementById(
      privates['ui_cfg']['base_id'] + 
      privates['ui_cfg']['blocks']['cpu']['id'] + 
      privates['ui_cfg']['blocks']['cpu']['img']['id']);
    try {
      var currcpu = Math.round((IntelWebAPIs.cpu.load * 100));
      //privates.debug_out("update_cpu(),currcpu," + currcpu);
      cpu_ui_img.setAttribute("title",privates['ui_cfg']['blocks']['cpu']['title'] + ": " + currcpu + " %");
      if(currcpu  > 90) {
        cpu_ui_img.src = privates['ui_cfg']['blocks']['cpu']['img']['red'];
        privates.cpu_state = 0;
      }
      else if (currcpu > 50){
        cpu_ui_img.src = privates['ui_cfg']['blocks']['cpu']['img']['yellow'];
        privates.cpu_state = 1;
      }
      else {
        cpu_ui_img.src = privates['ui_cfg']['blocks']['cpu']['img']['green'];
        privates.cpu_state = 1;
      }
    }
    catch(err){
      /* if an unhandled error gets here set some defaults that
         effectively mean "unknown" */
      cpu_ui_img.setAttribute("title",privates['ui_cfg']['blocks']['cpu']['title'] + ": unknown");
      cpu_ui_img.src = privates['ui_cfg']['blocks']['cpu']['img']['def'];
      privates.cpu_state = -1;
      debug_out("error while updating cpu," + err);
    }
  };

  privates.update_power = function() {
    var power_ui_img = document.getElementById(
      privates['ui_cfg']['base_id'] + 
      privates['ui_cfg']['blocks']['power']['id'] + 
      privates['ui_cfg']['blocks']['power']['img']['id']);
    try {
      var currTitle = privates['ui_cfg']['blocks']['power']['title'] + ":"
      var currlevel = IntelWebAPIs.power.powerLevel;
      if(currlevel != -1){
        currTitle += " " + currlevel + " %";
      }
      if(IntelWebAPIs.power.usingExternalPowerSource == false) {
        currTitle += " (battery power";
        if(IntelWebAPIs.power.timeRemaining != -1) {
          currTitle += " " + Math.round((IntelWebAPIs.power.timeRemaining / 60)) + " minutes";
        }
        currTitle += ")";
  
        if(currlevel < 40) {
          power_ui_img.src = privates['ui_cfg']['blocks']['power']['img']['red'];
          privates.power_state = 0;
        }
        else if(currlevel < 80) {
          power_ui_img.src = privates['ui_cfg']['blocks']['power']['img']['yellow'];
          privates.power_state = 1;
        }
        else {
          power_ui_img.src = privates['ui_cfg']['blocks']['power']['img']['green'];
          privates.power_state = 1;
        }
      }
      else {
          /* on external power - set to green, state = true */
          currTitle += " (ext. power,charging)";
          power_ui_img.src = privates['ui_cfg']['blocks']['power']['img']['green'];
          privates.power_state = 1;
      }
      power_ui_img.setAttribute("title", currTitle);
    }
    catch(err) {
      /* if an unhandled error gets here set some defaults that
         effectively mean "unknown" */
      power_ui_img.setAttribute("title", privates['ui_cfg']['blocks']['power']['title'] + ": unknown");
      power_ui_img.src = privates['ui_cfg']['blocks']['power']['img']['def'];
      privates.power_state = -1;
    }
  };

  privates.update_connection = function() {
    var connection_ui_img = document.getElementById(
      privates['ui_cfg']['base_id'] + 
      privates['ui_cfg']['blocks']['connection']['id'] + 
      privates['ui_cfg']['blocks']['connection']['img']['id']);
    try{
      var conninfo = IntelWebAPIs.connection.getConnectionInfo();
      var currsig = -1;
      var mbps = -1;
      var currTitle = privates['ui_cfg']['blocks']['connection']['title'] + ": ";
      if(IntelWebAPIs.connection.connected) {
        if(conninfo.wifi.connected) {
          //wifi takes priority
          currsig = conninfo.wifi.signalStrength;
          mbps = ((conninfo.wifi.linkSpeed*100)/1000/1000);
          currTitle += "WIFI: ";
          if(currsig != -1){
            currTitle += currsig + " %, ";
          }
          currTitle +=  + mbps + " Mbps";
        }
        
        if(conninfo.lan.connected) {
          if(conninfo.wifi.connected) {
            currTitle += ". ";
          }
          if(currsig == -1 ) {
            currsig = conninfo.lan.signalStrength;
          }
          mbps = ((conninfo.lan.linkSpeed*100)/1000/1000);
          currTitle += "LAN: " + mbps + " Mbps";
        }
      }
      else {
        currTitle += "disconnected";
      }
      if(currsig == -1) {
        connection_ui_img.src = privates['ui_cfg']['blocks']['connection']['img']['def'];
        privates.connection_state = -1;
      }
      else if(currsig < 50) {
        connection_ui_img.src = privates['ui_cfg']['blocks']['connection']['img']['red'];
        privates.connection_state = 0;
      }
      else if(currsig < 70) {
        connection_ui_img.src = privates['ui_cfg']['blocks']['connection']['img']['yellow'];
        privates.connection_state = 1;
      }
      else {
        connection_ui_img.src = privates['ui_cfg']['blocks']['connection']['img']['green'];
        privates.connection_state = 1;
      }
      connection_ui_img.setAttribute("title",currTitle);
    }
    catch(err){
      /* if an unhandled error gets here set some defaults that
         effectively mean "unknown" */
      connection_ui_img.setAttribute("title",privates['ui_cfg']['blocks']['connection']['title'] + ": unknown");
      connection_ui_img.src = privates['ui_cfg']['blocks']['connection']['img']['def'];
      privates.connection_state = -1;
    }
  };

  privates.update_experience = function() {
    var experience_ui_img = document.getElementById(
      privates['ui_cfg']['base_id'] + 
      privates['ui_cfg']['blocks']['experience']['id'] + 
      privates['ui_cfg']['blocks']['experience']['img']['id']);

    if(privates.cpu_state        == 1 && 
       privates.power_state      == 1 && 
       privates.connection_state == 1) {
      experience_ui_img.src = privates['ui_cfg']['blocks']['experience']['img']['green'];
      experience_ui_img.setAttribute("title","Experience: OK");
      privates.experience_state = 1;
    }
    else {
      if (privates.cpu_state        == -1 || 
          privates.power_state      == -1 || 
          privates.connection_state == -1) {
        experience_ui_img.src = privates['ui_cfg']['blocks']['experience']['img']['yellow'];
        experience_ui_img.setAttribute("title","Experience: Unknown");
        privates.experience_state = -1;
      }
      else {
        experience_ui_img.src = privates['ui_cfg']['blocks']['experience']['img']['red'];
        experience_ui_img.setAttribute("title","Experience: Poor");
        privates.experience_state = 0;
      }
    }
  };
  
  privates.updateUI = function(){
    //var userEl = document.getElementById(privates.drawElId);
    privates.update_cpu();
    privates.update_power();
    privates.update_connection();
    privates.update_experience();
  };

  privates.update = function() {
    //privates.debug_out("+,update()");
    if(privates.uiEnable){
      privates.updateUI();
    }
    if(typeof(privates.updateCB) != 'undefined' &&
       typeof(privates.updateCB) == 'function') {
      //andy idsinga todo: make sure this is the best way to call
      //the callback - we want to avoid, exposing internal
      //state/privates if possible
      privates.updateCB({
        'cpu'         : IntelWebAPIs.cpu,
        'power'       : IntelWebAPIs.power,
        'connection'  : IntelWebAPIs.connection
      });
    }    
    //privates.debug_out("-,update()");
  };  

  privates.debug_out = function(str) {
    if(privates.debugging && privates.loaded) {
      var destEl = document.getElementById(privates.debugElId);
      var destDiv = document.createElement("div");
      destDiv.setAttribute("class","IntelCPCIWidget_dbg_msg");
      destDiv.innerHTML = "IntelCPCIWidget,dbg," + str + "\r\n";
      destEl.appendChild(destDiv);
    }
  };

  /*  find an element and get its base url */
  privates.find_url_base = function(findSrc,elType,elAttr) {
    var jsbase = undefined;
    try {
      var srcregex = findSrc;
      switch(elType){
        case 'script':
          /* I do this on purpose to avoid unwanted stuff coming in as args*/
          var fndType = 'script';
        break;
        default:
          throw "unsupport element type passed to IntelWebAPIsSiteUtils.get_url_base";
        break;
      }
      switch(elAttr){
        case 'src':
          var fndAttr = 'src';
        break;
        default:
          throw "unsupported attribute type passed to IntelWebAPIsSiteUtils.get_url_base";
        break;
      }
      var foundEls = document.getElementsByTagName(fndType);
      if(typeof(foundEls) != 'undefined' && foundEls.length > 0){
        for(var sx = 0; sx < foundEls.length; sx++){
          var thesrc = foundEls[sx].getAttribute(fndAttr);
          if(typeof(thesrc) != 'undefined' && thesrc != null){
            var idx = thesrc.search(srcregex);
            if(idx >= 0) {
              /* found our script element...*/
              switch(idx){
                case 0:
                jsbase = '.';
                break;
                
                default:
                jsbase = thesrc.substring(0,idx);
                break;
              }
              if(jsbase.charAt(jsbase.length-1) == '/'){
                jsbase = jsbase.substring(0,jsbase.length-1);
              }
              //alert("src=" + thesrc + ",jsbase=" + jsbase);
              break; //break from for loop
            }
          }
        }
      }
    }
    catch(err){
      //some error occured..
      jsbase = undefined;
    }
    return jsbase;
  };
  
  /* utility to chain on onload handler function to window.onload */
  privates.add_onload_handler = function(addfunc,altwindow) {
  
    var targwindow = window;
    if(typeof(altwindow) != 'undefined') {
      targwindow = altwindow;
    }
    
    /* chain the onload functions together */
    var prevf = targwindow.onload;
    var newf  = addfunc;
    targwindow.onload = function() {
        newf();
        if(typeof(prevf) == 'function') {
          prevf();
        }
      };
  };
  
  /* critical errors set the full html of the div we draw into */
  privates.set_critical_error = function(htmlTxt) {
    document.getElementById(privates.drawElId).innerHTML = htmlTxt;
  };
  
  privates.connector_inst_html = function(preMsg,instUrl) {
    var htmlTxt = preMsg + "<br/>";
    htmlTxt += "<a href=\"" + instUrl + "\">Please install the IntelWebAPI Connector from this link</a><br/>";
    return htmlTxt;  
  };

  privates.start_intel_webapis = function(nextStartupStageCB) {

    IntelWebAPIs.init(init_result_func);
    
    //called by IntelWebAPIs.init()
    function init_result_func(result) {
      if(result.success) {
        // now bring in apis required by this widget
        privates.debug_out("IntelWebAPIs Connector init success");
        IntelWebAPIs.require(["cpu","power","connection"],
          require_completion_func, require_progress_func);
      }
      else {
        var errorMsg = "IntelWebAPIs Connector init error: " + result.error.msg + "<br/>";
        privates.set_critical_error(
          privates.connector_inst_html(errorMsg,result.connectorInstUrl));
      }
    }

    //called by IntelWebAPIs.require()
    function require_completion_func(result) {
      if(result.success){
        privates.debug_out("IntelWebAPIi,cpu,power,connection,ready for use by app");
        nextStartupStageCB();
      }
      else {
        //check other result properties and handle error condition
        //andy idsinga todo: build a larger, prettier error message here
        //with more detail from the result object...
        privates.set_critical_error("IntelWebAPIs error: " + result.installInfo.msg);
      }
    }
    
    //called by IntelWebAPIs.require()
    function require_progress_func(statusMsg){
      //statusMsg is a string - send this wherever
      privates.debug_out("IntelWebAPIs connector status: " + statusMsg);
    }
  
  };
  
  privates.widget_startup_1 = function() {
    privates.loaded = true;
    
    privates.debug_out("+,widget_startup()");
    
    /* if success will call startup_2 or die with error*/
    privates.start_intel_webapis(privates.widget_startup_2);

    privates.debug_out("-,widget_startup()");
  };

  privates.widget_startup_2 = function() {
    if(privates.uiEnable){
      privates.createUI();
    }
    /* finally, start updating...*/
    setTimeout(privates.update,100);
    privates.intervalId = setInterval(privates.update,privates.updateInt);
  };  
  
  /*********************** execution starts here ******************************/
  privates.add_onload_handler(privates.widget_startup_1);

  /* return the public interface for external code to use*/
  return publics;
}

