// TODO refactor this code into a UI and non-UI part.

TCPSocket = Orbited.TCPSocket;

var CHANNEL = "#hackyhack"
var IRC_SERVER = 'irc.hackyhack.net'
var IRC_PORT = 6667

var nickname = null;
var users = {};

var irc = new IRCClient();
var log = getIrcLogger("Chat");

var quitMessage = window.location.href;

var connect = function () {
  $('#signin_popup, #absorb_clicks').hide();
  $('#chatbox_input')
    .focus(function(){$(this).parent().addClass('focused')})
    .blur(function(){$(this).parent().removeClass('focused')})
    .focus();
  $('#chatbox_input').val('');
  $(".chatbox .chathistory").addClass('loading');
  $(".chatbox .contentpane").addClass('loading');
  $(".chatbox .user_list").addClass('loading');

  nickname = $("#nickname").val().replace(' ', '_').replace(/[^0-9a-zA-Z|_-]/, '');

  function parseName(identity) {
    // TODO remove privileges from name head.
    return identity.split("!", 1)[0];
  }
  irc.onACTION = function(command) {
      var messagediv = $('<div class="message"></div>');
      var sender = parseName(command.prefix);
      messagediv.addClass("action");
      var target = command.args[0];
      var message = command.args.slice(1).join(" ");

	//console.log(command);
      if (sender == nickname)
        messagediv.addClass("self");

      if (isSubstring(nickname, message))
        messagediv.addClass("mentioned");

      messagediv.html('<span class="user">' + sender + '</span> ' +
                      sanitize(message))
                .appendTo("#chathistory");
    scrollDown();
  }
  irc.onCTCP = function(command) {
	/*
    var messagediv = $('<div class="message"></div>');
    messagediv.addClass("ctcp");
    var message = command.args.slice(1).join(" ")
    var sender = parseName(command.prefix);
    messagediv.
       html('<span class="user">' + sender + '(CTCP):</span> ' + sanitize(message)).
       appendTo("#chathistory");    
    scrollDown();
	*/
  }
  irc.onPRIVMSG = function(command) {
    var sender = parseName(command.prefix);
    var target = command.args[0];
    var message = command.args[1];

	//console.log(command);
	
    var messagediv = $('<div class="message"></div>');

    if (message.charCodeAt(0) == 1) {
      // This is an CTCP request.

      // NB: the embedded CTCP requests is:
      //        \x01<request>[ <arg>]+\x01
      //     eg:
      //      when we type "/me waves", we receive the following message:
      //        \x01ACTION waves\x01
      //     eg:
      //      after we connect, the freenode networks sends:
      //        \x01VERSION\x01
      // See http://www.invlogic.com/irc/ctcp.html
      // NB: CTCP draft talks about quoting but it does not actually
      //      define it.
      var args = message.slice(1, message.length - 1).split(' ');
      var request = args.shift();
      if (request == "ACTION") {
        message = args.join(" ");
        messagediv.addClass("action");
      } else {
        message = request + " " + args.join(" ");
        messagediv.addClass("ctcp");
      }
    }

    if (sender == nickname)
      messagediv.addClass("self");

    if (target == nickname)
      messagediv.addClass("private");

    if (isSubstring(nickname, message))
      messagediv.addClass("mentioned");

    messagediv.html('<span class="user">' + sender + ':</span> ' +
                    sanitize(message))
      .appendTo("#chathistory");
    scrollDown();
  };
  irc.onTOPIC = function(command) {
    // See http://tools.ietf.org/html/rfc2812#section-3.2.4
    // Args: <channel> [ <topic> ]

    var channel = command.args[0];
    if (channel != CHANNEL)
      return;

    var topic = command.args[1];

    var user = parseName(command.prefix);
    $("<div class='informative topic'></div>").
        html('<span class="user">' + user + '</span> changed the topic to: ' + sanitize(topic)).
        appendTo("#chathistory");
  };
irc.onerror = function(command) {
    var responseCode = parseInt(command.type);
    if (responseCode == 431 || responseCode == 432 || responseCode == 433) {
    // 431     ERR_NONICKNAMEGIVEN
    // 432     ERR_ERRONEUSNICKNAME
    // 433     ERR_NICKNAMEINUSE
        nickname += '_'
        irc.nick(nickname)
        irc.join(CHANNEL)
    }
}


  irc.onresponse = function(command) {
    var responseCode = parseInt(command.type);
	
	//console.log(command);
    
    if (responseCode == 328) {
      //alert(command.args[2]);
	  // if($("#contentpane iframe").attr('src') != command.args[2]){
		// $("#contentpane iframe").attr('src', command.args[2]);
	  // }
    }else if (responseCode == 332) {
      // """
      // 331 RPL_NOTOPIC
      //     <target> <channel> :No topic is set
      // 332 RPL_TOPIC
      //     <target> <channel> :<topic>
      //
      // When sending a TOPIC message to determine the
      // channel topic, one of two replies is sent.  If
      // the topic is set, RPL_TOPIC is sent back else
      // RPL_NOTOPIC.
      // """ -- rfc2812

		var channel = command.args[1];
		if (channel != CHANNEL)
			return;

		var topic = command.args[2];

		$("<div class='informative topic'></div>").
			html("Channel topic is: " + sanitize(topic)).
			appendTo("#chathistory");
		
		// look for URL in topic title
		// http_pos = command.args[2].indexOf('http://');
		// if(http_pos > 0) {
			// pos2 = command.args[2].indexOf(' ', http_pos);
			// if(pos2 > 0)
				// pos2 = pos2;
			// else
				// pos2 = command.args[2].length;
			// url = command.args[2].substr(http_pos,pos2);
			// $("#contentpane iframe").attr('src', url);
		// } else {
			 $("#contentpane iframe").attr('src', 'http://irc.hackyhack.net/front.html');
		// }
	    $(".chatbox .contentpane").removeClass('loading');
		
		
    } else if (responseCode == 353) {
      // 353 is the code for RPL_NAMEREPLY.

      // The args are:
      //
      // """
      //   <target> ( "=" / "*" / "@" ) <channel>
      //    :[ "@" / "+" ] <nick> *( " " [ "@" / "+" ] <nick> )
      //
      //   - "@" is used for secret channels, 	"*" for private
      //     channels, and "=" for others (public channels).
      // """ -- rfc2812

      var channel = command.args[2];
      if (channel != CHANNEL)
        return;

      var partialUserList = command.args[3].split(' ');
      for (var i = 0, l = partialUserList.length; i < l; ++i) {
        var name = $.trim(partialUserList[i]);
        if (name == "" || users[name])
          continue;
        addName(name);
      }
    } else if (responseCode == 366) {
      // 366 is the code for RPL_ENDOFNAMES.

      fillUserList();
	  $(".chatbox .user_list").removeClass('loading');
		
      //$("<div class='informative welcome'></div>").
      //  html("Joined " + CHANNEL + " channel.").
      //  appendTo("#chathistory");
      //scrollDown();
    } else if (responseCode == 001) {
        irc.join(CHANNEL);
		irc.join('#active');
		$(".chatbox .chathistory").removeClass('loading');
        $("<div class='informative welcome'></div>").
          html("Joined channel " + CHANNEL + " as " + nickname).
          appendTo("#chathistory");
        scrollDown();
	}
	
  };


  irc.onopen = function() {
    irc.nick(nickname);
    irc.ident(nickname, '8 *', nickname);
    irc.join(CHANNEL);
    
    // Once we have joined, don't leave without warning the user
    window.onbeforeunload = function() {
      return confirm('Are you sure you want to quit and lose the chat connection?') && irc.quit(quitMessage);
    };
    $(window).unload(function() {
        hardquit();
//      quit();
    })
  }
  irc.onclose = function() {
    log.debug("closed...");  
  };
  irc.onNICK = function(command) {
    // See http://tools.ietf.org/html/rfc2812#section-3.1.2
    var previousNick = parseName(command.prefix);
    var newNick = command.args[0];
    userRenamed(previousNick, newNick);
  };
  // TODO implement onNOTICE...
  irc.onJOIN = function(command) {
    var joiner = parseName(command.prefix);
	var channel = command.args[0];
	
	if(joiner == nickname || channel == '#active') return;
	
    addName(joiner);
    fillUserList();
	
    $("<div class='informative join'></div>")
      .html("<span class='user'>" + joiner + '</span> has joined ' + CHANNEL)
      .appendTo("#chathistory");
    scrollDown();                        
  }                                      
  irc.onPART = function(command) {
    var leaver = parseName(command.prefix);
    var message = command.args.join(" ");
	var channel = command.args[0];
	
	if(channel == '#active') return;

    $("<div class='informative part'></div>")
      .html("<span class='user'>" + leaver + '</span> left ' + CHANNEL +
            (message ? ' (“' + sanitize(message) + '”)' : ''))
      .appendTo("#chathistory");         
    scrollDown();                        
                                         
    removeName(leaver);
  }

  irc.onQUIT = function(command) {       
    var quitter = parseName(command.prefix);
    var message = command.args.join(" ");

    $("<div class='informative quit'></div>")
      .html("<span class='user'>" + quitter + '</span> quit' +
            (message ? ' (“' + sanitize(message) + '”)' : ''))
      .appendTo("#chathistory");         
    scrollDown();
  
    removeName(quitter);
  }
  
  irc.connect(IRC_SERVER, IRC_PORT);
};

