/**
 * @browser IE8
 * An issue exists in IE where the `innerHTML' property of elements added to
 * the DOM during the onload event are not accessible.
 */
function visage_maxlength( obj ) {
	if( obj.value.length > obj.maxLength ) {
		obj.value = obj.value.substring( 0, obj.maxLength );
	}
	
	var wordCount = obj.maxLength - obj.value.length;
	var wordCountElement = document.createElement( 'strong' );
	wordCountElement.appendChild( document.createTextNode( wordCount ) );
	
	clearNode( obj.statusBar );
	obj.statusBar.appendChild( wordCountElement );
	obj.statusBar.appendChild( document.createTextNode(
		' Character'+( wordCount == 1 ? '' : 's' )+' Remaining'
	) );
}

function show_definition( def ) {
/*	def.style.top	=
		(getObjY( this ) + getObjHeight( this ))
			+ 'px';

	def.style.left	=
		(getObjX( this ) + 24 + (isIE() ? 18 : 0))
			+ 'px';
		
	def.style.width =
		(getObjWidth( this ) - 20)
			+ 'px'*/
	
	def.style.display = 'block';
	
	if( isIE6( ) ) {
		this.style.color = '#3366FF';
		this.style.cursor = 'pointer';
	}
	
	if( isIE( ) ) {
		def.style.backgroundColor = 'white';
	}
	
//	def.parentNode.getElementsByTagName( 'input' )[ 0 ].focus( );
}

function hide_definition( def ) {
	def.style.display = 'none';
	
	if( isIE6( ) ) {
		this.style.color = '';
		this.style.cursor = 'default';
	}
	
//	def.parentNode.getElementsByTagName( 'input' )[ 0 ].blur( );
}

var visageCollapsedFieldsets = new Hash.Cookie( 'sp_collapsed_groups' );
function visage_get_collapsed_fieldsets( ) {
	return visageCollapsedFieldsets;
}

