function chatBuddy() {
  //public properties //
  this.user_id      = null; 
  this.username     = null;
  this.online       = null;
  this.dialog_id    = 0;
  this.messages         = [];
  this.anchor   = null;
}

function shortenUrl(url){
	return url;
}

chatBuddy.prototype  = {
    
  setAnchor:  function(anchor) {
    this.anchor  = anchor;
  },
    
  setUserId:  function(id) {
    this.user_id  = id;
  },
  
  setDialogId:  function(id) {
    this.dialog_id  = id;
  },
  
  setUsername:  function(username) {
    this.username  = username;
  },
  
  setOnline:  function(bool) {
    this.online  = bool;
  },
  
  setMessages:  function(messages){
    this.messages   = messages;
  },
  
  addMessage:  function(message){
    this.messages.push(message);
  },
  
  addMessages:  function(messages){
    for(var i in messages) {
      this.addMessage(messages[i]);
    }
  },
  
  getUserId:  function() {
    return this.user_id;
  },
  
  getDialogId:  function() {
    return this.dialog_id;
  },
  
  getUsername:  function() {
    return this.username;
  },
  
  isOnline:  function() {
    return this.online;
  },
  
  getMessages:  function(){
    return this.messages;
  },
  
  getAnchor:  function(){
    return this.anchor;
  }
};

/*===================================*/

