/*
 * (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
 */
/**
 * ------------------------------------------------------------------------------------------
 * ---------------------------------INTRODUCTION---------------------------------------------
 * ------------------------------------------------------------------------------------------
 *
 * This class implements a frontend for a chat based on the javascript framework mootools.
 *
 * It uses a customizeable Architecture to ease extension and modification of the chat for your
 * website. AOChat uses the Mootools Options and an extended Version of the Mootools events
 * which allows blocking (chained) events.
 * It provides a lot of custom mootools events to inject your code at the right position.
 * You can use the Moodulizer-Framework to add modules to all instances of your chat.
 *
 * To create a new chat use the following html-code on your webpage:
 *
 * <div id='myChat'></div>
 *
 * <script type='text/javascript'>
 *   var chat = new AOChat({
 *                           chatId : 'chat',
 *                           rootElem : $('chat')
 *                         });
 * </script>
 *
 * Please be aware that you need a backend implementation,
 * which is defaultly located at: backend/aochat_service.php
 *
 * AOChat provides a lot of options to make it fit your web-environment.
 * The options provided with the default implementation are dokumentet below.
 *
 * The visible Appeareance of AOChat is fully customizable, by using dynamicly loaded
 * templates. An example for that including an other example which shows how you can
 * add modules to AOChat is desribed below the options.
 *
 * -------------------------------------------------------------------------------------------
 * ------------------------------------OPTIONS -----------------------------------------------_
 * -------------------------------------------------------------------------------------------
 *
 * autoscrolledElements:  An array of elements (or a valid mootools element selector)
 *                        which should be automatically scrolled to their bottom,
 *                        if they were scrolled to the bottom before
 *                        Default: value of messageOutputAreas
 *                        Module: autoscrollModule
 *
 * chatId:                The ID of this chat to authenticate with an chat backend
 *                        Default: empty
 *                        Module: aochat itself
 *
 *
 * debug:                 Checks if the chat is in debug-mode.
 *                        Turning this to true lets the chat send debug-messages
 *                        on the firebug-console.
 *                        Be careful this can break the chat in IE
 *                        (Firebug Lite needs to be loaded in this case).
 *                        Default: false
 *                        Module: debugModule
 *
 *
 * messageInputAreas:     An array of elements (or a valid mootools element selector),
 *                        which are used for typing new messages.
 *                        Default: '.AOChatMessageInput'
 *                        Module: 'messageInputAreaModule'
 *
 *
 * messageOutputAreas:    An array of elements (or a valid mootools element selector)
 *                        which are used for rendering incoming messages.
 *                        Default: '.AOChatOutput'
 *                        Module: messageOutputAreaModule
 *
 *
 * messageTemplateURL:    The URL of the message template
 *                        (containing an example for one template message)
 *                        Default: 'template/message.html'
 *                        Module: aochat itself
 *
 *
 * mainTemplateURL:       The URL of the main template of the chat.
 *                        (containing all gui elements etc....)
 *                        Default: 'template/main.html'
 *                        Module: aochat itself
 *
 *
 * refreshDelay:          The delay in milliseconds between an message request.
 *                        Default:1000
 *                        Module: aochat itself
 *
 *
 * rootElem:              An element or a valid selector which contains the element
 *                        to render the chat into .
 *                        Default: 'chat'
 *                        Module: aochat itself
 *
 *
 * sendButtons:           An array of elements (or a valid mootools element selector),
 *                        which should have a click event to send a message
 *                        Default: '.AOChatSendButton'
 *                        Module: sendButtonModule
 *
 *
 * serverURL:             The URL of the chat-server
 *                        (the page answering our message_send and message_requested requests)
 *                        Default: 'backend/aochat_service.php'
 *                        Module:  aochat itself
 *
 *
 * user:                  The user to authenticate on the server
 *                        Default: 'standard'
 *                        Module:  aochat itself
 *
 * -----------------------------------------------------------------------------------------------
 * ------------------HOW TO CUSTOMIZE THE VISUAL APPEAREANCE OF AOCHAT----------------------------
 * -----------------------------------------------------------------------------------------------
 *
 * AOChat uses Templates to render it's appeareance while initialisiation.
 * The default template for the Main Layout is located at 'template/main.html'
 * and looks like the following:
 *
 * <div class="AOChatBox">
 *   <div class="AOChatOutputPanel">
 *    <div class="AOChatOutput">
 *    </div>
 *  </div>
 *  <div class="AOChatInputPanel">
 *    <textarea rows="1" cols="50" class="AOChatMessageInput"></textarea>
 *    <input type="submit" class="AOChatSendButton" value="Send"/>
 *  </div>
 * </div>
 *
 * You can modify the appeareance of your AOChat by modifying this template
 * or by providing your own template, with the following initialisiation of the chat:
 *
 * <script type='text/javascript'>
 *   new AOChat({
 *               chatId : 'chat',
 *               rootElem : $('chat'),
 *               mainTemplateURL: 'PathToYourTemplate'
 *             });
 * </script>
 *
 * The default template for rendering your incomeing messages
 * is located at:'template/message.html'
 *
 *  A message returned by your chat-service should be a JSON-Object and
 *  look like that:
 *
 *  var message =
 * {
 *
 *    user : "Test User",
 *    .
 *    .
 *    .
 *    .... some other properties here ....
 *    .
 *    .
 *    .
 *    text : 'This is an example text'
 * }
 *
 * The Chat inspects for each incoming message the DOM of the message templates
 * and injects the value of each property at each element which has a class with
 * the same name like the property.
 *
 * -----------------------------------------------------------------------------------------------
 * ----------------------------EXTENDING THE BEHAVIOUR OF AOCHAT----------------------------------
 * -----------------------------------------------------------------------------------------------
 *
 * The AOChat class uses the Moodulizer-Framework to extend its behaviour by the use of modules.
 * A module is a simple mootools options object (which is essentialy a normal Javascript object).
 * Which could look like that if you want to modifie the default user and pop up a alert box after
 * a message was send:
 *
 * var alertModule =
 * {
 *   user : 'myModifiedUserName',
 *
 *   onAfterMessageSend : function(chat)
 *   {
 *     alert('A message was sent');
 *   }
 * }
 *
 * Such a module is now registered at the Moodulizer-Framework as a Module of AOChat
 * in the following way:
 *
 * Moodulize.registerModule("AOChat",alertModule);
 *
 * If you are now creating a new AOChat object, the options provided
 * by the alertModule are set automaticly.
 * Since AOChat is implementing the Mootools-Events class, it is also garanteed
 * that an eventHandler for the event onAfterMessageSend event is added.
 * Sending a new message in your AOChat results now in a Popup
 * telling you that a message was send.
 *
 *
 * -----------------------------------------------------------------------------------------------
 * --------------------------------------EVENTS---------------------------------------------------
 * -----------------------------------------------------------------------------------------------
 *
 * AOChat provides a lot of custom events which can be used to extend its behaviour
 * in the way desribed above.
 *
 * They are called in the following order
 *
 *
 * Intitialisation:
 *
 *  onInitialise --> onMainLayoutLoaded --> onInitDomElements
 *
 *
 * Sending a message:
 *
 *  onSetMessage --> onBeforeMessageSend --> onAfterMessageSend --> onUnsetMessage
 *
 *
 * Requesting a message:
 *
 *  onBeforeRequestingMessages --> onAfterRequestingMessages
 *                             --> onBeforeRenderingMessages
 *                             --> onRenderingMessages
 *                             --> onAfterRenderingMessages
 *
 * -------------------------------------------------------------------------------------------------
 * -------------------------DESCRIPTION OF ALL EVENTS-----------------------------------------------
 * -------------------------------------------------------------------------------------------------
 *
 * The following custom events are provided with the default implementation of AOChat:
 *
 * onInitialize:                This event is fired when the chat and is options are initialised,
 *                              the parameter of the event-handler contains the chat object itself
 *
 *
 * onMainLayoutLoaded:          This event is fired after successfuly loading the main layout
 *                              the parameter of the event-handler contains the chat object itself.
 *
 *
 * onError:                     This event is fired when an error occours,
 *                              the parameter of the event-handler contains the chat object itself.
 *
 *
 * onInitDomElements:           This element is fired for modules to initialise their dom-elements,
 *                              the parameter of the event-handler contains the chat object itself.
 *
 *
 * onSetMessage:                This Event is for Modules which set or modifie the message
 *                              the parameter of the event-handler contains the chat object itself.
 *
 *
 * onBeforeMessageSend:         This event is fired after setting the message and before sending it.
 *                              The parameter of the event-handler contains the chat object itself.
 *
 *
 * onAfterMessageSend:          This event is fired after the message is send.
 *                              The parameter of the event-handler contains the chat object itself.
 *
 *
 * onUnsetMessage:              This event is fired after the onAfterMessageSend event.
 *                              Modules should unset the message in this phase.
 *                              The parameter of the event-handler contains the chat object itself.
 *
 *
 * onBeforeRequestingMessages:  Event fired before requesting the incoming messages
 *                              The parameter of the event-handler contains the chat object itself.
 *
 *
 * onAfterRequestingMessages:   Event fired after requesting the incoming messages
 *                              The parameter of the event-handler contains the chat object itself.
 *
 *
 * onBeforeRenderingMessages:   Event fired before rendering the incoming messages.
 *                              The parameter of the event-handler contains the chat object itself.
 *
 *
 * onRenderingMessages:         Event fired while rendering the messages.
 *                              The parameter of the event-handler contains the chat object itself.
 *
 *
 * onAfterRenderingMessages:    Event fired after rendering the messages
 *                              The parameter of the event-handler contains the chat object itself.
 *
 */