var chat = function () {
  msg = $('#chatbox_input').val();
  
  if(msg.substr(0,1) == '/') {
	var pos = msg.indexOf(' ') > 0 ? msg.indexOf(' ') : msg.length;
	var cmd = msg.substr(0, pos).toLowerCase();
	var args = msg.substr(pos);
	
	switch (cmd) {
		case '/me':	
			irc.action(CHANNEL, args);
			irc.onACTION({prefix:nickname,type:'ACTION',args:[CHANNEL, args]});
		break;
		case '/msg':	
			var destpos = args.indexOf(' ') > 0 ? args.indexOf(' ') : args.length;
			var destination = args.substr(0, destpos);
			var message = args.substr(destpos);
			irc.privmsg(destination, message);
			irc.onPRIVMSG({prefix:nickname,type:'PRIVMSG',args:[destination, message]});
		break;
		case '/nick':
			irc.nick(args);
		break;
		default:
			$("<div class='informative error'>"+cmd+" is not a valid command</div>")
				.appendTo("#chathistory");
			scrollDown();
		break;
	}
  } else {
	irc.privmsg(CHANNEL, msg);
	irc.onPRIVMSG({prefix:nickname,type:'PRIVMSG',args:[CHANNEL, msg]});
  }
  $('#chatbox_input').val('');
}
var hardquit = function() {
	irc.reset();
}
var quit = function () {
  // XXX sometimes the quit reason is not seen when we quit...
  //     probably because the browser is immediately closed
  //     without waiting for socket flush?
  
  irc.quit(quitMessage);
}

