YAMMON.Widgets.Form = new Class({
    Extends: YAMMON.Widget ,
    options: {
    },
    initialize: function( node , options ){
        
        this.parent( node , options );        
        this.locked = 0;
        this.highlighting_attached = false;
        this.change_attached       = false;    
        this.eventManager          = {};
        this.refresh();
    },
    refresh: function(){
        this.setupHighlighting();
        this.setupDependencies();
        this.setupNested();
        this.setupLocks();
        this.setupPlaceHolders();
    },
    lock: function( bool ){

        if( bool == undefined )
            bool = true;
        
        if( bool )
            this.locked++;
        else
            this.locked--;
    },
    unlock: function(){
        this.lock( false );
    },
    setupHighlighting: function(){  
    
        if( this.highlighting_attached )
            return false;
    
        var node = this.node;
        if( node.addEventListener ){	
            node.addEventListener( 'focus' , this.onFocus.bindWithEvent( this ) , true );
            node.addEventListener( 'blur'  , this.onBlur.bindWithEvent(this)    , true );        
        }else{
           node.addEvent( 'focusin'   , this.onFocus.bindWithEvent( this ) );
           node.addEvent( 'focusout'  , this.onBlur.bindWithEvent(this)    );        
        }
        
        this.highlighting_attached = true;

        return true;
    },
    setupDependencies: function(){
        
    
        //Get dependant elements
        var has_dependencies  = false;
        var node              = this.node;
        var attribute         = 'ym-form-dependencies';
        var dependants_map    = this.dependants_map = {};
        var dependency_map    = this.dependency_map = {};        
        var dependants        = node.getElements('*['+ attribute +']');

        this.cleanEvents('Dependencies');

        //Create dependecy maps        
        for( var i = 0 ; i < dependants.length ; i++ ){

            var dependant      = dependants[i];            
            var property       = dependant.getProperty(attribute).split(":");
            var dependant_name = property[ 0 ];
            var depends_on     = property[1].split(",");
                        
            //Create Dependants Map
            dependants_map[ dependant_name ] = dependant.get('id');                       
         
            //Create dependency map                                       
            for( var j = 0 ; j < depends_on.length ; j++ ){
                var on = depends_on[j];

                if( !dependency_map[ on ] ) 
                    dependency_map[ on ] = [];

                dependency_map[ on ].push( dependant_name );                
                has_dependencies = true;
            }
            
        }
                  
        //Attach event handlers
		for( var id in dependency_map ){
			if( !dependency_map.hasOwnProperty( id ) ) continue;

            var el = $(id);
            if( !el ) continue;

            if( el.attachEvent ){
                //Internet Explorer Attach Events Directly            
                var elements = el.getElements("input , select, textarea ");
                elements.push( el );
            }else{
                //Other Browsers use event delegation
                var elements = [ el ];
            }

            //Attach events
			var len = elements.length;            
			for( var j = 0 ; j < len ; j++ ){                
                if( elements[j].match("input[type='radio']") || elements[j].match("input[type='checkbox']") ){
                    this.registerEvent( 'Dependencies', elements[j], 'click', this.onDependsChange.bindWithEvent( this ,  [elements[j]] ), false );
                }else{
                    this.registerEvent( 'Dependencies', elements[j], 'change', this.onDependsChange.bindWithEvent( this ,  [elements[j]] ), false );
                }
			}

		}

    },
    setupNested: function(){

        //Get dependant elements
        var has_dependencies  = false;
        var node              = this.node;
        var attribute         = 'ym-form-nested';
        var dependants_map    = this.nested_dependants_map = {};
        var dependency_map    = this.nested_dependency_map = {};        
        var dependants        = node.getElements('*['+ attribute +']');

		this.cleanEvents('Nested');

        //Create dependecy maps        
        for( var i = 0 ; i < dependants.length ; i++ ){

            var dependant      = dependants[i];            
            var property       = dependant.getProperty(attribute).split(":");
            var dependant_name = property[ 0 ];
            var depends_on     = property[1].split(",");
                        
            //Create Dependants Map
            dependants_map[ dependant_name ] = dependant.get('id');
         
            //Create dependency map                                       
            for( var j = 0 ; j < depends_on.length ; j++ ){
                var on = depends_on[j];

                if( !dependency_map[ on ] ) 
                    dependency_map[ on ] = [];

                dependency_map[ on ].push( dependant_name );                
                has_dependencies = true;
            }
            
        }

        //Attach event handlers
		for( var id in dependency_map ){
			if( !dependency_map.hasOwnProperty( id ) ) continue;

            var el = $(id);
            if( !el ) continue;
            
            if( el.attachEvent ){
                //Internet Explorer Attach Events Directly            
                var elements = el.getElements("input , select, textarea ");
                elements.push( el );
            }else{
                //Other Browsers use event delegation
                var elements = [ el ];
            }

            //Attach events
			var len = elements.length;            
			for( var j = 0 ; j < len ; j++ ){                
                if( elements[j].match("input[type='radio']") || elements[j].match("input[type='checkbox']") ){
                    this.registerEvent( 'Nested', elements[j], 'click' , this.onNestedChange.bindWithEvent( this  , [elements[j]] ), false );
                }else{
                    this.registerEvent( 'Nested', elements[j], 'change', this.onNestedChange.bindWithEvent( this , [elements[j]] ), false );
                }
			}
	
		}

    },
    setupLocks: function(){

        var node    = this.node;
        var self    = this;
        node.addEvent('submit' , function(e){
            if( self.locked > 0 ){
                if( confirm('There are pending processes.\n Do you want to wait until they are finished?') ){
                    e.stop();
                }
            }
        });

    },
    setupPlaceHolders: function(){
        
        //Get the elements to placeholder
        var placeholders = this.node.getElements('input[placeholder] , textarea[placeholder]');
        var len          = placeholders.length;
        for( var i = 0 ; i < len ; i++ ){
        
            var el = placeholders[i];

            //Check for placeholder support
            if( 'placeholder' in el )
                continue;

            //Bind Event
            el.addEvent( 'blur'  , this.onPlaceHolderBlur );
            el.addEvent( 'focus' , this.onPlaceHolderFocus );

            //Simulate a blur
            this.onPlaceHolderBlur.call( el );
            
        }
        
        //Remove Placeholders before submit
        this.node.addEvent('submit' , function(){
        
            var len = placeholders.length;
            for( var i = 0 ; i < len ; i++ ){        
                var el = placeholders[i];
                if( el.retrieve('placeholdered') )
                    this.value = '';
            }
        });
            
    
    },
    onPlaceHolderBlur: function(){
        if( this.value == '' ){
            this.value = this.getProperty('placeholder');
            this.setStyle('color' , '#999' );
            this.store('placeholdered' , true );
        }    
    },
    onPlaceHolderFocus: function(){
        if( this.retrieve('placeholdered') ){
            this.value = '';
            this.setStyle('color' , '' );            
            this.store('placeholdered' , false );                    
        }              
    },
    onFocus: function( e ){
       				
        //Get Target
        var target = $( e.target );

        //Get Highlight parent
		//Dont know why $().getParent wasn't working in ie
		var parent = target;
		do{
			parent = $(parent.parentNode);
			if( !parent ) return;
			if( parent.hasClass && parent.hasClass('ym-form-box') ) break;
		}while( true );

        //Check if highlighting is disabled
        if( parent.hasClass('ym-form-box-no-highlight') )
            return false;

        //Check if we need to highlight a container
        var container = parent.getParent('.ym-form-box-highlight');   
        if( container )
            parent = container;
   
        //Highlight
        this.activeElement = parent;
        parent.addClass( "ym-form-box-active" );    

    },
    onBlur: function( e ){
        
        //Remove Class
        if( this.activeElement )
            this.activeElement.removeClass('ym-form-box-active');

    },
    onDependsChange: function( e , target ){

        var id     = target.id;
        var form   = this.node;

		//Stop Event
		if(e ) e.stopPropagation();

        //Check if a depency matches
        var dependecy = false;
        var map       = this.dependency_map;
        if( map.hasOwnProperty( id ) )
            dependecy = map[ id ];
        else{

            //This is a hack for radios/checkboxes
            var parent = target;
            while( parent = parent.getParent() ){
                if( map.hasOwnProperty( parent.id ) ){
                    dependecy = map[parent.id];
                    break;
                }
            }

        }

        if( dependecy ){

            //Make request
            var method = form.get('method');
            var data   = form.toQueryString();
            var headers = {
                'X-YAMMON-REQUEST'         : 'HELPER_FORM-DEPENDS' ,
                'X-YAMMON-REQUEST-DEPENDS' : dependecy.join(',')
            };

            //Do Request
            var req = new Request({
                'method':     method ,
                'url':        window.location.href,
                'data':       data ,
                'headers':    headers ,
                'onComplete': this.onDependsComplete.bind( this )
            }).send();

        }
    },
    onNestedChange: function( e , target ){
                
        var id     = target.id;
        var form   = this.node;

		//Stop Event
		if(e) e.stopPropagation();
   
        //Make request
        var method = form.get('method');
        var data   = form.toQueryString();
        var headers = {
            'X-YAMMON-REQUEST'         : 'HELPER_FORM-NESTED' ,
            'X-YAMMON-REQUEST-NESTED'  : this.nested_dependency_map[ id ].join(',')
        };

        //Do Request
        var req = new Request({
            'method':     method ,
            'url':        window.location.href,
            'data':       data ,
            'headers':    headers ,
            'onComplete': this.onNestedComplete.bind( this )
        }).send();
        
    },
    onDependsComplete: function( response ){

        //Get response
        response = JSON.decode( response );
                   
        //Show hide elements
        for( var i in response ){
            if( !response.hasOwnProperty( i ) ) continue;

            //Get the element/value
            var el    = $( this.dependants_map[i] );
            var value = response[i];
            if( !el ) continue;
                        
            //Show Hide/Element
            if( value ) {
                el.setStyle('display' , '' );
                el.fireEvent( 'dependshow' );
            }
            else {
                el.setStyle('display' , 'none' );
                el.fireEvent( 'dependhide' );
            }

            this.node.fireEvent( 'form:depends' ,  !!value );
            window.fireEvent( 'form:depends'    ,  [el,!!value]  );        

            this.setupDependencies();
        }
    },
    onNestedComplete: function( response ){

        //Get response
        response = JSON.decode( response );
                                   
        //Show hide elements
        for( var i in response ){
            if( !response.hasOwnProperty( i ) ) continue;

            //Get the element/value
            var el    = $( this.nested_dependants_map[i] );
            var id    = el.id;
            var html = response[i];
                        
            if( !el ) continue;
                                          
            //Replace the element
            el.innerHTML = html;

            //Fire Event
            el.fireEvent('nestedcomplete');
            
        }
        
        this.node.fireEvent( 'nestedcomplete' );
        this.setupNested();
        
    },
    registerEvent: function( subWidget, element, type, handler, isCapture ){
    
    	if( !this.eventManager.hasOwnProperty( subWidget ) )
    		this.eventManager[ subWidget ] = [];
    	
		var event = { 
			'element'   : element , 
			'type'      : type ,
			'handler'   : handler ,
			'isCapture' : isCapture
		};
		
		if ( isCapture )
			element.addEventListener( type , handler , true );			
		else
			element.addEvent( type , handler );			
			
		this.eventManager[ subWidget ].push( event );
    },
    cleanEvents: function( subWidget ){

		var objEvent = null;

    	if( !this.eventManager.hasOwnProperty( subWidget ) )
    		this.eventManager[ subWidget ] = [];

        //Remove previous events
        for( i in this.eventManager[ subWidget ] ){
            if( !this.eventManager[ subWidget ].hasOwnProperty( i ) ) continue;

            objEvent = this.eventManager[subWidget][i];


			if ( objEvent.isCapture )
				objEvent.element.removeEventListener( objEvent.type , objEvent.handler, objEvent.isCapture );
			else
				objEvent.element.removeEvent( objEvent.type , objEvent.handler );
        }
        
        this.eventManager[ subWidget ] = [];
    }
});