function visage_attach_attributes( form ) {
	var collapsedFieldsets = visage_get_collapsed_fieldsets( );
	
	var txtArr = form.getElements( 'textarea' );
	var iptArr = form.getElements( 'input' );
	var divArr = form.getElements( 'div' );
	var fldSetArr = form.getElements( 'fieldset' );
	var lblArr = form.getElements( 'label' );
	var trArr = isIE6( ) ? [] : form.getElements( 'tr[class*=hier]' );
	
	var ctlArr = [];

	for( var ii = 0; ii < txtArr.length; ii++ ) {
		ctlArr.push( txtArr[ii] );
		}

	for( var ii = 0; ii < iptArr.length; ii++ ) {
		ctlArr.push( iptArr[ii] );
		}
		
	for( var ii = 0; ii < divArr.length; ii++ ) {
		ctlArr.push( divArr[ii] );
		}
	
	for( var ii = 0; ii < fldSetArr.length; ii++ ) {
		ctlArr.push( fldSetArr[ ii ] );
	}
	
	for( var ii = 0; ii < lblArr.length; ii++ ) {
		ctlArr.push( lblArr[ ii ] );
	}
	
	for( var ii = 0; ii < trArr.length; ii++ ) {
		ctlArr.push( trArr[ ii ] );
	}
	
	if( ctlArr ) {
		for( var ii = 0; ii < ctlArr.length; ii++ ) {
			var attr = ctlArr[ii].className.split( ' ' );
			var ctl	 = $( ctlArr[ii] );
			for( var jj = 0; jj < attr.length; jj++ ) {
				if( attr[jj].length > 1 ) {
					var p = attr[jj].split( ':' );
					if( p.length > 1 ) {
						switch( p[0] ) {
							
							case 'hier':
								ctl.addEvents( {
									 'mouseenter': function( id ) {
										$$( 'tr.sib-'+id ).addClass( 'siblings' );
									  }.bind( null, p[1] )
									
									,'mouseleave': function( id ) {
										$$( 'tr.sib-'+id ).removeClass( 'siblings' );
									  }.bind( null, p[1] )
								} );
								break;
							
							case 'tab':
								var tabViewId = p[0]+':'+p[1];
								ctl = $( ctl );
								
								if( !$( tabViewId ) ) {
									var tabView = getSplayTabCollection( tabViewId );
									tabView.attach( ctl, 'before' );
								}
								
								tabView.add( new SplayTab(
									  decodeURIComponent( p[3] )
									, ctl
								) );
								
								break;
							
							case 'tabToggle':
								var tabView = getSplayTabCollection( 'tab:'+p[1] );
								var tabLabel = decodeURIComponent( p[2] );
								var checkBox = $( ctl.htmlFor );
								
								checkBox.onclick = bind(
									  checkBox
									, function( label ) {
										if( this.checked ) {
											tabView.showLabel( label );
										}
										else {
											tabView.hideLabel( label );
										}
									  }
									, tabLabel
								);
								
								if( !checkBox.checked ) {
									tabView.hideLabel( tabLabel );
								}
								break;
							
							case 'combo':
								if( isset( p[1] ) ) {
									new JsonComboBox(
										  ctl
										, eval( p[1] )
										, { showMatchesMenu: false }
									);
								}
								// else AjaxComboBox?
								break;
							
							case 'autosize':
								var yes	= (p[1] == 'yes');
								if( yes ) {
									if( !ctlArr[ii].style.height ) {
										var vlen = ctlArr[ii].value.length;
										ctlArr[ii].style.height =
											 parseInt( 6 + vlen / 66 )
											+'em';
										}
									}
								break;
								
							case 'maxlength':
								var maxlen = parseInt( p[1] );
								
								ctlArr[ii].statusBar =
									appendSibling(
										 ctlArr[ii]
										,document.createElement( 'div' )
										);

								ctlArr[ii].statusBar.className =
									'viStatusBar';
										
								ctlArr[ii].maxLength	= parseInt( p[1] );
								
								ctlArr[ii].onkeyup		=
								ctlArr[ii].onchange		=
									bind(
										 null
										,visage_maxlength
										,ctlArr[ii]
										);
									
								if( ctlArr[ii].tagName == 'textarea' ) {
									ctlArr[ii].style.height	=
										parseInt(
											 6 + ctlArr[ii].maxLength / 66
											) + 'em';
									}
										
										
								visage_maxlength( ctlArr[ii] );
								
								break;
							}
						}
					else {
						switch( p[ 0 ] ) {
							case 'wysiwyg':
								ctl = ctl.getElementsByTagName( 'textarea' )[0];
								if( !ctl.style.height ) {
									var vlen = 6 + ctl.value.length / 66;
									var maxlen = window.screen.availHeight * .7;
									ctl.setStyle( 'height', parseInt( vlen )+'em' );
									ctl.setStyle( 'height', parseInt(
										Math.min( getObjHeight( ctl ), maxlen )
									)+'px' );
								}
								tinyMCE.execCommand(
									 'mceAddControl'
									, false
									, ctl.id
								);
								break;
								
							case 'copy':
								$( ctl ).getFirst( ).addEvent(
									 'change'
									, function( ) {
										this.set( 'tween', { duration: 'short' } );
										this.tween( 'background-color', '#fff' );
									  }
								);
								break;
							
							case 'collapsable':
							case 'expandable':
								// @browser IE7. It doesn't apply overflow:hidden to grandchildren, and we can't use display:none because the form data won't submit for this fields.
								var lgnd = ctl.getElementsByTagName( 
									'legend'
								);
								if( lgnd ) {
									lgnd = lgnd[ 0 ];
									$( lgnd ).addEvent(
										 'click'
										, toggle_fieldset.pass( ctl, lgnd )
									);
								}
								
								var ctlId = ctl.get( 'id' );
								
								// @browser IE8-: Doesn't support string as array notation. p[0][0] won't work here.
								if( !collapsedFieldsets.has( ctlId ) ) {
									collapsedFieldsets.set( ctlId, p[0].charAt( 0 ) );
								}
								else {
									if( p[0].charAt( 0 ) !== collapsedFieldsets.get( ctlId ) ) {
										lgnd.fireEvent( 'click' );
									}
								}
								break;
							
							case 'definition':
								var lbl = ctl
									.parentNode
									.getElementsByTagName( 'label' )[ 0 ];
								
								var inpt = ctl
									.parentNode
									.getElementsByTagName( 'input' )[ 0 ];
								
								if( lbl ) {
//									ctl.parentNode.removeChild( ctl );
//									document.body.appendChild( ctl );
									ctl.className = 'viHoverDefinition';
									
/*									if( isIE6( ) ) {
										continue;
									}*/
									
									lbl.onfocus
										= lbl.onmouseover
										= inpt.onfocus
										= inpt.onmouseover
//										= ctl.onfocus
//										= ctl.onmouseover
										= bind( lbl, show_definition, ctl );

									lbl.onblur
										= lbl.onmouseout
										= lbl.onclick
										= inpt.onblur
										= inpt.onmouseout
										= inpt.onclick
//										= ctl.onblur
//										= ctl.onmouseout
//										= ctl.onclick
										= bind( lbl, hide_definition, ctl );
									}
								break;
							
							/**
							 *	@browser IE
							 *	
							 *	Disable focus/blur silliness for IE6 because in 
							 *	cases where a link in the label open in a new 
							 *	window, focusing the label	on mouseenter/leave 
							 *	will blur the new window and send it to the 
							 *	back. Plus IE already focuses checkboxes on 
							 *	label hover -- so no need to mimic it.
							 */
							case 'tree':
								ctl.getElements( '.node' ).each( function( node ) {
									var lbl = node.getElements( 'label' )[0];
									var ctl = node.getElements( 'input' )[0];
									
									lbl.addEvents( {
										 'mouseenter': function( ) {
											if( !isIE( ) ) {
												ctl.focus( );
											}
										  }
										,'mouseleave': function( ) {
											if( !isIE( ) ) {
												ctl.blur( );
											}
										  }
									} );
									
									lbl.getElements( 'a' ).addEvents( {
										 'click': function( e ) {
										 	window.open( this.href );
											e.stop( );
										  }
										,'mouseenter': function( e ) {
											if( !isIE( ) ) {
												ctl.blur( );
											}
											e.stop( );
										  }
										,'mouseleave': function( e ) {
											if( !isIE( ) ) {
												ctl.focus( );
											}
											e.stop( );
										  }
									} );
								} );
								break;
							
							case 'map':
								var container = ctl.getElement( '#googlemap' );
								
								window.addEvent( 'mapload', function( map ) {
									map.center_mark.setDraggable( true );
									map.center_mark.infoWindow =
										new google.maps.InfoWindow( {
											  content: 'You may drag this marker to adjust your geocoded location.'
											, maxWidth: container.getDimensions( ).x * .45
										} );
									
									map.center_mark.infoWindow.open(
										  map
										, map.center_mark
									);
									
									google.maps.event.addListener( 
										  map.center_mark
										,'dragend'
										, function( e ) {
											google_map_click_handler( e );
										  }
									);
								} );
								break;
							}
						}
					}
				}
			}
		}
	}

