///////////////////////////////////////////
//   IMAGEOBSERVER
//
//  STATES
//  =============
//   enabled, down, hover, disabled
//
//  States that are given will force the images to be preloaded
// 

Voy.ImageObserver = Class.create();

Voy.ImageObserver.prototype = {
  initialize: function(img,states,options) {
		var self = this;
    this.img = $(img);
    this.links = $A();
    
    // Enabled is added by default
    this.states = $H({ enabled: { suffix: "", opacity: 1.0, img: null } }).merge(states);
    
    // STATES - change short notation into -> { suffix: .., opacity: ... }
    // Fix when opacity is not given
    this.states.each( function(state) {
      var value = state.value;
      if( value == null || Voy.isString( value ) ) {
        //self.states[state.key] = { suffix: state.value, opacity: 1.0 };
        self.states.set( state.key, { suffix: state.value, opacity: 1.0 } );
      } else {
      	//self.states[state.key] = { suffix: value.suffix || "", opacity: ( value.opacity || 1.0 ), img: null };
        self.states.set( state.key, { suffix: value.suffix || "", opacity: ( value.opacity || 1.0 ), img: null } );
      }
    } );
    
    // Set Default Options
    this.options = Object.extend( {
      onclick: Prototype.emptyFunction,
      onstatechange: Prototype.emptyFunction,
      token: "_",
      cursor: "pointer",
      div: this.img,
      state: "enabled",
      unselectable: true
    }, options || {} );
    
    if( this.options.unselectable ) {
      // Ensure div is unselectable
      this.img.onselectstart = function() { return false; };
      this.img.unselectable = "on";
      this.img.style.MozUserSelect = "none";
    }
        
    // Set the Current state of img
    this.events = {};
    this.observe( 'click', 'mousedown', 'mouseup', 'mouseover', 'mouseout' );
    this.state = ( this.state || this.options.state );
    
    var src = this.getImageSrc();
    var suffix = this.states.get( this.state ).suffix;
    var match = src.match(/^([^?]*)(\.[^?\/]+.*)/);
    this.prefix = Voy.isBlank( suffix ) ? match[1] : match[1].slice( 0, match[1].length - suffix.length - this.options.token.length);
    this.postfix = match[2];
    this.img.style.cursor = this.options.cursor;
    this.preloadImages();
    
    Element.setOpacity( this.options.div, this.states.get( this.state ).opacity );
  },
  setImageSrc: function(src) {
    if( this.img.nodeName == "IMG" || 
        ( this.img.nodeName == "INPUT" && this.img.type == "image" ) ) {
      this.img.src = src;
    } else {
      this.img.style.backgroundImage = "url(" + src + ")";
    }
  },
  getImageSrc: function() {
    if( this.img.nodeName == "IMG" || 
        ( this.img.nodeName == "INPUT" && this.img.type == "image" ) )
      return this.img.src;
    
    return this.img.style.backgroundImage.replace( /^url[ ]*\((.*)\)/, "$1" );
  },
  linkTo: function(img) {
    var newLink = new Voy.ImageObserver( img, this.states, this.options );
    this.links.push( newLink );
    newLink.links.push( this );
    return newLink;
  },
  mirrorEvents: function(div) {
    div = $(div)
    div.style.cursor = this.options.cursor;
  
    Event.observe( div, "mouseover", this.onmouseover.bind( this ) );
    Event.observe( div, "mouseout", this.onmouseout.bind( this ) );
    Event.observe( div, "mouseup", this.onmouseup.bind( this ) );
    Event.observe( div, "mousedown", this.onmousedown.bind( this ) );
  },
  preloadImages: function() {
    var self = this;
    
  	this.states.each( function(state) {
      if( state != null && state.value != null && Voy.isBlank( state.value.img ) ) {
        var img = new Image();
        self.getSrc(state.key);
        img.src = self.getSrc(state.key);
        state.value.img = img;
      }
  	} );
  },
  observe: function() {
    var args = $A(arguments).flatten();
    for( var i = 0; i < args.length; i++ ) {
      var name = args[i];
	    this.events[ name ] = this["on" + name].bindAsEventListener(this);
	    Event.observe( this.img, name, this.events[ name ] );
    }
  },
  isDisabled: function() { return this.state == 'disabled'; },
  setState: function(state) {
    if( this.states.get( state ) != null && this.state != state ) {
      var oldState = this.states.get( this.state );
      var newState = this.states.get( state );
      
      // Two states may have the same img.src
      if( oldState.suffix != newState.suffix ) {
        var value = this.states.get( state );
        if( Voy.isNotBlank( value ) && Voy.isNotBlank( value.img )) {
          this.setImageSrc( value.img.src );
        }
      }
      
      Element.setOpacity(this.options.div, newState.opacity );
      
      this.state = state;
      this.img.disabled = this.isDisabled();
      this.img.style.cursor = this.isDisabled() ? "not-allowed" : "pointer";
      
      this.options.onstatechange( state, this.state );
      // call this.options[  ondisabled, onenabled, ondown, OR onhover  ]
      ( this.options[ "on" + state ] || Prototype.emptyFunction )( this.state );
      this.links.each( function(link) { link.setState(state); } );
    }
  },
  getSrc: function(state) {
  	var suffix = this.states.get( state ).suffix;
    return this.prefix + ( Voy.isBlank( suffix ) ? "" : ( this.options.token + suffix ) ) + this.postfix;
  },
  enable: function() {
    this.setState( "enabled" );
  },
  disable: function() {
    this.setState( "disabled" );
  },
  callOnclick: function() {
    this.options.onclick(this)
  },
  onclick: function(evt) {
    if( ! this.isDisabled() )
      window.setTimeout( this.callOnclick.bind(this), 50 );
  },
  onmousedown: function(evt) {
    if( this.state != "disabled" ) {
      this.setState( "down" );
    }
  },
  onmouseup: function(evt) {
    if( this.state != "disabled" ) {
      this.enable();
      this.leaveState = this.state;
    }
  },
  onmouseover: function(evt) {
    if( this.state != "disabled" ) {
	    if( this.leaveState == "down" ) {
	      this.setState( "down" );
	    } else if( Voy.isNotBlank( this.states.get( "hover" ) ) && this.state != "down" ) {
	      this.setState( "hover" );
	    }
    }
  },
  onmouseout: function(evt) {
    this.leaveState = this.state;
    if( this.state != "disabled" ) {
      this.enable();
	  this.leaveState = this.state;
    }
  },
  destroy: function() {
		for(  var name in this.events )
		  Event.stopObserving( this.img, name, this.events[name] );
  }
};