var chat  = {
  maxMessagesCount: 20,
  maxMessageLength: 255,
  
  smilesDir:  "/",
  smilesMap:  [{from: ":-) :)", to: "smile.png"}, {from: "8-) 8)", to: "cool.png"}, {from: ":-d :d :-D :D", to: "grin.png"},
                  {from: ":-( :(", to: "sad.png"}, {from: ":-o :o :-O :O", to: "surprise.png"}, {from: ":p :-p :-P :P", to: "tongue.png"},
                  {from: ";-) ;)", to: "wink.png"}, {from: "??? o.O", to: "confused.png"}, {from: ":'(", to: "cry.png"},
                  {from: ">:", to: "doh.png"}, {from: ":-/ :-\\", to: "downer.png"}, {from: "3-)", to: "devil.png"}],
  
  buddies:   [],
  buddies_loaded:   0,
  
  // load buddies ch
  buddies_ajax_handler:  null,
  dialogs_ajax_handler:  null,
  messages_ajax_handler:  null,
  
                  
  //routes //
  switch_chat_mode_url:  null,
  get_buddies_url:  null,
  get_new_messages_url: null,
  get_new_dialogs_url:  null,
  save_message_url: null,
  
  main_bar_minimized:    true,
  chat_dialog_minimized: true,
  chat_dialog_closed:    true,
  chat_status_offline:   false,
  
  
    
  setUp:    function(){
    //show/hide main chat window
    $("#main_chat_panel_heading_text").click(function(){
      chat.setMainBarMinimized(!chat.isMainBarMinimized());
    });
  
    // switch chat mode (online/offline)
    $("#switch_chat_mode").click(function(){
      $.ajax({
        url: chat.switch_chat_mode_url,
        cache: false,
        dataType: "json",
        data: {"chat_status": (chat.isChatOffline() ? 1 : 0)},
        success: function(response){
          chat.setChatOffline(!parseInt(response.status));
        }
      });
    });
    
    // send chat message
    $("#chat_dialog_input").keyup(function(e){
      var text = $(this).val();
      if(text.length > chat.maxMessageLength) {
        $(this).val(text.substr(0, chat.maxMessageLength));
      }
    });
    
    
  
    // send chat message
    $("#chat_dialog_input").keypress(function(e){
      if( e.keyCode == 13) {
        var text = $(this).val();
        if ( text.length ) {
          chatDialog.sendMessage(text);
          chat.clearChatDialogInput();
        }
       }
    });
  
    // hide chat dialog
    $("#hide_chat_dialog").click(function(){
      chat.setChatDialogMinimized(!chat.isChatDialogMinimized());
    });
  
    // completely close chat dialog
    $("#close_chat_dialog").click(function(){
      chat.setChatDialogClosed(true);
    });
    
  },
  
  setMainBarMinimized:  function(minimize) {
    if ( minimize) { 
      $("#main_chat_panel").css('height', ''); 
      $("#main_chat_panel_users_list").hide();
    } else {
      $("#main_chat_panel").css('height', '200px');
      $("#main_chat_panel_users_list").show();
    }
    
    this.main_bar_minimized  = minimize;
  },
  
  setChatDialogMinimized:  function(minimize) {
    var elem = $("#chat_dialog_messages");
    if (minimize) {
      elem.hide();
    } else {
      elem.show();
      elem.attr({'scrollTop': elem.attr('scrollHeight') });
    }
    
    this.chat_dialog_minimized  = minimize;
  },
  
  setChatDialogClosed:  function(close) {
    if (close) {
      chat.setChatDialogMinimized(true);
      $("#chat_dialog_box").hide();
    } else {
      chat.setChatDialogMinimized(false);
      $("#chat_dialog_box").show();
    }
    
    this.chat_dialog_closed  = close;
  },
  
  setChatDialogTitle:  function(title) {
    $("#chat_dialog_box_heading_text").text(title);
  },
  
  clearChatDialogInput:  function() {
    $("#chat_dialog_input").val("");
  },
  
  setChatOffline:  function(offline) {
    if ( offline) {
      clearInterval(this.dialogs_ajax_handler);
      clearInterval(this.messages_ajax_handler);
      clearInterval(this.buddies_ajax_handler);
      
      for(var i = 0, n = this.buddies.length; i < n; i++) {
        $($(this.buddies[i].getAnchor()).parent()).remove();
      }
      
      this.buddies_loaded       = 0;
      this.buddies              = [];
      
      $("#switch_chat_mode").text('Status: Off');
    } else {
      // load biddies each 10 sec.
      this.loadBuddies();
      this.buddies_ajax_handler = setInterval(this.loadBuddies, 10000);
      this.dialogs_ajax_handler = setInterval(this.loadNewDialogs, 8000);
      this.messages_ajax_handler = setInterval(this.loadNewMessages, 5000);
      $("#switch_chat_mode").text('Status: On');
    }
    
    this.chat_status_offline  = offline;
  },
  
  setChatModePostUrl:  function(url) {
    this.switch_chat_mode_url  = url;
  },
  
  setGetFriendsPostUrl:  function(url) {
    this.get_buddies_url  = url;
  },
  
  setNewMessagesPostUrl: function(url) {
    this.get_new_messages_url  = url;
  },
  
  setGetDialogsPostUrl: function(url) {
    this.get_new_dialogs_url  = url;
  },
  
  setSendMessagePostUrl:  function(url) {
    this.save_message_url  = url;
  },
  
  setSmileysDir:  function(dir){
    this.smilesDir   = dir;
  },
  
  setMaxMessageLength:  function(len) {
    this.maxMessageLength = len;
  },
  
  isMainBarMinimized:  function() {
    return this.main_bar_minimized;
  },
  
  isChatDialogMinimized:  function() {
    return this.chat_dialog_minimized;
  },
  
  isChatDialogClosed:  function() {
    return this.chat_dialog_closed;
  },
  
  isChatOffline:  function() {
    return this.chat_status_offline;
  },
 
  getSmileysDir:  function(){
    return this.smilesDir;
  },
  
  
  loadBuddies:  function() {
    $.ajax({
      url:    chat.get_buddies_url,
      data:   {buddies_loaded: chat.buddies_loaded},
      dataType: "json",
      success: function(data){
        if (data.length) {
          $("#main_chat_panel_no_users_online").hide();
        }
        
        // find offline users
        for (var i in chat.buddies) {
          var curr_buddy  = chat.buddies[i];
          var user_offline_now = true;
          for (var j in data) {
            var new_buddy  = data[j]; 
            if(curr_buddy.getUserId() == new_buddy.user_id) {
              user_offline_now  = false;
              break;
            }
          }
          
          // user signed off
          if(curr_buddy.isOnline() && user_offline_now) {
            $(curr_buddy).trigger({
              type: 'signed_off'
            });
          } else if(!curr_buddy.isOnline() && !user_offline_now ) { // user signed on 
            $(curr_buddy).trigger({
              type: 'signed_on'
            });
          }
          
        }
        
        // find new users
        for (var j in data) {
          var new_buddy  = data[j];
          var not_in_list = true;
          for (var i in chat.buddies) {
            var curr_buddy  = chat.buddies[i];
            if(curr_buddy.getUserId() == new_buddy.user_id) {
              not_in_list  = false;
              break;
            }
          }

          if(not_in_list) {
            
            /*if(chatDialog.getBuddy() && chatDialog.getBuddy().getUserId() == new_buddy.user_id) {
              //chatDialog.setBuddy(new_buddy);
              //chatDialog.showDialogMessages(new_buddy.messages);
              alert("ddd");
            }*/
            
            chat.addBuddy(new_buddy);
          }
        }
        
        chat.buddies_loaded     = 1;
      }
    });
  },
  
  loadNewDialogs:  function(){
    var do_ajax = false;
    for(var i = 0, n = chat.buddies.length, chat_buddy; i < n, chat_buddy = chat.buddies[i]; i++) {
      if(chat_buddy.isOnline()) {
        do_ajax = true;
        break;
      }
    }
    
    if ( do_ajax) {
      $.ajax({
        url: chat.get_new_dialogs_url,
        cache: false,
        dataType: "json",
        success: function(data){
          for(var i = 0, n = data.length, dialog; i < n, dialog = data[i]; i++) {
            for(var j = 0, k = data.length, chat_buddy; j < k, chat_buddy = chat.buddies[j]; j++) {
              if(dialog.from_user == chat_buddy.getUserId()) {
                
                chat_buddy.setDialogId(dialog.dialog_id);
                // new message trigger
                $(chat_buddy).trigger({
                  type: 'new_message',
                  messages: dialog.messages
                });
                
              }
            } 
          }
        }
      });
    }
  },
  
  loadNewMessages:  function(){
    var dialog_offsets = {};
    var do_ajax = false;
    for(var i = 0, n = chat.buddies.length, chat_buddy; i < n, chat_buddy = chat.buddies[i]; i++) {
      var messages = chat_buddy.getMessages();
      if(chat_buddy.isOnline() && messages.length ) {
        // find max message id
        var max_message_id = 0;
        for(var m = 0, l = messages.length; m < l, message = messages[m]; m++) {
          //console.log("max id: " + message.id);
          if(!message.error && message.id > max_message_id ) {
            max_message_id    = message.id;
          }
        }
        
        dialog_offsets["did_" + chat_buddy.getDialogId()] = max_message_id;
        do_ajax  = true;
      }
    }
    
    if (do_ajax) {
      $.ajax({
        url: chat.get_new_messages_url,
        cache: false,
        data: dialog_offsets,
        dataType: "json",
        success: function(data){
          // add new messages
          var update_decorators = false;
          for(var i = 0, n = data.length, dialog; i < n, dialog = data[i]; i++) {
            for(var j = 0, k = chat.buddies.length, chat_buddy; j < k, chat_buddy = chat.buddies[j]; j++) {
              if(dialog.from_user == chat_buddy.getUserId()) {
                
                // new message trigger
                $(chat_buddy).trigger({
                  type: 'new_message',
                  messages: dialog.messages
                });
                
              }
            }
          }
        }
      });
    }
  },
  
  addBuddy:   function(data){
    
    var user = new chatBuddy();
    user.setUserId(data.user_id);
    user.setDialogId(data.dialog_id);
    user.setUsername(data.username);
    user.setOnline(data.online);
    user.addMessages(data.messages);
    
    var atag   = document.createElement("a");
    atag.href = "javascript: void(0);";
    atag.innerHTML = data.username;
    
    user.setAnchor( atag );
    

    $(atag).bind('click', function(){
      chatDialog.open(user);
    });
    
    $(user).bind('new_message', function(e){
      if(chat.isChatDialogMinimized() || !chatDialog.getBuddy() || (chatDialog.getBuddy() && chatDialog.getBuddy().getUserId() != this.getUserId())) {
        $(this.getAnchor()).addClass('has_new_message');
      }
      
      var buddy_messages = this.getMessages();
      var new_messages = [];
      for(var i = 0, n = e.messages.length; i < n; i++) {
        var is_new_message = true;
        for(var j = 0, k = buddy_messages.length; j < k; j++) {
          if(e.messages[i].id == buddy_messages[j].id ) {
            is_new_message = false;
            break;
          }
        }
        
        if(is_new_message ) {
          new_messages.push(e.messages[i]);
        }
      }
      
      if(chatDialog.getBuddy() && chatDialog.getBuddy().getUserId() == this.getUserId()) {
        chatDialog.appendDialogMessages(new_messages);
      }
      
      this.addMessages(new_messages);
      chat.setMainBarMinimized(false);
    });
    
    $(user).bind('signed_on', function(e){
      $(this.getAnchor()).removeClass('offline');
      
      var notice =  this.getUsername() + ' signed on.';
      var message = {
        error:      true,
        message:    notice
      };
      
      if(chatDialog.getBuddy() && chatDialog.getBuddy().getUserId() == this.getUserId()) {
        chatDialog.appendDialogMessage(message);
      }
      
      this.addMessage(message);
      this.setOnline(true);
    });
    
    $(user).bind('signed_off', function(e){
      $(this.getAnchor()).addClass('offline');
      
      var notice =  this.getUsername() + ' signed off.';
      var message = {
        error:      true,
        message:    notice
      };
      
      if(chatDialog.getBuddy() && chatDialog.getBuddy().getUserId() == this.getUserId()) {
        chatDialog.appendDialogMessage(message);
      }
      
      this.addMessage(message);
      this.setOnline(false);
    });
    
    
    if(chatDialog.getBuddy() && chatDialog.getBuddy().getUserId() == user.getUserId()) {
      chatDialog.setBuddy(user);
      //chatDialog.showDialogMessages(new_buddy.messages);
    }
    
    this.buddies.push(user);
    
    
    
    var li = document.createElement("li");
    li.appendChild(atag);
    
    $("#main_chat_panel_users_list ul").append(li);
  },
  
  preParseMessage:  function(text){
    text    = text.substr(0, chat.maxMessageLength);
    text    = text.replace(/<([^>]*)>/g, "&lt;$1&gt;");
    return text;
  },
  
  postParseMessage:  function(text){
    var retString = '';
    var link_max_len = 20;
    
    // parse links
    //text = text.replace(/((https?:\/\/)?(www\.)?(([a-z0-9-]+\.)+[a-z]{2,6})(\/\S+|\/)*)/ig, '<a href="$1" target="_blank">' + shortenUrl('$1') + '</a>');
    var link_reg	= /(\s|^)((https?:\/\/|https?:\/\/www\.|www\.)(([a-z0-9-]+\.)+[a-z]{2,6})(\/\S+|\/)*)/ig;
	var match = link_reg.exec(text);
	while(match != null) {
		var raw_url = match[0];
		var urlnospace = raw_url.replace(/(^\s)/, ''); 
	
		if(urlnospace.length > link_max_len) {
			text	= text.replace(urlnospace, '<a href="'+urlnospace+'" target="_blank">'+urlnospace.substring(0, link_max_len)+'...</a>');
		} else {
			text	= text.replace(urlnospace, '<a href="'+urlnospace+'" target="_blank">'+urlnospace+'</a>');
		}
		
		match = link_reg.exec(text);
	}
    
    // parse smiles
    var dir = chat.getSmileysDir();
    	
    for(var j = 0, ln = text.length; j < ln; j++) {
      for(var k in chat.smilesMap) {
    	   
        var item = chat.smilesMap[k];
        
        var smiles = item.from.split(" ");
        for(var i = 0, n = smiles.length; i < n; i++) {
          var smile = smiles[i];
          if( text.substr(j, smile.length) == smile && text.substr(j-4, 4) != 'http' && text.substr(j-5, 5) != 'https') {
            retString += '<img src="'+dir+'/'+item.to+'" />';
            j += smile.length;
          }
        }
        
      }

      //if (j < text.length ) {
    	//alert(text.substring(j, 1));
        retString += text.substring(j, j+1);
      //}
    }
    //alert(retString);
    //alert(text);
    //return text;
    return retString;
  },
  
  _wordwrap:   function(str, int_width, str_break, cut) {
    // from http://phpjs.org/functions/wordwrap:581  
    var m = ((arguments.length >= 2) ? arguments[1] : 75   );
    var b = ((arguments.length >= 3) ? arguments[2] : "\n" );    
    var c = ((arguments.length >= 4) ? arguments[3] : false);
 
    var i, j, l, s, r;
 
    str += ''; 
    if (m < 1) {
        return str;
    }
    
    for (i = -1, l = (r = str.split(/\r\n|\n|\r/)).length; ++i < l; r[i] += s) {
      for (s = r[i], r[i] = ""; s.length > m; r[i] += s.slice(0, j) + ((s = s.slice(j)).length ? b : "")){
          j = c == 2 || (j = s.slice(0, m + 1).match(/\S*(\s)?$/))[1] ? m : j.input.length - j[0].length || c == 1 && m || j.input.length + (j = s.slice(m).match(/^\S*/)).input.length;
      }
    }    
    return r.join("\n");
  }
  
};