function toggle_fieldset( fldSet ) {
	var fldSetId = fldSet.get( 'id' );
	var collapsedFieldsets = visage_get_collapsed_fieldsets( );
	
	var newClassName = 'collapsable';
	var oldClassName = 'expandable';
	
	if( hasClass( fldSet, 'collapsable' ) ) {
		newClassName = 'expandable';
		oldClassName = 'collapsable';
	}
	
	collapsedFieldsets.set( fldSetId, newClassName.charAt( 0 ) );
	
	swapClass( fldSet, newClassName, oldClassName );
}

function visage_attach_array_ctls( arrayCtlGroups ) {
	var path = auri( 'resource/image/splay/' );
	
	var addBtn = new Asset.image(
		  path+'add.png'
		, { 'title': 'Add a new one' }
	).addClass( 'add' );
	
	var delBtn = new Asset.image(
		  path+'remove.png'
		, { 'title': 'Delete this one' }
	).addClass( 'remove' );
	
	var mvUBtn = new Asset.image(
		  path+'up.png'
		, { 'title': 'Move up one' }
	).addClass( 'up' );
	
	var mvDBtn = new Asset.image(
		  path+'down.png'
		, { 'title': 'Move down one' }
	).addClass( 'down' );
	
	var onclick = function( ctlGroup, arrayGroup ) {
		switch( this.retrieve( 'type' ) ) {
			case 'add':
				var newCtlGroup = ctlGroup.clone( );
				newCtlGroup.getElements( '.nav' )[0].destroy( );
				cloneNav( newCtlGroup, arrayGroup );
				
				newCtlGroup
					.getElements( 'input, select, textarea' )
					.each( function( ctl ) {
						ctl.erase( 'value' );
					} );
				
				var vaultCtls = newCtlGroup.getElements( 'label.vault' );
				vaultCtls.each( function( newVaultCtl, i ) {
					var span = newVaultCtl.getElements( 'span' )[0];
					var input = newCtlGroup.getElements( 'input' )[0];
					
					var id = ctlGroup.getElements( 'label' )[i]
						.getElements( 'input' )[0]
						.get( 'id' )
						.replace( /(\d+)/, function( m ) { return ++m; } );
					
					span.empty( ).set( { 'id': id+'_hardpoint', 'html': 'Loading Vault control&hellip;' } );
					input.set( { 'id': id , 'name': id } );
				} );
				
				newCtlGroup.inject( ctlGroup, 'after' );
				
				vaultCtls.each( function( ctl, i ) {
					var boundControl = ctlGroup.getElements( 'label' )[i]
						.getElements( 'span' )[0]
						.retrieve( 'vaultControl' );
					
					new boundControl.constructor(  // Could be WriteOnlyVC or VC
						  ctl.getElements( 'span' )[0].get( 'id' )
						, ctl.getElements( 'input' )[0].get( 'id' )
						, 0
						, null
						, boundControl.getDynamic( )
						, boundControl.getImageWidth( )
						, boundControl.getImageHeight( )
						, boundControl.getSavePath( )
						, boundControl.getAssetKind( )
					);
					
					if( !ctlGroup.didEndClick ) {
						ctlGroup.didEndClick = function( ) {
							this.onclick( );
							ctlGroup.didEndClick = null;
						}.bind( ctl.getElements( '.vaultSummary .icon img' )[0] );
					}
				} );
				break;
			case 'remove':
				// FIXME: maybe add an undo/redo feature instead?
				if( isCtlEmpty( ctlGroup ) || confirm( 'Really delete?' ) ) {
					if( arrayGroup.getChildren( ).length == 1 ) {
						ctlGroup.didEndClick = function( ) {
							ctlGroup.didEndClick = null;
						}
						ctlGroup.getElements( '.nav img.add' )
							.fireEvent( 'click' );
					}
					ctlGroup.destroy( );
				}
				break;
			case 'up':
				var prev = ctlGroup.getPrevious( );
				if( prev ) {
					ctlGroup
						.dispose( )
						.inject( prev, 'before' );
				}
				break;
			case 'down':
				var next = ctlGroup.getNext( );
				if( next ) {
					ctlGroup
						.dispose( )
						.inject( next, 'after' );
				}
				break;
		}
		
		arrayGroup.refresh( );
		
		if( ctlGroup.didEndClick ) {
			ctlGroup.didEndClick( );
		}
	}
	
	var isCtlEmpty = function( ctl ) {
		var ctls = ctl.getElements( 'input, select, textarea' );
		for( var i = ctls.length - 1; i >= 0; i-- ) {
			if( ctls[ i ].get( 'value' ) != false ) {
				return false;
			}
		}
		return true;
	}
	
	var navClick = null;
	var nav = new Element( 'div', { 'class': 'nav' } )
		.adopt( [ addBtn, mvUBtn, mvDBtn, delBtn ] );
	
	nav.enable = function( btn, args ) {
		btn.addClass( 'hoverable' ).removeClass( 'inactive' );
		
		// Reset click event. Clear it first to prevent dup events.
		btn.removeEvents( 'click' );
		btn.addEvent( 'click', function( ) {
			onclick.attempt( args, btn );
		} );
	}
	nav.disable = function( btn ) {
		btn.addClass( 'inactive' ).removeClass( 'hoverable' );
		btn.removeEvents( 'click' );
	}
	
	var cloneNav = function( ctl, group ) {
		var ctlNav = nav.clone( false );
		nav.getChildren( ).each( function( btn ) {
			var cbtn = btn.clone( );
			cbtn.store( 'type', btn.get( 'class' ) );
			cbtn.addEvent( 'click', navClick = function( ) {
				onclick.bind( cbtn, ctl, group )( );
			} );
			ctlNav.grab( cbtn );
		} );
		
		ctl.grab( ctlNav, 'bottom' );
		ctlNav.setStyle( 'opacity', .1 );
		
		ctl.addEvent( 'mouseenter', function( e ) {
			ctlNav.fade( 'show' );
		} );
		
		ctl.addEvent( 'mouseleave', function( e ) {
			ctlNav.setStyle( 'opacity', .1 );
		} );
	}
	
	var refreshGroup = function( ) {
		var groups = this.getChildren( );
		groups.each( function( group, groupID ) {
			group
				.removeClass( 'first' )
				.removeClass( 'last' );
			
			group.getElements( '.nav' )[0].getChildren( ).each( function( btn ) {
				nav.enable( btn, [ group, this ] );
				
				switch( btn.retrieve( 'type' ) ) {
					case 'up':
						if( groupID == 0 ) {
							group.addClass( 'first' );
							nav.disable( btn );
						}
						break;
					case 'down':
						if( groupID == groups.length - 1 ) {
							group.addClass( 'last' );
							nav.disable( btn );
						}
						break;
					case 'remove':
					case 'add':
						break;
				}
			}, this );
			
			group
				.getElements( 'input' )
				.append( group.getElements( 'select' ) )
				.append( group.getElements( 'textarea' ) )
				.each( function( ctl ) {
					ctl.set( 'name', ctl.name.replace( /\d+/, groupID ) );
				} );
			
			group.getElements( 'label.vault' ).each( function( ctl ) {
				var input = ctl.getElements( 'input' )[0]
				var id = input.get( 'id' ).replace( /\d+/, groupID );
				
				ctl.getElements( 'span' )[0].set( 'id', id+'_hardpoint' );
				input.set( 'id', id ).set( 'name', id );
			} );
		}, this );
	}
	
	arrayCtlGroups.each( function( group ) {
		group.getElements( '.viControlGroup' ).each( function( ctl ) {
			cloneNav( ctl, group );
		} );
		group.refresh = refreshGroup;
		group.refresh( );
	} );
}

