module Fm
	# ALL combinations of multiple keys (but without the last letter)
	# or regexps which match combinations need to be in here!
	COMBS = %w(
		g d df y c Z delet cu
		ter ta S ? ?g ?f :q

		/[m`']/ /[fF/!].*/
		/[ri]\d*\w*[^ri]/
		/(cw|cd|mv).*/
		/b(l(o(c(k(.*)?)?)?)?)?/
		/m(k(d(i(r(.*)?)?)?)?)?/
		/r(e(n(a(m(e(.*)?)?)?)?)?)?/
	)

	# Create a regular expression which detects these combos
	ary = []
	for token in COMBS
		if token =~ /^\/(.*)\/$/
			ary << $1
		elsif token.size > 0
			ary << token.each_char.map {|t|
				if t == '?'
					t = '\?'
				end

				"(?:#{t}"
			}.join +
				(')?' * (token.size - 1)) + ')'
		end
	end
	REGX = Regexp.new('^(?:' + ary.uniq.join('|') + ')$')

	def self.ignore_keys_for(t)
		@ignore_until = Time.now + t
	end

	def self.search(str, offset=0, backwards=false)
		begin
			rx = Regexp.new(str, Regexp::IGNORECASE)
		rescue
			return false
		end

		ary = @pwd.files_raw.dup
		ary.wrap(@pwd.pos + offset)

		ary.reverse! if backwards

		for f in ary
			g = File.basename(f)
			if g =~ rx
				@pwd.pointed_file = f
				break
			end
		end
	end

	def self.find_newest()
		newest = nil
		for f in @pwd.files
			if newest.nil? or newest.ctime < f.ctime
				newest = f
			end
		end
		@pwd.pointed_file = newest.path
	end

	def self.hints(str)
		begin
			rx = Regexp.new(str, Regexp::IGNORECASE)
		rescue
			return false
		end

		ary = @pwd.files_raw.dup
		ary.wrap(@pwd.pos)

		n = 0
		pointed = false
		for f in ary
			g = File.basename(f)
			if g =~ rx
				unless pointed
					log "point at #{f}"
					@pwd.pointed_file = f
					pointed = true
				end
				n += 1
			end
		end

		return n
	end

	def self.remember_dir
		@memory["`"] = @memory["'"] = @pwd.path
	end

	def self.press(key)
		return if @ignore_until and Time.now < @ignore_until

		@ignore_until = nil

		if key == '<bs>'
			if @buffer.empty?
				@buffer = key
			elsif @buffer == 'F'
				descend
			elsif @buffer[-1] == ?>
				@buffer.slice! /(<.*)?>$/
			else
				@buffer.slice! -1
			end
		elsif key == '<c-u>'
			@buffer = ''
		else
			@buffer << key
		end

		case @buffer
		when '<redraw>'
			closei
			starti

		when 'j', '<down>'
			@pwd.pos += 1

		when 's'
			closei
			system('clear')
			ls = ['ls']
			ls << '--color=auto' if OPTIONS['color']
			ls << '--group-directories-first' if OPTIONS['color']
			system(*ls)
			system('bash')
			@pwd.schedule
			starti


		when /^S(.)$/
			OPTIONS['sort_reverse'] = $1.ord.between?(65, 90)

			case $1
			when 'n'
				OPTIONS['sort'] = :name
			when 'e'
				OPTIONS['sort'] = :ext
			when 't'
				OPTIONS['sort'] = :type
			when 's'
				OPTIONS['sort'] = :size
			when 'm'
				OPTIONS['sort'] = :mtime
			when 'c'
				OPTIONS['sort'] = :ctime
			end
			@pwd.schedule

		when 'tar'
			closei
			system('tar', 'cvvf', 'pack.tar', *selection.map{|x| x.basename})
			@pwd.refresh!
			starti

		when 'R'
			@pwd.refresh!

		when 'a'
			Process.kill('INT', Process.pid)
#			Process.kill('INT', Process.pid)

		when 'x'
			@bars.first.kill unless @bars.empty?

		when 'X'
			@bars.last.kill unless @bars.empty?

		when 'J'
			@pwd.pos += lines/2

		when 'K'
			@pwd.pos -= lines/2

		when 'cp', 'yy'
			@copy = selection
			@cut = false

		when 'cut'
			@copy = selection
			@cut = true

		when 'n'
			if @search_string.empty?
				find_newest
			else
				search(@search_string, 1)
			end

		when 'N'
			search(@search_string, 0, true)

#		when 'fh'
#			@buffer.clear
#			press('h')

		when /^F(.+)$/
			str = $1
			if str =~ /^\s?(.*)(<cr>|<esc>)$/
				if $2 == '<cr>'
					ascend
					@buffer = 'F'
				else
					@buffer.clear
					@search_string = $1
				end
			else
				test = hints(str)
				if test == 1
					if ascend
						@buffer.clear
					else
						@buffer = 'F'
					end
					ignore_keys_for 0.5
				elsif test == 0
					@buffer = 'F'
					ignore_keys_for 1
				end
			end

		when 'A'
			@buffer = "cw #{currentfile.name}"

		when /^f(.+)$/
			str = $1
			if str =~ /^\s?(.*)(L|;|<cr>|<esc>)$/
				@buffer = ''
				@search_string = $1
				press('l') if $2 == ';' or $2 == 'L'
			else
				test = hints(str)
				if test == 1
					@buffer = ''
					press('l')
					ignore_keys_for 0.5
				elsif test == 0
					@buffer = ''
					ignore_keys_for 1
				end
			end

		when /^\/(.+)$/
			str = $1
			if str =~ /^\s?(.*)(L|;|<cr>|<esc>)$/
				@buffer = ''
				@search_string = $1
				press('l') if $2 == ';' or $2 == 'L'
			else
				search(str)
			end

		when /^mkdir(.*)$/
			str = $1
			if str =~ /^\s?(.*)(<cr>|<esc>)$/
				@buffer = ''
				if $2 == '<cr>'
					closei
					system('mkdir', $1)
					starti
					@pwd.schedule
				end
			end

		when /^block.*stop$/
			@buffer = ''
			
		when /^!(.+)$/
			str = $1
			if str =~ /^(\!?)(.*)(<cr>|<esc>)$/
				@buffer = ''
				if $3 == '<cr>'
					closei
					system("bash", "-c", $2)
					gets unless $1.empty?
					starti
					@pwd.schedule
				end
			end

		when /^cd(.+)$/
			str = $1
			if str =~ /^\s?(.*)(<cr>|<esc>)$/
				@buffer = ''
				if $2 == '<cr>'
					remember_dir
					enter_dir_safely($1)
				end
			end

		when /^(mv|cw|rename)(.+)$/
			str = $2
			if $1 == 'mv'
				if str =~ /['`"]([\w\d])/
					if path = @memory[$1]
						str = ''
						@buffer.clear
						if File.exists?(path) and File.directory?(path)
							Action.move(selection, path)
						end
					end
				end
			end
			log str
			if str =~ /^\s?(.*)(<cr>|<esc>)$/
				@buffer = ''
				if $2 == '<cr>'
					files = selection
					if files.size == 1
						fn = $1
						log "!!! #{fn}"
						unless fn.include? '.'
							if ext = files.first.basename.from_last('.')
								fn << ".#{ext}"
							end
							log "??? #{ext}"
						end
						Action.move(files, fn)
						@pwd.refresh!
						@pwd.find_file(fn)
					else
						Action.move(files, $1)
						@pwd.refresh!
					end
				end
			end

		when 'tc'
			OPTIONS['color'] ^= true

		when 'tf'
			OPTIONS['filepreview'] ^= true

		when 'th'
			OPTIONS['hidden'] ^= true
			@pwd.refresh!

		when 'td'
			OPTIONS['dir_first'] ^= true
			@pwd.schedule

		when 'delete'
			files = selection
			@marked = []
			for f in files
				if f and f.exists? and f.dir?
					system('rm', '-r', f.to_s)
					@pwd.schedule
				end
			end

		when 'p'
			if @cut
				Action.move(@copy, @pwd.path)
				@cut = false
			else
				Action.copy(@copy, @pwd.path)
			end
			@pwd.refresh!
			if @copy.size == 1
				@pwd.find_file(@copy[0].basename)
			end

		when /^[`'](.)$/
			if dir = @memory[$1] and not @pwd.path == dir
				remember_dir
				enter_dir_safely(dir)
			end

		when '<s-tab>'
			if dir = @memory['`'] and not @pwd.path == dir
				remember_dir
				enter_dir_safely(dir)
			end
			
		when '<tab>'
			if dir = @memory['9'] and dir != '/'
				unless @pwd.path == dir
					enter_dir_safely(dir)
				end
			elsif dir = @memory['`'] and not @pwd.path == dir
				remember_dir
				enter_dir_safely(dir)
			end
			

		when /^m(.)$/
			@memory[$1] = @pwd.path

		when ' '
			if currentfile.marked
				@marked.delete(currentfile)
				currentfile.marked = false
			else
				@marked << currentfile
				currentfile.marked = true
			end

			@pwd.pos += 1

		when 'v'
			@marked = []
			for file in @pwd.files
				if file.marked
					file.marked = false
				else
					file.marked = true
					@marked << file
				end
			end

		when 'V'
			for file in @marked
				file.marked = false
			end
			@marked = []


		when 'gg'
			@pwd.pos = 0

		when 'dd'
			new_path = move_to_trash(currentfile)
			if new_path
				new_path = Directory::Entry.new(new_path)
				new_path.get_data
				@copy = [new_path]
				@cut = false
			end
			@pwd.schedule

		when 'dD', 'dfd'
			cf = currentfile
			if cf and cf.exists?
				cf.delete!
				@pwd.schedule
			end

		when 'term'
			fork do exec 'x-terminal-emulator' end

		when 'g0'
			remember_dir
			enter_dir('/')

		when 'gh'
			remember_dir
			enter_dir('~')

		when 'gu'
			remember_dir
			enter_dir('/usr')

		when 'ge'
			remember_dir
			enter_dir('/etc')

		when 'gm'
			remember_dir
			enter_dir('/media')

		when 'gt'
			remember_dir
			enter_dir('~/.trash')

		when 'G'
			@pwd.pos = @pwd.size - 1

		when 'k', '<up>'
			@pwd.pos -= 1

		when '<bs>', 'h', 'H', '<left>'
			descend

		when 'E'
			cf = currentfile.path
			unless cf.nil? or enter_dir_safely(cf)
				closei
				system VI % cf
				starti
			end

		when '<cr>', 'l', ';', 'L', '<right>'
			ascend(@buffer=='L', @buffer=='l')

		# a = run all
		# d or e = detach
		# t = run in a terminal
		# w = wait for <enter> after execution
		# capital letter inverts
		when /^[ri](\d*)([adetw]*)[ri]$/
			if $2.empty?
				f = @marked.empty?? currentfile : @marked.first
				flags = get_default_flags(f)
			else
				flags = $2
			end
			opt = OpenStruct.new
			opt.newway = true

			opt.mode = $1.to_i unless $1.empty?

			# Set options based on flags
			
			if flags =~ /a/
				opt.all = true
			end
			if flags =~ /[de]/
				opt.detach = true
			end
			if flags =~ /t/
				opt.new_term = true
				opt.detach = true
			end
			if flags =~ /w/
				opt.wait = true
			end

			if flags =~ /A/
				opt.all = false
			end
			if flags =~ /[DE]/
				opt.detach = false
			end
			if flags =~ /T/
				opt.new_term = false
			end
			if flags =~ /W/
				opt.wait = false
			end

			Action.run(opt.__table__)
		
#		when 'ra'
#			unless File.directory?(currentfile.path)
#				Action.run(:all=>true)
#			end

		when 'ZZ', '<c-d>', ':q<cr>', 'Q'
			exit
			
		when '<c-r>'
			Fm.boot_up

		when "-", "="
			val = "2#{key=='-' ? '-' : '+'}"
			system("amixer", "-q", "set", "PCM", val, "unmute")

		else
#			log key.ord

		end

		@buffer = '' unless @buffer == '' or @buffer =~ REGX
	end
	
	def self.ascend(wait = false, all=false)
		if all and !@marked.empty?
			closei
			system(*['mplayer', '-fs', *@marked.map{|x| x.path}])
			starti
			return true
		else
			cf = currentfile
			enter = enter_dir_safely(cf.path)
			unless enter
				return Action.run(:detach=>false)
			end
			return false
		end
	end

	def self.descend
		unless @path.size == 1
			enter_dir(@buffer=='H' ? '..' : @path[-2].path)
		end
	end
end