/*  
 * (C)2010 Jan Bartel <barteljan@yahoo.de>
    licensed for use in the DSA browser game http://www.antamar.org

    license for other software products:
    GNU GENERAL PUBLIC LICENSE, Version 3, 29 June 2007: http://www.gnu.org/licenses/gpl.html
*/

/***
 * 
 * 
 * RECEIVE-MESSAGE-MODULE
 * 
 * Implements a Module for receiving a message
 * 
 ***/
var receiveMessageModule =
{
  newestReceivedMessageTime : 0
   
  /**
   * Request messages from the server 
   * @param {AOChat} chat
   */
  ,onRequestMessages : function(chat)
  {
    var reqMessage = {
      // The id of this chat
      chatId : chat.options.chatId
     
      //set request time to the time of the newest message
      //because we want to receive only new messages 
      ,time : chat.options.newestReceivedMessageTime
      
      // the operation delivered to the server for indicating
      // what the server has to do
      ,operation : "get_messages"
      
      // the user sending the message
      ,user : chat.options.user
    }

    // request messages from the server
    var jsonRequest = new Request.JSON( {
      url : chat.options.serverURL,
      onSuccess : chat.requestMessagesSuccessful.bind(chat),
      onFailure : chat.createError.bind(chat),
      noCache : true
    });

    // save request for debugging purpose
    chat.jsonMessagesRequest = jsonRequest;

    jsonRequest.post(reqMessage);
  }
   
  /**
   *  Save newestReceivedMessageTime if a new message was received
   * @param {Object} chat
   */
  ,onAfterRenderingMessages : function(chat)
  {
    var newMsgReceived = false;
    var playSound = true;
    var anotherAuthor = false;
    
    // bei Initialisierung des Chats keinen Sound abspielen lassen
    if(chat.options.newestReceivedMessageTime.toInt() == 0) {
      playSound = false;
    }
    
    chat.incomingMessages.each(function(message)
    {
      if(message.time.toInt() > chat.options.newestReceivedMessageTime.toInt())
      {
        chat.setOptions({newestReceivedMessageTime : message.time.toInt()});
        newMsgReceived = true;
        if(message.heldId != chat.options.heldId) {
          anotherAuthor = true;
        }
      }
    });
        
    if(newMsgReceived) {
    
      if(playSound && anotherAuthor) {
        spieleChatSound();
      }
      
      // Tooltips nachladen
      renewTips();
    }
  }
}



/***
 * 
 * 
 * SEND-MESSAGE-MODULE
 * 
 * Implements a Module for sending a message
 * 
 ***/
var sendMessageModule = 
{
  onSendMessage : function(chat) {
    
    // holen von Nachrichten unterbrechen, wird später neu gestartet
    chat.msgReloader = clearTimeout(chat.msgReloader);
    
    //create a new request send it to the server
    //call AoChats afterSendMethods after that
    var messageRequest = new Request( {
      url : chat.options.serverURL,
      onSuccess : chat.afterSendMessage.bind(chat),
      onFailure : chat.afterSendMessage.bind(chat),
      onException : chat.afterSendMessage.bind(chat),
      onCancel : chat.afterSendMessage.bind(chat)
    });
    messageRequest.post(chat.message);
  }
  
  , onAfterMessageSend : function(chat) {
    // Nachrichten neu holen
    chat.msgReloader = clearTimeout(chat.msgReloader);
    chat.requestMessages();
  }
}

/***
 * 
 * 
 * MESSAGE-OUTPUT-AREA-MODULE
 * 
 * Module for rendering the messages in an outputArea
 * 
 ***/