var ComboBox = new Class( {
	  Implements: [ Options ]
	
	, options: {
		  showMatchesMenu: true
	  }
	
	, control: null
	, matchesMenu: null
	
	, initialize: function( ctl, options ) {
		this.setOptions( options );
		
		ctl.addEvents( {
			  keypress: this.keyPressHandler.bind( this )
			, keyup: this.keyUpHandler.bind( this )
		} );
		
		if( this.options.showMatchesMenu ) {
			this.matchesMenu = new Element( 'ul.viMatches' );
		}
		
		this.control = ctl;
	  }
	
	  // The control's value is actually updated between the keypress and keyup events. But the keyup event will also trigger when any modifier keys are released. So check here to make sure the keyed input is actually something that would've changed the control's value.
	, keyPressHandler: function( e ) {
//		console.log( 'Key Pressed:', e.key, this.control.getSelectionStart( ), e );
		this._lastInput = this.control.get( 'value' );
	  }
	
	, keyUpHandler: function( e ) {
		var input = this.control.get( 'value' );
		
//		console.log( 'Input changed?', '"'+input+'"', this._lastInput, input !== this._lastInput );
		if( input ) {
			if( input !== this._lastInput ) {
//				console.log( 'Key Up:', e.key, e );
				
				if( this.options.showMatchesMenu ) {
					this.matchesMenu.empty( );
				}
				
				var matches = this.findMatches( input );
//				console.log( 'Input/matches:', input, matches );
				
				if( matches.length ) {
					// Only fill if the cursor is at the end. Otherwise filler will appear in the middle of a word. Still show matches, though. To illustrate the error: for the match "Addison", type in "addso", insert the missing "i" and watch the fill get applied after the cursor, resulting in "addino". Moving the caret to the end first is too unexpected.
					if( this.control.getCaretPosition( ) === input.length ) {
						switch( e.key ) {
							case 'backspace':
							case 'delete':
							case 'esc':
								break;
							
							default:
//								console.log( matches[0], this.control.getCaretPosition( ), input.length, matches[0].slice( input.length ) );
								
								this.control.insertAtCursor(
									  matches[0].slice( input.length )
									, true
								);
								break;
						}
					}
					
					if( this.options.showMatchesMenu ) {
						matches.each( function( match ) {
							this.matchesMenu.grab( new Element( 'li.viMatch', {
								  text: match
								, events: {
									click: function( e ) {
										this.control.set(
											 'value'
											, e.target.get( 'text' )
										);
										this.matchesMenu.empty( );
									}.bind( this )
								  }
							} ) );
						}, this );
						
						this.control.grab( this.matchesMenu, 'after' );
					}
				}
			}
		}
		else {
			if( this.options.showMatchesMenu ) {
				this.matchesMenu.empty( );
			}
		}
	  }
} );