/*===================================*/
var chatDialog = {
  buddy: null,
  
  setBuddy:  function(buddy){
    this.buddy  = buddy;
  },
  
  getBuddy:  function(){
    return this.buddy;
  },
  
  open:  function(buddy) {
    chatDialog.setBuddy(buddy);
    chat.clearChatDialogInput();
    chat.setChatDialogTitle(buddy.getUsername());
    chat.setChatDialogClosed(false);
    
    // show message list
    chatDialog.showDialogMessages(buddy.getMessages());
    $(buddy.getAnchor()).removeClass('has_new_message');
  },
  
  sendMessage:  function(text){
    // post data
    var dialog_buddy  = chatDialog.getBuddy();
    var postData    = {
      to_user:   dialog_buddy.getUserId(),
      dialog_id: dialog_buddy.getDialogId(),
      message:   chat._wordwrap(chat.preParseMessage(text), 20, "\n", true)
    };
    
    if(dialog_buddy && dialog_buddy.isOnline() && !chat.isChatOffline()){
      $.ajax({
        url: chat.save_message_url,
        cache: false,
        data: postData,
        type: "POST",
        dataType: "json",
        success: function(data){
          // log message to buddy list
          if(data.error) {
            var message = {
              error:   parseInt(data.error),
              message:  data.message
            };
              
            // new message trigger
            $(dialog_buddy).trigger({
              type: 'new_message',
              messages: [message]
            });
            
          } else {
            var message = {
              id:   data.id,
              from_user:  data.from_user,
              from_username:  data.from_username,
              to_user:  data.to_user,
              time:     data.time,
              message:  data.message
            };
            
            // new message trigger
            $(dialog_buddy).trigger({
              type: 'new_message',
              messages: [message]
            });
            
            // update dialog id
            dialog_buddy.setDialogId(data.dialog_id);
          }
        },
        error: function(){
          var message = {
            error:    true,
            message: 'An error occured during sending request.'
          };
          
          // new message trigger
          $(dialog_buddy).trigger({
            type: 'new_message',
            messages: [message]
          });
        }
      });
    }
  },
  
  showDialogMessages:  function(messages){
    $("#chat_dialog_messages").empty();
    chatDialog.appendDialogMessages(messages);
  },
  
  appendDialogMessage:  function(message){
    var target_elem = $("#chat_dialog_messages");
    target_elem.append(chatDialog.renderDialogMessage(message));
    target_elem.attr({scrollTop: target_elem.attr('scrollHeight')});
  },
  
  appendDialogMessages:  function(messages){
    var target_elem = $("#chat_dialog_messages");
    var html = '';
    for(var i = 0, n = messages.length; i < n; i++) {
      html  += chatDialog.renderDialogMessage(messages[i]);
    }
    
    target_elem.append(html);
    target_elem.attr({scrollTop: target_elem.attr('scrollHeight')});
  },
  
  renderDialogMessage:  function(message){
    var retHtml = '';
    if ( message.error ) { // error
      retHtml = '<div class="error_message">'+message.message+'</div>';
    } else { // user message
      retHtml += '<div class="message"><span class="time">'+message.time+'</span> <span class="username">'+message.from_username+'</span>: '+chat.postParseMessage(message.message)+'</div>';
    }
    
    return retHtml;
  }
  
  
};


