/**
 * Forge XmlHttpRequest Test
 *
 * @author Dave Longley
 * @author David I. Lehn <dlehn@digitalbazaar.com>
 *
 * Copyright (c) 2009-2010 Digital Bazaar, Inc. All rights reserved.
 */
(function($)
{
   // load flash socket pool
   window.forge.socketPool = {};
   window.forge.socketPool.ready = function()
   {
      // init forge xhr
      forge.xhr.init({
         flashId: 'socketPool',
         policyPort: 19945,
         msie: $.browser.msie,
         connections: 10,
         caCerts: [],
         verify: function(c, verified, depth, certs)
         {
            // don't care about cert verification for test
            return true;
         }
      });
   };
   swfobject.embedSWF(
      'forge/SocketPool.swf', 'socketPool', '0', '0', '9.0.0',
      false, {}, {allowscriptaccess: 'always'}, {});
})(jQuery);

jQuery(function($)
{
   var cat = 'forge.tests.xhr';

   var tests = [];
   var passed = 0;
   var failed = 0;
   
   var init = function() {
      passed = failed = 0;
      $('.ready,.testing,.pass,.fail')
         .removeClass('ready testing pass fail');
      $('#status')
         .text('Ready.')
         .addClass('ready');
      $('#total').text(tests.length);
      $('#pass').text(passed);
      $('#fail').text(failed);
      $('.expect').empty();
      $('.result').empty();
      $('.time').empty();
      $('.timePer').empty();
      $('#start').removeAttr('disabled');
   };

   var start = function()
   {
      $('#start').attr('disabled', 'disabled');
      // meta! use tasks to run the task tests
      forge.task.start({
         type: 'test',
         run: function(task) {
            task.next('starting', function(task) {
               forge.log.debug(cat, 'start');
               $('#status')
                  .text('Testing...')
                  .addClass('testing')
                  .removeClass('idle');
            });
            $.each(tests, function(i, test) {
               task.next('test', function(task) {
                  var title = $('li:first', test.container);
                  if($('#scroll:checked').length === 1)
                  {
                     $('html,body').animate({scrollTop: title.offset().top});
                  }
                  title.addClass('testing');
                  test.run(task, test);
               });
               task.next('test', function(task) {
                  $('li:first', test.container).removeClass('testing');
               });
            });
            task.next('success', function(task) {
               forge.log.debug(cat, 'done');
               if(failed === 0) {
                  $('#status')
                     .text('PASS')
                     .addClass('pass')
                     .removeClass('testing');
               } else {
                  // FIXME: should just be hitting failure() below
                  $('#status')
                     .text('FAIL')
                     .addClass('fail')
                     .removeClass('testing');
               }
            });
         },
         failure: function() {
            $('#status')
               .text('FAIL')
               .addClass('fail')
               .removeClass('testing');
         }
      });
   };

   $('#start').click(function() {
      start();
   });
   
   $('#reset').click(function() {
      init();
   });
   
   var stressStats = 
   {
      sent: 0,
      success: 0,
      error: 0
   };
   var stressStatsMessage = function()
   {
      return 'received:' + (stressStats.success + stressStats.error) + '/' +
         stressStats.sent + ' errors:' + stressStats.error;
   };
   
   $('#stress').click(function() {
      for(var i = 1; i <= 100; ++i)
      {
         (function(seqnum)
         {
            setTimeout(function()
            {
               $.ajax(
               {
                  type: 'GET',
                  url: '/result.txt?seq=' + seqnum,
                  beforeSend: function(xhr)
                  {
                     ++stressStats.sent;
                     xhr.setRequestHeader('Connection', 'close');
                  },
                  success: function(data, textStatus, xhr)
                  {
                     ++stressStats.success;
                     forge.log.debug(cat, 'xhr connection completed' +
                        ' seq:' + seqnum +
                        ' datalen:' + data.length + ' ' +
                        stressStatsMessage());
                  },
                  error: function(xhr, textStatus, errorThrown)
                  {
                     ++stressStats.error;
                     forge.log.error(cat, 'xhr connection failed' +
                        ' seq:' + seqnum + ' ' +
                        stressStatsMessage(), arguments);
                  },
                  xhr: forge.xhr.create
               });
            }, 0);
         })(i);
      }
      return false;
   });
   
   /**
    * Creates a simple XMLHttpRequest wrapper. For testing.
    */
   var createWrapper = function()
   {
      var UNSENT = 0;
      var OPENED = 1;
      var HEADERS_RECEIVED = 2;
      var LOADING = 3;
      var DONE = 4;
      
      var toWrap = new XMLHttpRequest();
      
      // create xhr wrapper object
      var xhr =
      {
         // FIXME: an EventListener
         onreadystatechange: null,
         // FIXME: readonly
         readyState: UNSENT,
         // FIXME: a string
         responseText: null,
         // FIXME: a document
         responseXML: null,
         // FIXME: readonly, returns the HTTP status code
         status: 0,
         // FIXME: readonly, returns the HTTP status message
         statusText: null,
         
         // FIXME: async, user, and password are optional
         open: function(method, url, async, user, password)
         {
            toWrap.open(method, url, async, user, password);
         },
         
         setRequestHeader: function(header, value)
         {
            toWrap.setRequestHeader(header, value);
         },
         
         // FIXME: data can be a string or a document
         send: function(data)
         {
            toWrap.send(data);
         },
         
         abort: function()
         {
            toWrap.abort();
            toWrap.onreadystatechange = null;
            toWrap = null;
         },
         
         // FIXME: return all response headers as a string
         getAllResponseHeaders: function()
         {
            return toWrap.getAllResponseHeaders();
         },
         
         // FIXME: return header field value
         getResponseHeader: function(header)
         {
            return toWrap.getResponseHeader(header);
         }
      };
      
      toWrap.onreadystatechange = function()
      {
         // copy attributes
         xhr.readyState = toWrap.readyState;
         xhr.responseText = toWrap.responseText;
         xhr.responseXML = toWrap.responseXML;
         
         if(toWrap.readyState == HEADERS_RECEIVED)
         {
            xhr.status = toWrap.status;
            xhr.statusText = toWrap.statusText;
         }
         
         if(xhr.onreadystatechange)
         {
            //forge.log.debug(cat, 'wrapper orsc', toWrap);
            xhr.onreadystatechange();
         }
      };
      
      return xhr;
   };
   
   var addTest = function(name, run)
   {
      var container = $('<ul><li>Test ' + name + '</li><ul/></ul>');
      var expect = $('<li>Expect: <span class="expect"/></li>');
      var result = $('<li>Result: <span class="result"/></li>');
      var time = $('<li>Time: <span class="time"/></li>');
      var timePer = $('<li>Time Per Iteration: <span class="timePer"/></li>');
      $('ul', container)
         .append(expect)
         .append(result)
         .append(time)
         .append(timePer);
      $('#tests').append(container);
      var test = {
         container: container,
         startTime: null,
         run: function(task, test) {
            test.startTime = new Date();
            run(task, test);
         },
         expect: $('span', expect),
         result: $('span', result),
         check: function() {
            var e = test.expect.text();
            var r = test.result.text();
            (e == r) ? test.pass() : test.fail();
         },
         pass: function(iterations) {
            var dt = new Date() - test.startTime;
            if(!iterations)
            {
               iterations = 1;
            }
            var dti = (dt / iterations);
            passed += 1;
            $('#pass').text(passed);
            $('li:first', container).addClass('pass');
            $('span.time', container).html(dt + 'ms');
            $('span.timePer', container).html(dti + 'ms');
         },
         fail: function(iterations) {
            var dt = new Date() - test.startTime;
            if(!iterations)
            {
               iterations = 1;
            }
            var dti = (dt / iterations);
            failed += 1;
            $('#fail').text(failed);
            $('li:first', container).addClass('fail');
            $('span.time', container).html(dt + 'ms');
            $('span.timePer', container).html(dti + 'ms');
         }
      };
      tests.push(test);
   };

   addTest('builtin xhr', function(task, test)
   {
      task.block();
      
      $.ajax(
      {
         type: 'GET',
         url: '/result.txt',
         success: function(data)
         {
            test.expect.html('expected result');
            test.result.html(data);
            task.unblock();
         },
         error: function()
         {
            task.fail();
         }
      });
      
      task.next(function(task)
      {
         test.pass();
      });
   });
   
   addTest('builtin xhr (10 serial)', function(task, test)
   {
      var N = 10;
      for(var i = 0; i < N; i++)
      {
         task.next(function(task)
         {
            task.parent.block();
            
            $.ajax(
            {
               type: 'GET',
               url: '/result.txt',
               success: function(data, textStatus)
               {
                  test.result.append('.');
                  task.parent.unblock();
               },
               error: function(xhr, textStatus, errorThrown)
               {
                  task.fail(N);
               }
            });
         });
      }
      
      task.next(function(task)
      {
         test.pass(N);
      });
   });
   
   addTest('builtin xhr (10 parallel)', function(task, test)
   {
      var N = 10;
      task.block(N);
      for(var i = 0; i < N; i++)
      {
         $.ajax(
         {
            type: 'GET',
            url: '/result.txt',
            success: function(data, textStatus)
            {
               test.result.append('.');
               task.unblock();
            },
            error: function(xhr, textStatus, errorThrown)
            {
               task.fail(N);
            }
         });
      }
      
      task.next(function(task)
      {
         test.pass(N);
      });
   });
   
   // test only works with non-IE
   if(!$.browser.msie)
   {
      addTest('generic wrapper xhr', function(task, test)
      {
         task.block();
         
         $.ajax(
         {
            type: 'GET',
            url: '/result.txt',
            success: function(data)
            {
               test.expect.html('expected result');
               test.result.html(data);
               task.unblock();
            },
            error: function()
            {
               task.fail();
            },
            xhr: createWrapper
         });
         
         task.next(function(task)
         {
            test.pass();
         });
      });
      
      addTest('generic wrapper xhr (10 serial)', function(task, test)
      {
         var N = 10;
         for(var i = 0; i < N; i++)
         {
            task.next(function(task)
            {
               task.parent.block();
               
               $.ajax(
               {
                  type: 'GET',
                  url: '/result.txt',
                  success: function(data, textStatus)
                  {
                     test.result.append('.');
                     task.parent.unblock();
                  },
                  error: function(xhr, textStatus, errorThrown)
                  {
                     task.fail(N);
                  },
                  xhr: createWrapper
               });
            });
         }
         
         task.next(function(task)
         {
            test.pass(N);
         });
      });
      
      addTest('generic wrapper xhr (10 parallel)', function(task, test)
      {
         var N = 10;
         task.block(N);
         for(var i = 0; i < N; i++)
         {
            $.ajax(
            {
               type: 'GET',
               url: '/result.txt',
               success: function(data, textStatus)
               {
                  test.result.append('.');
                  task.unblock();
               },
               error: function(xhr, textStatus, errorThrown)
               {
                  task.fail(N);
               },
               xhr: createWrapper
            });
         }
         
         task.next(function(task)
         {
            test.pass(N);
         });
      });
   }
   
   for(var i = 0; i < 3; i++) {
   addTest('TLS xhr ' + i, function(task, test)
   {
      task.block();
      
      $.ajax(
      {
         type: 'GET',
         url: '/result.txt',
         success: function(data, textStatus, xhr)
         {
            test.expect.html('expected result');
            test.result.html(data);
            task.unblock();
         },
         error: function(xhr, textStatus, errorThrown)
         {
            task.fail();
         },
         xhr: forge.xhr.create
      });
      
      task.next(function(task)
      {
         test.pass();
      });
   });
   }
   
   addTest('TLS xhr (10 serial)', function(task, test)
   {
      var N = 10;
      for(var i = 0; i < N; i++)
      {
         task.next(function(task)
         {
            task.parent.block();
            
            $.ajax(
            {
               type: 'GET',
               url: '/result.txt',
               success: function(data, textStatus, xhr)
               {
                  test.result.append('.');
                  task.parent.unblock();
               },
               error: function(xhr, textStatus, errorThrown)
               {
                  task.fail(N);
               },
               xhr: forge.xhr.create
            });
         });
      }
      
      task.next(function(task)
      {
         test.pass(N);
      });
   });
   
   addTest('TLS xhr (10 parallel) ' +
      '(hit "Reset" then "Start" to speed up - uses SSL session cache)',
      function(task, test)
   {
      var N = 10;
      task.block(N);
      for(var i = 0; i < N; i++)
      {
         $.ajax(
         {
            type: 'GET',
            url: '/result.txt',
            success: function(data, textStatus, xhr)
            {
               test.result.append('.');
               task.unblock();
            },
            error: function(xhr, textStatus, errorThrown)
            {
               task.fail(N);
            },
            xhr: forge.xhr.create
         });
      }
      
      task.next(function(task)
      {
         test.pass(N);
      });
   });
   
   init();
});