var messageOutputAreaModule = 
{
  //create a new default option
  //if this option contains a string
  //then it will be initialised 
  //with an Array of all dom elements
  //which are spezified by this expression
  messageOutputAreas : '.AOChatOutput'
  
  //Initialise our output areas 
  ,onInitDomElements : function(chat)
  {
    // initialise the messageInputAreas, if the property contains a string
    // use all childs of the chats root element which match the given expression
    if(typeof chat.options.messageOutputAreas == "string")
    {
      chat.setOptions({messageOutputAreas : chat.options.rootElem.getElements(chat.options.messageOutputAreas)});
    }
    
    //take care that messageInputArea is an array of elements
    chat.setOptions({messageOutputAreas : $splat(chat.options.messageOutputAreas)});
  }
    
  //Render messages in output area
  ,onRenderingMessages : function(chat) {
    chat.options.messageOutputAreas.each(function(outputArea) {
      if(chat.incomingMessages!=null) {
        chat.incomingMessages.each(function(message) {
          //create a copy of the message template
          var messageTemplateCopy = chat.messageTemplate.clone();
          
          //creates a iterable message hash
          var messageHash = new Hash(message);
          
          messageHash.each(function(value,property) {
            //get field elements (a html tag with the same class as the message.properties name)
            var fieldElements = messageTemplateCopy.getElements('.' + property);
            
            //inject value for each element
            fieldElements.each(function(elem) {
              elem.set('html',value);
            });
          });
          
          //insert message into our outputArea
          messageTemplateCopy.inject(outputArea);
        });
      }
    });
  }
  
  // todo: show successful sent message immediately, no waiting for next update
  ,onAfterMessageSend : function(chat) {

    if(chat.lastMessageServerResponse.success == false
      && chat.lastMessageServerResponse.error != null) {
      // todo: show error message in output area -> no alert
      alert(chat.lastMessageServerResponse.error);
    }
  }
}




/***
 * 
 * 
 * MESSAGE-INPUT-AREA-MODULE
 * 
 * A module for initialising and registering the MessageInputArea of the Chat 
 * 
 ***/
var messageInputAreaModule = 
{
  //create a new default option
  //if this option contains a string
  //then it will be initialised 
  //with an Array of all dom elements
  //which are spezified by this expression  
  messageInputAreas : '.AOChatMessageInput'
  
  //Initialise our input area 
  ,onInitDomElements : function(chat)
  {
    // initialise the messageInputAreas, if the property contains a string
    // use all childs of the chats root element which match the given expression
    if(typeof chat.options.messageInputAreas == "string")
    {
      chat.setOptions({messageInputAreas : chat.options.rootElem.getElements(chat.options.messageInputAreas)});
    }
    
    //take care that messageInputArea is an array of elements
    chat.setOptions({messageInputAreas : $splat(chat.options.messageInputAreas)});
  }

  //adds the input of all messageAreas to the chat message
  //deletes content of messageArea afterwards
  ,onSetMessage : function(chat)
  {
    //alert("onSetMessage");
    chat.options.messageInputAreas.each(function(area)
    {
      chat.message.text += area.get('value');
      chat.message.rawText += area.get('value');
      
      area.set('value','')
    });
  }
}



/***
 * 
 * 
 * SEND-BUTTON-MODULE
 *
 *
 * A module for initialising the send button of the chat it registers
 *  a onClick event which invokes the send method of the chat 
 * 
 */
var sendButtonModule = 
{
  //create a new default option
  //if this option contains a string
  //then it will be initialised 
  //with an Array of all dom elements
  //which are spezified by this expression  
  sendButtons : '.AOChatSendButton'
  
    
  //Initialise our send buttons 
  ,onInitDomElements : function(chat)
  {
    //initialise the send buttons, if the property contains a string
    // use all childs of the chats root element which match the given expression
    if(typeof chat.options.sendButtons == "string")
    {
      chat.setOptions({sendButtons : chat.options.rootElem.getElements(chat.options.sendButtons)});
    }
    
    //take care that sendButton is an array of elements
    chat.setOptions({sendButtons : $splat(chat.options.sendButtons)});

    //add the send method of the chat as an onClick eventhandler to all sendButtons
    chat.options.sendButtons.each(function(button)
    {
      button.addEvent('click',chat.send.bind(chat));
    });
  }
}




/***
 * 
 * 
 * AUTOSCROLL-MODULE
 * 
 * A Module which implements autoscrolling in the output areas
 * (or a scrolled container which embeds an output area )
 * 
 */
