(function() {
  
  var $p = pulp.base;
  
  var $E = pulp.Unit.prototype._$E;
  
  var $ = function(id) {
    return document.getElementById(id);
  };
  
  var getHttpRequest = function() {
    try { return new XMLHttpRequest(); } catch(e) {}
    try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch(e) {}
    try { return new ActiveXObject('Msxml3.XMLHTTP'); } catch(e) {}
    try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch(e) {}
  };
  
  pulp.unit.launcher = function(files, options) {
    return new pulp.unit.Launcher(files, options);
  };
  
  pulp.unit.Launcher = pulp.cls.create({
    initialize: function(files, options) {
      // get handles to all the buttons and the iframe
      this.table = $(options.table || 'test-launch-table');
      this.runAll = $(options.runAll || 'run-all');
      this.cancel = $(options.cancel || 'cancel');
      this.frame = $(options.iframe || 'test-frame');
      this.marginLeft = options.marginLeft || 400;
      this.marginTop = options.marginTop || 0;
      this.baseUrl = options.baseUrl || './';
      
      // attach runAll handler
      this.runAll.onclick = $p.bind(this.runAllTests, this);
      // attach cancel handler
      this.cancel.onclick = $p.bind(this.stop, this);
      this.cancel.style.display = 'none';
      // create rows given the list of files      
      this.createRows(files);
      // size frame to fit the window
      this.resizeFrame();
      // resize the iframe if the window is resized
      window.onresize = $p.bind(this.resizeFrame, this);
      // get the url of the current page
      //this.url = window.location.protocol + '//' + window.location.host + window.location.pathname;      
      this.frame.onload = $p.bind(this.setResultCallback, this);
    },
    createRows: function(files) {
      var self = this;
      var statusTd, runButton, viewButton;
      var tbody = this.table.getElementsByTagName('tbody')[0];
      this.runButtons = [];
      this.statusTds = [];
      for (var href in files) {
        statusTd = $E('td', {className: 'test-status'}, '(NOT RUN)');
        runButton = $E('input', {type: 'button', value: 'Run', onclick: (function(href, title, statusTd) {
          return function() {
            self.runFile(href, title, statusTd);
          };
        })(href, files[href], statusTd)});
        viewButton = $E('input', {type: 'button', value: 'Source', onclick: (function(href, title) {
          return function() {
            self.viewFile(href, title);
          };
        })(href, files[href])});        
        this.runButtons.push(runButton);
        this.statusTds.push(statusTd);
        tbody.appendChild(
          $E('tr', {className: 'not-run'}, [
            $E('td', {className: 'test-action'}, [runButton, viewButton]),
            $E('td', {className: 'test-name'}, files[href]),
            statusTd
          ])
        );
      }
    },
    resizeFrame: function() {
      this.setWidth(this.frame, this.marginLeft - 70);
      //this.setWidth(window.frames[0].document.body, this.marginLeft + 60);
      this.setHeight(this.frame, this.marginTop);
      //this.setHeight(window.frames[0].document.body, this.marginTop + 60);
    },
    setWidth: function(node, marginLeft) {
      node.style.width = ((self.innerWidth || (
        document.documentElement.clientWidth || document.body.clientWidth)
      ) - marginLeft) + 'px';  
    },
    setHeight: function(node, marginTop) {    
      node.style.height = ((self.innerHeight || (
        document.documentElement.clientHeight || document.body.clientHeight)
      ) - marginTop) + 'px';    
    },          
    runAllTests: function() {
      this.runningAll = true;
      this.runAll.style.display = 'none';
      this.cancel.style.display = '';
      this.runAndRecord(this.runButtons[0]);      
      this.runNumber = 0;
    },
    stop: function() {
      this.runningAll = false;
      this.runAll.style.display = '';
      this.cancel.style.display = 'none';
    },
    runFile: function(href, title, statusTd) {
      this.frame.src = this.baseUrl + href + '?' + (+new Date);
      statusTd.innerHTML = 'RUNNING...';
      statusTd.parentNode.className = '';
      this.currentStatusTd = statusTd;        
    },
    runAndRecord: function(button) {
      var unit = window.frames[0].pulp.unit();
      unit.finish = $p.bind(this.finish, this);
      button.click();
    },    
    setResultCallback: function() {
      if (this.currentStatusTd) {
        (function(td) {
          var unit = window.frames[0].pulp.unit();
          unit.setLauncherResult = function(status) {
            td.innerHTML = status.toUpperCase();
            td.parentNode.className = status;
          };
          unit.writeSummary();
        })(this.currentStatusTd);
      }
    },
    finish: function() {
      if (this.runningAll) {
        this.runNumber++;
        if (this.runButtons[this.runNumber]) {
          this.runAndRecord(this.runButtons[this.runNumber]);
        } else {
          this.stop();
          this.runNumber = 0;
        }
      }
    },
    viewFile: function(href, title) {
      var request = getHttpRequest();
      request.open('GET', href, true);
      request.onreadystatechange = function() {
        var doc = window.frames[0].document;
        var html = '';
        html += '<html><head>' +
          '<link rel="stylesheet" type="text/css" media="all" href="../js/pulp.unit.launcher/highlight/styles/idea.css" />';
        html += '<script type="text/javascript" src="../js/pulp.unit.launcher/highlight/highlight.js"></script>' +
          '<script type="text/javascript">' + 
          'hljs.initHighlightingOnLoad("html");' +
          '</script></head><body>';
        html += '<h1>Source of ' + title + ' (' + href + ')</h1>';
        html += '<pre style="padding:10px"><code class="html">';
        var properIndent = request.responseText.replace(/\t/g, '  ');
        var wrapped = $p.replaceAll(properIndent, (/(<script[^>]+>)(.+?)(<\/script>)/), function(match) {
          return match[1] + '~~~Start JS~~~' + match[2] + '~~~End JS~~~' + match[3];
        }, true);
        wrapped = $p.escapeHTML(wrapped);
        wrapped = wrapped.replace(/~~~Start JS~~~/g, '</code></pre><pre><code class="js">');
        wrapped = wrapped.replace(/~~~End JS~~~/g, '</code></pre><pre><code class="html">');
        html += wrapped + '</code></pre></body></html>';
        doc.open();
        doc.write(html)
        doc.close();
        window.setTimeout(function() {
          doc.body.appendChild(doc.createElement('div'));
        }, 10);
      };
      request.send(null);
    }
  });
  
})();

pulp.Modules['unit.launcher'] = 'UnitLauncher';