jQuery(function($){
  $.fn.isActive = function(){
    return $(this).hasClass('active');
  };
  
  $.removeItem = function(array, item){
    return($.grep(
      array, 
      function(e){ return(e == item) },
      true
    ));
  };
  
  var guid = null;
  
  var BP = BrowserPlus;
  var BPInit = false;
  
  var assets = [];
  var remote_assets = [];
  
  var status = $('#status');
  status.unsupported = function(){
    status.text("Sorry, Yahoo! BrowserPlus is not supported on your platform");
  }
  status.connFailed = function(){
    $('#instructions').hide();
    status.addClass("error");
    status.text("Connection failed - Machsend doesn't work with your Firewall/NAT :(");
  }
  status.error = function(msg){
    $('#instructions').hide();
    status.addClass("error");
    status.text("Error: " + msg);
    throw(msg);
  }
  status.progress = {
    setup: function(name){
      status.text("Installing...");
    },
    localPercentage: function(per){
    },
    totalPercentage: function(per){
      status.text("Installing: " + per + "%");
    }
  };
  
  var bpVoid = function(res){ 
    if(res.success) return;
    status.error(res.error + ": " + res.verboseError);
  };
    
  var elementBPInstall = $('#bpInstall');
  elementBPInstall.click(function(){
    BPTool.Installer.show({}, bpRequire);
    return false;
  });
  
  $(".downloadAll").click(function(){
    if(BPInit)
      downloadAll();
    return false;
  });
  
  $('#browse').click(function(){
    if(BPInit)
      browse();
    return false;
  });
  
  var instructInstall = function(){
    $('#instructions #install').addClass('active').show();
    $('#instructions').show();
  };
  
  var instructDrag = function(){
    if(!BPInit) return;
    $('#instructions #install').hide();
    $('#instructions #step1').addClass('active');
    $('#instructions #step2').removeClass('active');
    $('#instructions').show();
  };
  
  var instructShare = function(){
    if(!BPInit) return;
    $('#instructions #step2').addClass('active');
    $('#instructions').show();
  };
  
  var GIGA_SIZE = 1073741824.0;
  var MEGA_SIZE = 1048576.0;
  var KILO_SIZE = 1024.0;
  var sizeFormat = function(size){
    if(size == 1) {
      return "1 Byte";
    } else if (size < KILO_SIZE) {
      return(size + " Bytes");
    } else if (size < MEGA_SIZE) {
      return(Math.round(size / KILO_SIZE) + " KB");
    } else if(size < GIGA_SIZE) {
      return(Math.round(size / MEGA_SIZE) + " MB");
    } else {
      return(Math.round(size / GIGA_SIZE) + " GB");
    }
  };
  
  var remoteAssetsEl    = $('#remoteAssets');
  var remoteAssetsItems = remoteAssetsEl.find('.items');

  remoteAssetsEl.hide();
  remoteAssetsItems.update(function(){
    if(remote_assets.length > 0) {
      remoteAssetsEl.show();
    } else {
      remoteAssetsEl.hide();
    }
  });
  
  remoteAssetsItems.chain({
    '.size': {
      content: function(data, el){
        return(sizeFormat(data.size));
      }
    },
    builder: function(){
      var data = this.item();
      this.find('.download').click(function(){
        download(data);
        return false;
      });
    }
  });
  
  var assetsItemsEl = $('#assets');
  var assetsItems   = assetsItemsEl.find('.items');
  
  assetsItemsEl.hide();
  assetsItems.update(function(){
    if(assets.length > 0) {
      instructShare();
      assetsItemsEl.show();
    } else {
      instructDrag();
      assetsItemsEl.hide();
    }
  });
  
  var removeItem = function(item){
    assets = $.removeItem(assets, item);
    syncAssets();
  };
  
  assetsItems.chain({
      '.size': {
        content: function(data, el){
          return(sizeFormat(data.size));
        }
      },
      builder: function(){
        var data = this.item();
        this.find('.remove').click(function(){
          removeItem(data);
          return false;
        })
      }
    }
  );
  
  var requiredServices = [
      {service: "FileBrowse"},
      {service: "FileAccess"},
      {service: "DragAndDrop"},
      {service: "Notify"},
      {service: "RubyInterpreter", version:"4"},
      {service: "Machsend"}
  ];
  var defaultDir = null;
  var downloading = [];
    
  var syncRemoteAssets = function(res){
    remote_assets = res;
    remoteAssetsItems.items('replace', remote_assets);
  };
  
  var lastThrottled;
  var throttle = function(callback) {
    if(lastThrottled) {
      var diff = lastThrottled - new Date().getTime();
      if(diff < 0.5) {
        callback = setTimeout(callback, 0.51 - diff);
      }
    }
    lastThrottled = new Date().getTime();
    callback();
  }
  
  var notify = function(msg){
    throttle(function(){ 
      BP.Notify.show({title:"Machsend", message:msg}, bpVoid);    
    });
  }
  
  var filterToFiles = function(paths){
    paths = $.grep(paths, function(p){ 
      return($.inArray("application/x-folder", p.mimeType))
    }, false);
    return paths;
  };
  
  var selectFiles = function(callback){
    BP.FileBrowse.OpenBrowseDialog({}, function(rsp){
      if(!rsp.success) return;
      paths = filterToFiles(rsp.value);
      if(paths.length == 0) return;
      callback(paths);
    });
  };
  
  var browse = function(){
    selectFiles(function(paths){
      assets = assets.concat(paths);
      syncAssets();
    });
  };
  
  var selectFolder = function(callback){
    BP.FileBrowse.OpenBrowseDialog(
      {mimeTypes:["application/x-folder"]}, 
      function(res){
        if(!res.success) return;
        var path = res.value[0];
        if(!path) return;
        callback(path);
      }
    );
  };
  
  var downloadToPath = function(asset, path){
    if($.inArray(asset, downloading) != -1) return;
    downloading.push(asset);
    var element = remoteAssetsItems.items(asset);
    element = element.find('.actions');
    element.text('Downloading...');
    var progress = function(res){
      if(!res) return;
      var perc = (res.size / res.total) * 100;
      element.text('Downloading: ' + perc.toFixed(1) + '%');
    }
    BP.Machsend.download({
      guid: asset.guid, 
      path: path, 
      progress: progress
    }, function(res){
      downloading = $.removeItem(
        downloading, asset
      );
      element.text('Downloaded');
      notify(
        "Successfully downloaded " + 
        asset.name + " to " + path.name
      );
    });
  };
  
  var isDownloading = function(){
    return(downloading.length > 0);
  }
  
  var download = function(asset){
    if(defaultDir){
      downloadToPath(asset, defaultDir);
    } else {
      selectFolder(function(path){
        downloadToPath(asset, path);
      });
    }
  };
  
  var downloadAll = function(){
    if(defaultDir){
      for (var i=0; i < remote_assets.length; i++) {
        downloadToPath(remote_assets[i], defaultDir);
      }
    } else {
      selectFolder(function(path){
        for (var i=0; i < remote_assets.length; i++) {
          downloadToPath(remote_assets[i], path);
        }
      });
    }
  };
  
  var dropHover = function(over){
    $('#main')[over ? 'addClass' : 'removeClass']('over');
  };
  
  var dropDrop = function(paths){
    assets = assets.concat(filterToFiles(paths));
    syncAssets();
  };
  
  var syncAssets = function(){
    assetsItems.items('replace', assets);
    BP.Machsend.syncAssets({assets:assets}, bpVoid);
  };
      
  var callback = function(res){
    switch(res.type) {
      case 'sync':
        syncRemoteAssets(res.value);
        break;
      case 'conn_failed':
        status.connFailed();
        break;
      default:
        throw "Unknown callback"
    } 
  };
  
  var bpLoaded = function(res){
    status.empty();
    if (res.success) {
      setupHash();
      BPInit = true;
      instructDrag();
      $(window).unload(function(){
        BP.Machsend.shutdown({}, bpVoid);
      });
      window.onbeforeunload = function(){
        if(isDownloading())
          return("A file is still downloading, " +
                 "leaving the page will cancel it.");
      }
      BP.Machsend.startup({guid:guid, callback:callback}, bpVoid);
      BP.DragAndDrop.AddDropTarget({id: 'body'}, function(){});
      BP.DragAndDrop.AttachCallbacks(
        {
          id:    'body', 
          hover: dropHover, 
          drop:  dropDrop
        }, 
        function(){}
      );
      BP.Machsend.defaultDir({}, function(res){
        defaultDir = res.value;
      });
    } else {
      status.error(res.error + ": " + res.verboseError);
    }
  };
  
  var bpRequire = function(){
    $('#instructions').hide();
    BP.require(
      {
        services: requiredServices,
        progressCallback: function(res) {
            status.progress.setup(res.name);
            status.progress.localPercentage(res.localPercentage);
            status.progress.totalPercentage(res.totalPercentage);
        }
      }, 
      bpLoaded
    );
  };
  
  var setupHash = function(){
    window.location.hash = guid;
    $('#link').val(window.location.href).change();
  };
  
  // Todo
  var generateGuid = function(){
    return($.md5(
      Date() + Math.random()
    ));
  };
  
  if(window.location.hash != ""){
    guid = window.location.hash;
    // Remove hash
    guid = guid.substring(1);
  }
  
  if(!guid){
    guid = generateGuid();
  }
  
  BP.init(function(res) {
    status.empty();
    if (res.success) {
      bpRequire();
    } else {
      if(res.error == 'bp.notInstalled'){
        instructInstall();
      } else if(res.error == 'bp.unsupportedClient') {
        status.unsupported();
      } else {
        status.error(res.error + ": " + res.verboseError);
      }
    }
  });
  
});