var autoscrollModule = 
{
  //create a new default option
  //if this option contains a string
  //then it will be initialised 
  //with an Array of all dom elements
  //which are spezified by this expression
  //autoscrolledElements : '.AOChatOutput'
  autoscrolledElements : ''
  
  //Initialise our output areas 
  ,onInitDomElements : function(chat)
  {
    //Set autoscrolledElements to our messageOutputAreas 
    //if the property contains an empty string
    if(chat.options.autoscrolledElements=='')
    {
      chat.setOptions({autoscrolledElements : chat.options.messageOutputAreas});
    }

    // initialise the autoscrolledElements, if the property contains a string
    // use all childs of the chats root element which match the given expression
    if(typeof chat.options.autoscrolledElements == "string")
    {
      chat.setOptions({autoscrolledElements : chat.options.rootElem.getElements(chat.options.autoscrolledElements)}); 
    }
    
    //take care that messageInputArea is an array of elements
    chat.setOptions({autoscrolledElements : $splat(chat.options.autoscrolledElements)});

    chat.options.autoscrolledElements.each(function(scrolledElement)
    {
      //scroll outputArea to the bottom
      scrolledElement.scrollTop = scrolledElement.getScrollHeight();
    });
  }

  
  ,onBeforeRenderingMessages : function(chat)
  {
    chat.options.autoscrolledElements.each(function(scrolledElement)
    {
      //Check if this scrolledElement should automaticly scrolled to the bottom
      //This is true if it is actualy (without new messages) scrolled to the bottom
      //The scroll bar is scrolled to the bottom if outputArea.scrollTop is equal to the scrollHeigth - the true height of the element 
      var bottom = (scrolledElement.getScrollHeight()-scrolledElement.getHeight());
      
      if(scrolledElement.getScrollTop()>=(bottom-10))
      {
        //scroll area is scrolled to the bottom
        scrolledElement.set('scrollAreaToBottom',true);
      }
      else
      {
        //scroll area is not scrolled to the bottom
        scrolledElement.set('scrollAreaToBottom',false);
      }
    });
  }

  ,onAfterRenderingMessages : function(chat)
  {
    chat.options.autoscrolledElements.each(function(scrolledElement)
    {
      //scroll outputArea to the bottom
      //if this is wanted
      if(scrolledElement.get('scrollAreaToBottom')=='true')
      {
        scrolledElement.scrollTop = scrolledElement.getScrollHeight();
      }
    });
  }
}


/***
 * 
 *  DEBUG MODULE
 * 
 *  Registers a module which adds debug messages for each event 
 *  it prints the event and the chat-instances to the firefox console
 *
 ***/
var debugModule = 
{
  // Disable this method if you are not using firebug
  // or if you don't want to have debug messages
  debug : false
  ,onInitialize : function(chat)
   {   
    if(chat.options.debug)
    {
      console.log("onInitialize: ", " chat: " ,chat);
    }
  }

  ,onMainLayoutLoaded : function(chat)
   {
    if(chat.options.debug)
    {
      console.log("onMainLayoutLoaded: ", " chat: " ,chat);
    }
   }
  
  ,onError : function(chat)
   {
    if(chat.options.debug)
    {
      console.log("onError: ",chat.options.error);
    }
   }
  
  ,onInitDomElements : function(chat)
  {
    if(chat.options.debug)
    {
      console.log("onInitDomElements: ", " chat: " ,chat);
    }
  }
  
  ,onSetMessage : function(chat)
  {
    if(chat.options.debug)
    {
      console.log("onSetMessage: ", " chat: " ,chat);
    }
  }
  
  ,onBeforeMessageSend : function(chat)
  {
    if(chat.options.debug)
    {
      console.log("onBeforeMessageSend ", " chat: " ,chat);
    }
  }

  ,onAfterMessageSend : function(chat)
  {
    
    if(chat.options.debug)
    {
      console.log("onAfterMessageSend ", " chat: " ,chat);
    }
  }
  
  ,onUnsetMessage : function(chat)
  {
    if(chat.options.debug)
    {
      console.log("onUnsetMessage ", " chat: " ,chat);
    }
  }
  
  ,onBeforeRequestingMessages : function(chat)
  {
    if(chat.options.debug)
    {
      console.log("onBeforeRequestingMessages: ", " chat: " ,chat);
    }
  }
  
  ,onAfterRequestingMessages : function(chat)
  {
    if(chat.options.debug)
    {
      console.log("onAfterRequestingMessages: ", " chat: " ,chat);
    }
  }
  
  ,onBeforeRenderingMessages : function(chat)
  {
    if(chat.options.debug)
    {
      console.log("onBeforeRenderingMessages: ", " chat: " ,chat);
    }
  }
  
  ,onRenderingMessages : function(chat)
  {
    if(chat.options.debug)
    {
      console.log("onRenderingMessages: ", " chat: " ,chat);
    }
  }
  
  ,onAfterRenderingMessages : function(chat)
  {
    if(chat.options.debug)
    {
      console.log("onAfterRenderingMessages: ", " chat: " ,chat);
    }
  }
}