var aochats = [];

var AOChat = new Class( {
  // class implements the mootools option class
  Implements : [ Options, ChainEvents ]

  , msgReloader : ''

  , clsTimer : {}

  , initialize : function(options) {
    // register this instance in module controler
    Moodulize.registerInstance('AOChat', this);

    // heldId muss angegeben werden!
    if(!options.heldId || options.heldId == 0) {
      alert("Der Chat konnte nicht initialisiert werden!");
      return;
    }

    this.setOptions(options);

    if (typeof this.options.rootElem == 'string') {
      this.setOptions( {
        rootElem : $(this.options.rootElem)
      });
    }

    if (typeof this.options.chatId != 'string') {
      this.setOptions( {
        chatId : this.options.rootElem.get("id")
      });
    }
    this.fireEvent('initialize', this);
    this.createLayout();

    // Referenz auf diesen Chat in globaler Variable ablegen
    aochats.push(this);
  }


  // possible options and their default values of an AntamarChat
  , options : {
    // chatDiv for this chat instance
    rootElem : $('chat')

    // The id of this chat, this var is set to the id of chatDiv
    // if it is not spezified in the starting options
    , chatId : ''

    // URL of the chat-server (page for getting the chat-content)
    , serverURL : "backend/aochat_service.php"

    // URL of the main template
    , mainTemplateURL : "template/main.html"

    // URL of the message template
    , messageTemplateURL : "template/message.html"

    // username to authenticate
    , user : "standard"

    // hero-id is needed
    , heldId : 0

    // the refresh delay of this chat
    , refreshDelay : 5000
    
    // load Autocompletion module?
    , loadAutocompletion : true
  }

  // The message to send with the next send request
  , message : {
    // The id of this chat
    chatId : ""

    // the operation delivered to the server for indicating
    // what the server has to do
    , operation : ""

    // the user sending the message
    , user : ""

    // the command sended by the user
    , command : ""

    // the message sended by the user
    , text : ""

    // the full message
    , rawText : ""
  }

  // The message template
  , messageTemplate : {}

  //The messages displayed in this chat
  , messages : []

  // the last message which was sent out
  , lastMessage : null
  , lastMessageServerResponse : null

  // The new incoming messages requested from the server
  , incomingMessages : []

  // Last Request used for requesting the messages ....
  , jsonMessagesRequest : {}

  // an array containing all error messages of this chat
  , error : []

  // creates the main layout of the chat
  , createLayout : function() {
    // load the layout of a single message
    new Request.HTML({
      url : this.options.messageTemplateURL
      , onSuccess : function(template) {
        this.messageTemplate = template[0];
        if (this.options.debug) {
          console.log("Message-Template:", this.messageTemplate);
        }
        this.createMainLayout();
      }.bind(this)
    }).post();
  }

  , createMainLayout : function() {
    new Request({ // HTML.Request does not work in IE9 here... please do not ask why, because I don't fucking know!!!
      url : this.options.mainTemplateURL
      , onSuccess : function(res) {
        if (this.options.debug) {
          console.log("Main-Layout:", res);
        }

        // set content
        this.options.rootElem.set('html', res);

        // call method to proceed rendering the chat
        this.createMainLayoutSuccessful();
      }.bind(this)
    }).post();
  }

  // method created in case of successfuly loading the main template
  , createMainLayoutSuccessful : function() {
    this.fireEvent("mainLayoutLoaded", this);

    // fire events for modules initialising the dom elements (send
    // button,user list ....)
    this.fireEvent("initDomElements", this);

    // start requesting messages
    this.requestMessages();
  }

  // method created in case of an error while loading the main layout
  , createError : function(errorMessage) {
    // log error message
    this.error.push(errorMessage);

    if (this.options.debug) {
      this.console.log('Error:', errorMessage);
    }

    // fire error event
    this.fireEvent("error", this);
  }

  // Send a message to the chat server
  , send : function(event) {
    // stop the default behaviour of this button
    if(event) {
      var ev = new Event(event);
      ev.preventDefault();
    }

    // set server operation
    this.message.operation = "send_message";

    // sets the chat id of the message
    this.message.chatId = this.options.chatId;

    // sets the user of the message
    this.message.user = this.options.user;

    // chain event for modules setting the chat message
    this.chainEvent('setMessage');

    // fires an event for all modules
    // which want to do something before sending the message
    // this.fireEvent('beforeMessageSend',this);
    this.chainEvent('beforeMessageSend');

    // fires an event for all modules which are involved
    // when sending the message
    this.chainEvent('sendMessage');

    // fires the chained events
    this.fireChainedEvents(this);

    //The message send module has to call afterSendMessage
    //to proceed the send message action
  }

  , afterSendMessage : function(text, xml) {
    // remember sent message
    this.lastMessage = this.message.text; // hopefully it's a copy of that element and not a reference...
                                         // clone() doesn't work here

    // JSON auswerten, siehe http://mootools.net/docs/core/Utilities/JSON#JSON:decode
    this.lastMessageServerResponse = JSON.decode(text);

    // chains an event for all modules
    // which want to do something after sending the message
    this.chainEvent('afterMessageSend');

    // chains an event for unsetting the message
    this.chainEvent('unsetMessage');

    // fire the chained events
    this.fireChainedEvents(this);

    // unset message
    this.message.chatId = '';
    this.message.operation = '';
    this.message.user = '';
    this.message.command = '';
    this.message.text = '';
    this.message.rawText = '';
  }

  , requestMessages : function() {
    //fire all events before requesting messages
    this.chainEvent('beforeRequestingMessages');

    this.chainEvent('requestMessages');

    // fire the chained events
    this.fireChainedEvents(this);
  }

  // render messages after successfully receiving them
  , requestMessagesSuccessful : function(messages) {

    if (this.options.debug) {
      console.log("request successful",
          this.jsonMessagesRequest.response.text);
    }

    if (typeof messages == 'object') {
      // set messages to the new received messages
      this.incomingMessages = messages;
    } else {
      this.incomingMessages = [];
    }

    //chain a event when having successfully requested the messages
    this.chainEvent('requestMessagesSuccessful');

    // Chain event for all modules which want to do something
    // after requesting the Messages
    this.chainEvent('afterRequestingMessages');

    // Chain event for all modules which want to do something
    // before rendering the Messages
    this.chainEvent('beforeRenderingMessages');

    // Chain event for all modules which render the messages
    this.chainEvent('renderingMessages');

    // Chain event for all modules which want to do something after
    // rendering the messages
    this.chainEvent('afterRenderingMessages');

    // fire chained events
    this.fireChainedEvents(this);

    //push incoming messages to the messages array
    //delete them afterwards
    this.incomingMessages.each(function(message)
    {
      this.messages.push(message);
    }.bind(this));

    this.incomingMessages = [];

    // restart messages request cycle
    this.msgReloader = clearTimeout(this.msgReloader);
    this.msgReloader = this.requestMessages.delay(this.options.refreshDelay, this);
  }
  
  //stops getting new messages, e.g. after ajax changed html content of webpage, and chat is not displayed anymore
  , stop : function() {
    this.msgReloader = clearTimeout(this.msgReloader);
  }
});

