require 'rexml/sax2parser'
require 'rouge/builder'

module Rouge
	class Xul
		include RougeTK

		attr_accessor :script_handler
		attr_reader :widgets

		def initialize preferred='fox'
			_rouge_setup(preferred)
			@widgets = {}
		end

		def parse source
			doc = REXML::Document.new( source )
			@script = ScriptEnv.new( self )
			source.rewind if source.kind_of? IO
			sax = REXML::SAX2Parser.new(source)
			sax.listen( :end_element ) { |uri,local,qname|
				script = false if local == 'script'
				@toolkit.exit_widget unless local.downcase == "script"
			}
			sax.listen( :end_document ) {
				begin
					@script.instance_eval( @onload ) if @onload
				rescue Exception
					puts $!
				end
				@toolkit.run
			}
			sax.listen( :start_element, "window" ) { |uri,local,qname,attributes|
				@onload = attributes[ 'onload' ]
				title = attributes[ 'title' ]
				title = 'NO TITLE' unless title
				push( @toolkit.window(title), attributes )
			}
			sax.listen( :start_element, 'vbox' )  { |uri,local,qname,attributes|
				push( @toolkit.box( :vertical ), attributes )
			}
			sax.listen( :start_element, 'hbox' )  { |uri,local,qname,attributes|
				push(@toolkit.box( :horizontal ), attributes)
			}
			sax.listen( :start_element, 'button' )  { |uri,local,qname,attributes|
				label = attributes[ 'label' ]
				command = attributes[ 'oncommand' ]
				command = '' unless command
				button = @toolkit.button( label ) { 
					begin
						@script.instance_eval( command ) 
					rescue Exception
						puts $!
					end
				}
				push(button, attributes)
			}
			sax.listen( :start_element, 'text' ) { |uri,local,qname,attributes|
				value = attributes['value']
				push( @toolkit.text( value ), attributes )
			}
			sax.listen( :start_element, 'textbox' ) { |uri,local,qname,attributes|
				value = attributes['value']
				push( @toolkit.textbox( ), attributes )
			}
			sax.listen( :start_element, 'radiogroup' ) {|uri,local,qname,attributes|
				group = attributes['id']
				orient = attributes['orient']
				push( @toolkit.radiogroup( group ), attributes )
			}
			sax.listen( :start_element, 'radio' ) {|uri,local,qname,attributes|
				label = attributes['label']
				group = attributes['group']
				disabled = attributes['disabled']
				checked = attributes['checked']
				push( @toolkit.radio( label, group ), attributes )
			}
			sax.listen( :start_element, 'checkbox' ) {|uri,local,qname,attributes|
				label = attributes['label']
				id = attributes['id']
				disabled = attributes['disabled']
				checked = attributes['checked']
				push( @toolkit.checkbox( label ), attributes )
			}
			#     FXSlider.new(matrix, @intTarget, FXDataTarget::ID_VALUE,
			# LAYOUT_CENTER_Y|LAYOUT_FILL_ROW|LAYOUT_FIX_WIDTH, 0, 0, 100)
			# FXDial.new(matrix, @intTarget, FXDataTarget::ID_VALUE, (LAYOUT_CENTER_Y|
			# LAYOUT_FILL_ROW|LAYOUT_FIX_WIDTH|DIAL_HORIZONTAL|DIAL_HAS_NOTCH),
			# 0, 0, 100)
			# FXSpinner.new(matrix, 5, @intTarget, FXDataTarget::ID_VALUE,
			# SPIN_CYCLIC|FRAME_SUNKEN|FRAME_THICK|LAYOUT_CENTER_Y|LAYOUT_FILL_ROW)
			# FXProgressBar.new(matrix, @intTarget, FXDataTarget::ID_VALUE,
			# (LAYOUT_CENTER_Y|LAYOUT_FILL_X|FRAME_SUNKEN|FRAME_THICK|
			# PROGRESSBAR_PERCENTAGE|LAYOUT_FILL_COLUMN|LAYOUT_FILL_ROW))
			sax.listen( :start_element, 'separator' ) {|uri,local,qname,attributes|
				push( @toolkit.separator( ), attributes )
			}
			sax.listen( :start_element, 'progressmeter' ) {|uri,local,qname,attributes|
				push( @toolkit.progressmeter( ), attributes )
			}
			sax.listen( :start_element, 'menubutton' ) {|uri,local,qname,attributes|
				@toolkit.stub
			}

			sax.listen( :start_element, 
				['menupopup', 'menuitem', 'menulist', 'spacer', 'scrollbar' ] ) {
				|uri,local,qname,attributes|
				puts "#{local} is unimplemented"
				@toolkit.stub
			}
			sax.listen( :start_element, 'script' ) { |uri,local,qname,attributes|
				type = attributes['type']
				src = attributes['src']
				# FIXME: This is wrong.  src should be a URL
				# There should also be some sandboxing here
				if src
					f = File.new( src )
					eval(f.read)
				else
					script = true
				end
			}
			sax.listen( :characters ) { |text|
				return if text.strip.size == 0
				begin 
					@script.instance_eval( text )
				rescue Exception
					puts $!
				end
			}
			sax.listen( :cdata ) { |text|
				return if text.strip.size == 0
				begin 
					@script.instance_eval( text )
				rescue Exception
					puts $!
				end
			}
			sax.parse
			@toolkit
		end

		def push( widget, attributes )
			@widgets[attributes['id']] = widget if attributes['id']
			widget
		end
	end

	class ScriptEnv
		def initialize( rouge )
			@rouge = rouge
		end
		def setTimeout( method, timeout ) 
		end
		def prompt( msg1, msg2 )
			window = @rouge.toolkit.window("Prompt")
			@rouge.toolkit.text( msg1 )
			@rouge.toolkit.exit_widget
			tb = @rouge.toolkit.textbox
			tb.value = msg2
			@rouge.toolkit.exit_widget
			@rouge.toolkit.box( :horizontal )
			rvalue = nil
			@rouge.toolkit.button( "Ok" ) {
				rvalue = tb.value
				window.close
			}
			@rouge.toolkit.exit_widget
			@rouge.toolkit.button( "Cancel" ) {
				window.close
			}
			3.times { @rouge.toolkit.exit_widget }
			window.show
			return rvalue
		end
	end

	class WidgetWrapper
		def initialize widget, mapping
			@widget = widget
			@mapping = mapping
		end

		def []( key )
			@widget.send( @mapping[ key ] )
		end

		def []=( key, value )
			@widget.send( @mapping[ key ]+"=", value )
		end

		def method_missing( method, *args )
			method = method.to_s
			if @mapping[ method ]
				if @mapping[ method ].is_a? Proc
					@mapping[ method ].call( @widget )
				else
					@widget.send( @mapping[ method ], *args )
				end
			else
				puts
				puts "MISSING METHOD \"#{@mapping[method]}\" FOR WIDGET #{@widget.to_s}"
				puts "ARGUMENTS: "
				print "\t"
				p args
				puts "MAPPINGS: "
				print "\t"
				p @mapping
			end
		end
	end
end
