var gBAS = null;
const AUTH_OK = "DONE";
const RES_OK = "OK";
const RES_EQ = "EQUALS";
const RES_NEQ = "NEQUALS";
const RES_EN = "ENOUGH";
const RES_NEN = "NOTENOUGH";

// General functions

function GetWindow(WinID){
  var WindowManager = Components.classes['@mozilla.org/appshell/window-mediator;1'].
                                 getService(Components.interfaces.nsIWindowMediator);
  return WindowManager.getMostRecentWindow(WinID);
}

function Sleep(miliseconds){
  var thread = Components.classes["@mozilla.org/thread;1"].createInstance();
  thread = thread.QueryInterface(Components.interfaces.nsIThread);
  thread.currentThread.sleep(miliseconds);
}

function MD5(str){

  function toHexString(charCode){
    return ("0" + charCode.toString(16)).slice(-2);
  }

  var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].
                             createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
  converter.charset = "UTF-8";
  var result = {};

  var data = converter.convertToByteArray(str, result);
  var ch = Components.classes["@mozilla.org/security/hash;1"]
                     .createInstance(Components.interfaces.nsICryptoHash);
  ch.init(ch.MD5);
  ch.update(data, data.length);
  var hash = ch.finish(false);

  // convert the binary hash data to a hex string.
  return [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
}

function CleanBody(str){
  var f = str.toUpperCase().indexOf("<HTML>");
  var t = str.toUpperCase().lastIndexOf("<\HTML>") + 6;
  if ((f == -1) || (t == -1)){
    alert("Message body in invalid format!");
    alert(str);
  }
  return str.substring(f, t);
}

// BAS functions

function BASLog(str){
  var WindowManager = Components.classes['@mozilla.org/appshell/window-mediator;1'].
                                getService(Components.interfaces.nsIWindowMediator);
  var LogWindow = WindowManager.getMostRecentWindow("Log:Window");
  var Text = LogWindow.document.getElementById("logtext");
  Text.value = Text.value + str;
}

function BASObjectLog(filename, obj){
  var file = Components.classes["@mozilla.org/file/local;1"].
                      createInstance(Components.interfaces.nsILocalFile);
  file.initWithPath(filename);
  var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"].
                         createInstance(Components.interfaces.nsIFileOutputStream);
  foStream.init(file, 0x02 | 0x08, 0666, 0);
  for(var i in obj){
    var text = i + " : " + obj[i] + "\r\n";
    foStream.write(text, text.length);
  }
  foStream.close();
}

function BASEnabled(){
  var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
  var BasEnabled = false;
  var GpgPath = "";
  try{
    BasEnabled = prefs.getBoolPref("extensions.bas.enabled");
  }
  catch(ex){  }
  return BasEnabled;
}

function BASInit(){
  gBAS = Components.classes["@mydomain.com/XPCOMSample/MyComponent;1"].createInstance()
                   .QueryInterface(Components.interfaces.IMyComponent);
}

function BASSetPGP(){
  var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
  var GpgPath = "";
  try{
    GpgPath = prefs.getCharPref("extensions.bas.gpgpath");
  }
  catch(ex){  }
  gBAS.SetPGP(GpgPath);
}

function BASTryConnect(){
  var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
  var Address = "";
  var Port = "2222";
  try{
    Address = prefs.getCharPref("extensions.bas.address");
  }
  catch(ex){  }
  try{
    Port = prefs.getCharPref("extensions.bas.port");
  }
  catch(ex){  }
  try{
    gBAS.Connect(Address, Port);  
  }
  catch(ex){
    return false;
  }
  return true;
}

function BASDisconnect(){
  gBAS.Disconnect();
}

function BASAuth(user){
  gBAS.Auth('"'+user+'"');
}

function BASLogin(user){
  gBAS.Login('"'+user+'"');
}

function BASGetMessage(sync){
  var data = "";
  var times = 0;
  function AsyncRead(){
    data+=gBAS.Read();
    times++;
    if((times<5) && (data.match(/\$\r\n$/)==null)){
      setTimeout(AsyncRead, 1000);
    } else {
      var plaintext = gBAS.Decrypt(data.substring(0, data.length-3));
      var servernonce = plaintext.slice(plaintext.lastIndexOf(" ")+1);
      var part = plaintext.substring(0, plaintext.lastIndexOf(" "));
      var usernonce = plaintext.substring(part.lastIndexOf(" ")+1, plaintext.lastIndexOf(" "));
      var msg = part.substring(0, part.lastIndexOf(" "));
      gBAS.SetNonce(usernonce, servernonce);
      sync.send(msg);
    }
  }
  setTimeout(AsyncRead, 0);
}