/**
 * gibt die Instanz eines AOChats zurück, identifiziert anhand der chat_id
 * sollte keine Instanz gefunden werden, ist Rückgabe null
 */
function getAOChat(chat_id) {
  var gefunden = null;
  aochats.each(function(chat) {
    if( chat.options.chatId == chat_id )
      gefunden = chat;
  });
  return gefunden;
}

function stopAllChats() {
  aochats.each(function(chat) {
    chat.stop();
  });
}

function spieleChatSound(datei) {
  // neues Element erstellen
  var musicDiv = new Element('div');
  $('content').adopt(musicDiv);
  
  // bestimmte Datei abspielen?
  var params = '';
  if( datei && datei.length > 2 )
    params += 'datei='+datei;
  
  new Request.HTML({
    url: 'ajax/chatsound.php',
    update: musicDiv,
    data: params
  }).post();
}

function cancelCls(chat_id) {
  // eine Nachricht "/cancelcls" abschicken
  var chat = getAOChat(chat_id);
  if(chat) {
    var msg = { rawText: "/cancelcls" };
    chat.message = msg;
    chat.send();
  }
}

function verarbeiteEingabe(e, textarea) {
  var key = null;
  var bRetVal = true;

  if (e.which) {
    key = e.which; //normale Browser
  } else {
    key = e.keyCode; //IE
  }

  if (key == 13) { //ENTER
    var form = textarea.getParent('form');
    if(form) {
      // submitbutton suchen
      var btn = form.getElement('input[type=submit]');
      if (btn != null) {
        //Klick ausführen
        btn.click();
        bRetVal = false;
      }
    }
  }

  if (bRetVal == false) {
    //weitere Aktionen verhindern, wie z.B. Zeilenumbruch
    if(e.preventDefault) {
      e.preventDefault(); //normale Browser
    } else {
      e.returnValue = false; //IE
    }
  }
}
