/*******************************************************************************
 * Alerts
 * 
 * Imports:
 * 
 *   Asset
 *   Class
 *   Element
 *   Fx
 *   Options
 * 
 * Methods:
 * 
 *   error(message, title)
 *   info(message, title)
 *   warn(message, title)
 * 
 */



/*******************************************************************************
 * Constants
 */

// Defines message priorities 
var LEVEL_ERROR   = "error";
var LEVEL_INFO    = "info";
var LEVEL_WARNING = "warning";



/*******************************************************************************
 * Classes
 */
var Alert = new Class({
  Implements: Options,
  options: {
    debug: false,
    path: '/',
    theme: "growl"
  },
  initialize: function(options) {
    // Merges the default options with the ones given as parameters
    this.setOptions(options);
    
    // Injects the stylesheet corresponding to the specified theme except in the
    // case a custom theme was provided throught external stylesheets  
    if (this.options.theme !== "custom") {
      // Updates the path according to the chosen theme
      this.options.path = this.options.path + "themes/" + this.options.theme;
      
      // Injects the CSS file of the theme inside the page
      Asset.css(this.options.path + "/styles.css");
    }
    
    // Creates the following template and injects it at the bottom of the page:
    // 
    //   <div class="alert">
    //     <dl>
    //       <dt>A title</dt>
    //       <dd>A message</dd>
    //     </dl>
    //   </div>
    // 
    var container = new Element("DIV");
    container.addClass("alert");
    container.inject(document.body);
    
    var list = new Element("DL");
    list.inject(container);
    
    var title = new Element("DT");
    title.inject(list);
    
    var message = new Element("DD");
    message.inject(list);
    
    // Initializes visual effects
    var opacityEffect = new Fx.Tween(container, {property: "opacity", duration: 400, link: "cancel"});
    var positionEffect = new Fx.Tween(container, {property: "top", duration: 400, link: "cancel"});
    
    // Saves references for later use
    this.container = container;
    this.message = message;
    this.opacityEffect = opacityEffect;
    this.positionEffect = positionEffect;
    this.title = title;
    
    if (!this.options.debug) {
      // Hides the template at startup
      this.hide(false);
      
      // Positions the template even if it is hidden, so it is not simply added
      // to the end of the page (which resulted in a blank bottom margin) 
      // because the opacity effect only acts on the visibility property and not
      // on the display property.
      this.center();
    } else {
      this.setTitle("A title");
      this.setMessage("A message");
      
      this.container.addEvent("click", function(event) {
        // Stops event propagation (no other listener will ever get it)
        event.stop();
        
        this.hide();
      }.bind(this));
    }
  },
  error: function(message, title) {
    this.show(LEVEL_ERROR, message, title);
  },
  hide: function(withEffect) {
    // Handles the case where no parameter was provided
    withEffect = $defined(withEffect) ? withEffect : true;
    
    if (withEffect) {
      // Computes coordinates of the template
      var coordinates = this.container.getCoordinates();
      var height = coordinates.height;
      var top = coordinates.top;
      
      // Hides the template
      this.opacityEffect.start(0);
      this.positionEffect.start(top + (height / 2));
    } else {
      this.opacityEffect.set(0);
    }
  },
  center: function() {
    // Makes sure the template is not currently being repositionned
    this.positionEffect.cancel();
    
    // Computes coordinates of the template
    var coordinates = this.container.getCoordinates();
    var height = coordinates.height;
    var width = coordinates.width;
    
    // Computes the new position
    var top = window.getScrollTop() + (window.getHeight() / 2) - (height / 2);
    var left = window.getScrollLeft() + (window.getWidth() / 2) - (width / 2);
    
    // Positions template
    this.container.setStyles({"top": top, "left": left});
  },
  info: function(message, title) {
    this.show(LEVEL_INFO, message, title);
  },
  setMessage: function(message) {
    this.message.set("html", message);
  },
  setTitle: function(title) {
    this.title.set("html", title);
  },
  show: function(level, message, title) {
    // Retrieves the current display status of the template
    var visibility = this.container.getStyle("visibility");
    
    // Hides any message currently displayed
    if (visibility !== "hidden") {
      this.timer = $clear(this.timer);
      
      this.hide(false);
    }
    
    // Updates title if it was provided
    if ($defined(title)) {
      this.setTitle(title);
    } else {
      this.setTitle("");
    }
    
    // Updates message
    this.setMessage(message);
    
    // Styles the template according to the message priority
    this.container.addClass(level);
    
    // Positions the template in the middle of the viewport
    this.center();
    
    // Shows the template
    this.opacityEffect.start(0.8);
    
    // Hides the template only if debug mode is disabled
    if (!this.options.debug) {
      this.timer = this.hide.delay(2000, this);
    }
  },
  warn: function(message, title) {
    this.show(LEVEL_WARNING, message, title);
  }
});