// Filter input against ajax call. Build out when/if needed.
var AjaxComboBox = new Class( {
	 Extends: ComboBox
} );

// Filter input against JSON array of valid values.
var JsonComboBox = new Class( {
	 Extends: ComboBox
	
	, filter: []
	, filterDeliminator: ';'
	
	, initialize: function( ctl, filter, options ) {
		this.parent( ctl, options );
		this.filter =
			  this.filterDeliminator
			+ filter.join( this.filterDeliminator )
			+ this.filterDeliminator;
	  }
	
	, findMatches: function( keyword ) {
		var matches = [];
		
//		console.log(keyword);
		var regex = new RegExp(
			  this.filterDeliminator
			  +'('//[^'+this.filterDeliminator+']*'  // Uncomment to search for an occurrence of the keyword anywhere within a word.
			  + keyword
			  +'[^'+this.filterDeliminator+']*)'
			,'gi'
		);
		
//		console.log(regex.source);
		while( ( match = regex.exec( this.filter ) ) !== null ) {
//			console.log(match);
			matches.push( match[1] );
			
			if( !this.options.showMatchesMenu ) {
				break;
			}
		}
		
		return matches;
	  }
} );

window.addEvent( 'domready', function( ) {
	var form = $$( '.viBoundary' );
	if( form.length ) {
		visage_attach_attributes( form[0] );
	}
} );

window.addEvent( 'domready', function( ) {
	var arrayCtlGroups = $$( '.viArrayGroup' );
	if( arrayCtlGroups.length ) {
		visage_attach_array_ctls( arrayCtlGroups );
	}
} );