// TODO enable the nick change code.
// function nickChanged(newnick) {
//     userRenamed(nickname, newnick);
//     nickname = newnick;
// }

function userRenamed(oldname, newname) {
  $("<div class='informative rename'></div>")
    .html("<span class='user'>" + oldname + "</span> is now known as " +
          "<span class='user'>" + newname + "</span>")
    .appendTo("#chathistory");
  scrollDown();

  $(".user_list .user_entry#user_" + oldname)
    .attr("id", "user_" + newname)
    .html(newname);
  
  if(nickname == oldname) nickname = newname;
}

var user_priviledges = function (name) {
  var privs_symbols = {"@":"admin", "%":"subadmin", "+":"voice"};
  if (name[0] in privs_symbols) {
    return privs_symbols[name[0]];
  } else {
    return "";
  };
};

var addName = function (name) {
  // XXX this MUST remove the nick privileges from name (eg: because
  //     these prefixes are not sent when the user changed his nick,
  //     etc).
  var priviledges = user_priviledges(name);
  users[name] = {"privs":priviledges};
  $('<div class="user_entry" id="user_' + name + '">' + name + '</div>')
    .addClass(priviledges)
    .appendTo("#user_list");
};

var removeName = function (name) {
  delete users[name];
  $(".user_list #user_" + name).remove()
};

var fillUserList = function () {
	$('.user_list').empty();
	//alert("test");
	var list = [];
	for (var user in users) {
		list.push(user);
	};
	list.sort()
	for (var i=0; i < list.length; i++) {
		var user = list[i];
		privs = users[user]['privs'];
		$('<div class="user_entry" id="user_' + user + '">' + user + '</div>')
		  .addClass(privs)
		  .appendTo("#user_list");
	};
};

var scrollDown = function () {
  var box = document.getElementById('chathistory');
  box.scrollTop = box.scrollHeight;
}

var isSubstring = function (sub, str) {
  // case insensitive substring test
  return str.toLowerCase().indexOf(sub.toLowerCase()) >= 0;
};

var sanitize = (function(str) {
  // See http://bigdingus.com/2007/12/29/html-escaping-in-javascript/
  var MAP = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#39;'
  };
  var repl = function(c) { return MAP[c]; };
  return function(s) {
    s = s.replace(/[&<>'"]/g, repl);
    s = s.replace(/(http:\/\/|https:\/\/|ftp:\/\/|irc:\/\/)([^ ]*)/gi, "<a href='$1$2' target='contentpane' class='generated'>$1$2</a>");
	return s;
  };
})();

// function infoMessage(message) {
//   $("<div class='informative'></div>")
//     .html(message)
//     .appendTo("#chathistory");
//   scrollDown();
// };	
$(document).ready(function(){
	/*
	$('#text_input a.dump_url').click(function(e){
		e.preventDefault();
		url = $('#contentpane iframe').attr('src');
		$('#text_input input.text').val($(this).val() + url).focus();
	});
	*/
	$('#chatbox_input').mousemove(function(){
		$('#chatbox_input').focus();
	});
	$('#chathistory a').live('click', function(e){
		e.preventdefault
		$('#contentpane').attr('src', $(this).attr('href'));
	});
	$('.chatbox a.toggle_chat').click(function(e){
		e.preventDefault();
		$('.chatbox').toggleClass('chatdown');
		scrollDown();
	});
	
});

ENTER_KEY = 13
$(function(){
  $('#nickname').focus();
  $('#nickname').keypress( function(e) {
	var key = e.charCode || e.keyCode || 0
	if (key == ENTER_KEY) {
	  connect();
	};
  });
  $('#chatbox_input').keypress(function(e) {
	var key = e.charCode || e.keyCode || 0;
	if (key == ENTER_KEY) {
	  chat();
	  return false;
	};
  });
});