function BASRequestCredits(){
  gBAS.RequestCredits();
}

function BASEnoughCredits(to){
  gBAS.EnoughCredits('"'+to+'"');
}

function BASSendStamp(to, stamp){
  gBAS.SendStamp('"'+to+'"', stamp);
}

function BASCheckStamp(from, stamp){
  gBAS.CheckStamp('"'+from+'"', stamp);
}

// wrappers

var WaitForConnectionAsync;
function WaitForConnection(sync){
  function Allow(){
    if(gBAS.Connected()==true){
      setTimeout(Allow, 500);
    } else {
      sync.send();  
    }
  }
  setTimeout(Allow, 0);
}

var LoginAsync;         
function Login(sync, user){
  var self = LoginAsync;
  BASAuth(user);
  BASGetMessage(self);
  var result = yield;
  if(result!=AUTH_OK){
    sync.send(false);
  }
  BASLogin(user);
  BASGetMessage(self);
  var result = yield;
  if(result==RES_OK){
    sync.send(true);
  } else {
    sync.send(false);
  }
}

var SendStampAsync;
function SendStamp(from, to, stamp){
  var self = SendStampAsync;
  WaitForConnectionAsync = WaitForConnection(self);
  yield;
  BASTryConnect();
  LoginAsync = Login(self, from);
  LoginAsync.next();
  var res = yield;
  if(res){
    BASSendStamp(to, stamp);
    GetCreditsAsync = GetCredits(self);
    GetCreditsAsync.next();
    var credits = yield;
    var item = document.getElementById("button-credits");
    item.label = credits;
  }
  BASDisconnect();
}

var CheckStampAsync;
function CheckStamp(sync, from, to, stamp){
  var self = CheckStampAsync;
  var ret = false;
  WaitForConnectionAsync = WaitForConnection(self);
  yield;
  if(!BASTryConnect()){
    alert("Failed to connect to BAS-Server!");
    yield;
  }
  LoginAsync = Login(self, to);
  LoginAsync.next();
  var res = yield;
  if(res){
    BASCheckStamp(from, stamp);
    BASGetMessage(self);
    var res2 = yield;
    if(res2 == RES_EQ){
      ret = true;	
      GetCreditsAsync = GetCredits(self);
      GetCreditsAsync.next();
      var credits = yield;
      var item = document.getElementById("button-credits");
      item.label = credits;
    }
  }
  BASDisconnect();
  sync.send(ret);
}

var EnoughCreditsAsync;
function EnoughCredits(sync, from, to){
  var self = EnoughCreditsAsync;
  var ret = false;
  WaitForConnectionAsync = WaitForConnection(self);
  yield;
  if(!BASTryConnect()){
    alert("Failed to connect to BAS-Server!");
    yield;
  }
  LoginAsync = Login(self, from);
  LoginAsync.next();
  var res = yield;
  if(res){
    BASEnoughCredits(to);
    BASGetMessage(self);
    var res2 = yield;
    if(res2 == RES_EN){
      ret = true;	
    }
  }
  BASDisconnect();
  sync.send(ret);
}


var GetCreditsAsync;
function GetCredits(sync){
  var self = GetCreditsAsync;
  BASRequestCredits();
  BASGetMessage(self);
  var credits = yield;
  sync.send(credits);
}

var UpdateCreditsAsync; 
function UpdateCredits(user){  
  var self = UpdateCreditsAsync;
  WaitForConnectionAsync = WaitForConnection(self);
  yield;
  if (!BASTryConnect()){ 
    var item = document.getElementById("button-credits");
    item.label = "?";
    yield;
  }
  //BASLog("success\n");
  LoginAsync = Login(self, user);
  LoginAsync.next();
  var res = yield;
  if(res){
    GetCreditsAsync = GetCredits(self);
    GetCreditsAsync.next();
    var credits = yield;
    var item = document.getElementById("button-credits");
    item.label = credits;
  }
  BASDisconnect();
}

// void main()

BASInit();
BASSetPGP();