/**
 * Modul, das den Nachrichtenverlauf löscht
 */
var clsChatModule =
{
  countdownClsTime : function (chat, clsTimer) {
    var chat_id  = chat.options.chatId;
    var spanElem = $('span_cls_'+chat_id);
    var aElem    = $('a_cls_'+chat_id);
    
    if(!spanElem || !aElem) {
      alert("FEHLER: Ein HTML-Element für den Countdown konnte nicht gefunden werden.");
      return;
    }

    var countClsTimeDown = function () {
      var restzeit = spanElem.get('html').toInt();
      if(restzeit >= 1) {
        // runterzählen
        restzeit--;
        spanElem.set('html', restzeit);
        
        // Verweigern-Button ausblenden lassen, wenn <= 7 sek
        if(restzeit == 6) {
          aElem.fade('out');
        }
      } else {
        spanElem.getParent('span').setStyle('display', 'none');
        
        // Timer stoppen
        clearTimeout(clsTimer);

        var newMsg = {    text: "/clearchat "+chat.options.heldId
                      ,rawText: "/clearchat "+chat.options.heldId
                     };
        chat.message = newMsg;
        chat.send();
      }
    };
    clsTimer = countClsTimeDown.periodical(1000);
  }
  
  ,onRenderingMessages : function(chat) {
    if(chat.incomingMessages!=null) {
      chat.incomingMessages.each(function(message) {
        if(message.command != null && message.command == 'cls') {
          // Countdown starten
          clsChatModule.countdownClsTime(chat, chat.clsTimer);
        }
      });
    }
  }
}

var clearChatModule =
{
  clearChat : function(chat) {
    chat.options.messageOutputAreas.each(function(outputArea) {
      outputArea.empty(); // löscht alles, sollte also unbedingt vor dem Einfügen danach kommender Nachrichten ausgeführt werden
    });
  }
  
  ,onRenderingMessages : function(chat) {
    if(chat.incomingMessages!=null) {
      chat.incomingMessages.each(function(message) {
        if(message.command == 'clearchat') {
          // Timer stoppen
          clearTimeout(chat.clsTimer);
          
          // davor angezeigte Chatnachrichten entfernen
          clearChatModule.clearChat(chat);
        } else if(message.command == 'cancelcls') {
          // Timer stoppen
          clearTimeout(chat.clsTimer);
          
          // countdown ausblenden
          var spanElem = $('span_cls_'+chat.options.chatId);
          var aElem    = $('a_cls_'+chat.options.chatId);
          if(spanElem) {
            spanElem.getParent('span').destroy();
          }
          if(aElem) {
            aElem.destroy();
          }
        }
      });
    }
  }
}

var autocompletionModule = 
{
  onInitDomElements : function(chat) {
    // load autocompletion?
    if(chat.options.loadAutocompletion == false)
      return;
  
    // grab the input areas
    var at_least_one_init = false;
    chat.options.messageInputAreas.each(function(elem) {
      // init Autocompleter
      // documentation: http://www.clientcide.com/docs/3rdParty/Autocompleter
      new Autocompleter.Request.JSON(elem, './ext/chat/backend/autocomplete.php', {
        'postVar': 'autocomplete'
        , 'minLength': 2
        , 'selectMode': 'selection'
        , 'delay': 700
        , 'overflow': true
        , 'maxChoices': 6
      });
      at_least_one_init = true;
    });
    
    if(at_least_one_init == false)
      alert("Autocompletion Module could not be initialised");
  }
}

//Moodulize.registerModule("AOChat",debugModule);
Moodulize.registerModule("AOChat",sendMessageModule);
Moodulize.registerModule("AOChat",receiveMessageModule);
Moodulize.registerModule("AOChat",clearChatModule); // muss vor dem OutputAreaModule registriert werden!
Moodulize.registerModule("AOChat",messageOutputAreaModule);
Moodulize.registerModule("AOChat",messageInputAreaModule);
Moodulize.registerModule("AOChat",sendButtonModule);
Moodulize.registerModule("AOChat",clsChatModule); // muss nach dem outputAreaModule registriert werden!
Moodulize.registerModule("AOChat",autoscrollModule);
Moodulize.registerModule("AOChat",autocompletionModule);
