^bind ^i parse_command TabKey.expand 1
^bind ^r parse_command TabKey.expand -1

alias TabKey.chaseNick {
	@LIGHT.Data.TabKey.nickList = chngw($findw($0 $LIGHT.Data.TabKey.nickList) $1 $LIGHT.Data.TabKey.nickList)
}

alias TabKey.addNick (what) {
	@LIGHT.Data.TabKey.nickList = uniq($what $LIGHT.Data.TabKey.nickList)
	@LIGHT.Data.TabKey.index = 0
	@LIGHT.Data.Tabkey.areTabbing = 0
}

alias TabKey.remNick (what) {
	@LIGHT.Data.TabKey.nickList = remw($what $LIGHT.Data.TabKey.nickList)
}

alias TabKey.nickComplete {
	TabKey.complete $C.NICK $oncurchannel()
}

alias TabKey.inject (mode, part, what) {
	@:insmode = INSERT_MODE
	@:partlen = strlen($part) + (mode ? 2 : 0)

	if (strlen($L) == curpos() && mid(${curpos() - 1} 1 $L) == [ ]) {
		parsekey backspace
	} elsif (mid($curpos() 1 $L) == [ ]) {
		parsekey delete_character
		@:moveback = 1
	}
	^set insert_mode on
	if (curpos() == strlen($L) || mid($curpos() 1 $L) == [ ]) {
		# at end of word/line
		repeat $partlen parsekey backspace
		xtype -l $what${mode ? [\: ] : []}
		^set insert_mode $insmode
		if (moveback) { parsekey backward_character }
		return
	}

	@:pos = curpos() - 1
	@:skip = 0

	# not at end of word: need to advance to end
	while (pos >= 0 && mid($pos 1 $L) != [ ]) {
		@pos--
		@skip++
	}
	if (skip < partlen) {
		repeat ${partlen - skip} parsekey forward_character
	}
	repeat $partlen parsekey backspace
	xtype -l $what${mode ? [\: ] : []}
	^set insert_mode $insmode
	if (moveback) { parsekey backward_character }
}

alias TabKey.complete (color, list) {
	@:pos  = curpos() - (curpos() >= strlen($L))
	@:part = word($_indextoword($pos $L) $L)
	if ([] == part) {
		return
	}
	@:candidates = pattern($part* $list)
	if (0 == #candidates) {
		beep
		lecho ? No matches for $color$part$RST\.
		return
	}
	if (1 == #candidates) {
		if (candidates !~ [*/]) { @candidates #= [ ] }
		TabKey.inject 0 $part $candidates
		return
	}
	@candidates = sort($candidates)
	beep
	lecho ? $color$part$RST is ambiguous. Potential matches:
	lecho ?   $color$candidates
	if (LIGHT.Misc.menuCompletion) {
		TabKey.inject 0 $part $word(0 $candidates)
		@LIGHT.Data.menuComplete.is       = 1
		@LIGHT.Data.menuComplete.list     = candidates
		@LIGHT.Data.menuComplete.nick     = 0
		@LIGHT.Data.menuComplete.index    = 0
		@LIGHT.Data.menuComplete.lastPart = word(0 $candidates)
	} {
		@:prefix = prefix($candidates)
		@prefix = left($strlen($prefix) $part) ## rest($strlen($part) $prefix)
		TabKey.inject 0 $part $prefix
	}
}

alias TabKey.filecomplete (which, dirs) {
	^local globbed
	^local ww $word($which $L)

	if (dirs) {
		fe ($dirs) xx {
			@:yy = twiddle($xx)
			push globbed $msar(g#$yy/##$xx/##$glob($xx/$ww\*))
		}
	} {
		@globbed = glob(${ww}*)
	}
	TabKey.complete $C.FILE $globbed
}

alias TabKey.expand (direction) {
	if (LIGHT.Data.menuComplete.is) {
		@:pos  = curpos() - (curpos() >= strlen($L))
		@:part = word($_indextoword($pos $L) $L)
		if (LIGHT.Data.menuComplete.nick) { @part = before(: $part) }
		if (part == LIGHT.Data.menuComplete.lastPart) {
			@LIGHT.Data.menuComplete.index = (LIGHT.Data.menuComplete.index + direction) % numwords($LIGHT.Data.menuComplete.list)
			if (LIGHT.Data.menuComplete.index < 0) { @LIGHT.Data.menuComplete.index = numwords($LIGHT.Data.menuComplete.list) - 1 }
			@:newpart = word($LIGHT.Data.menuComplete.index $LIGHT.Data.menuComplete.list)
			TabKey.inject $LIGHT.Data.menuComplete.nick $part $newpart
			@LIGHT.Data.menuComplete.lastPart = newpart
			return
		} {
			Light.purge LIGHT.Data.menuComplete
		}
	}
	switch ($L) {
		() {
			TabKey.getNick $direction
		}
		(/m % *) (/msg % *) (/des% % *) {
			if (Light.Misc.preferCompletion) {
				TabKey.nickComplete
			} {
				TabKey.getNick $direction
			}
		}
		(/m %) (/msg %) (/des% %) {
			if (Light.Misc.preferCompletion) {
				TabKey.nickComplete
			} {
				parsekey end_of_line
				TabKey.complete $C.NICK $LIGHT.Data.TabKey.nickList
			}
		}
		(/m) (/msg) (/des%) {
			TabKey.getNick $direction
		}
		(/ban *) (/dc %) (/dcc chat %) (/deo% *) (/dev% *) (/k *) (/kb *) (/kbs *) (/op *) (/unban *) (/z% *) {
			TabKey.nickComplete
		}
		(/load %) {
			TabKey.filecomplete 1 $msar(g#:# #/ # #$LOAD_PATH)
		}
		(/dcc send *) (/ds *) {
			@:offset = 0
			if (word(0 $L) == [/dcc]) {
				@offset = -1
			}
			@:word = _indextoword($curpos() $L)
			if (-1 == word || [] == word) {
				@word = numwords($L) - 1
			}
			if (offset + word == 1) {
				TabKey.complete $C.NICK $oncurchannel()
				return
			}
			if (offset + word == 2) {
				TabKey.filecomplete $word
				return
			}
			beep
		}
		(/ex *) (/exec *) {
			@:word = _indextoword($curpos() $L)
			if (-1 == word || [] == word) {
				@word = numwords($L) - 1
			}
			TabKey.filecomplete $word
		}
		(%) {
			@:nickpart = L
			@:candidates = pattern($nickpart* $oncurchannel())
			if (0 == #candidates) {
				beep
				lecho ? No matches.
				return
			}
			if (1 == #candidates) {
				@:insmode = INSERT_MODE
				@:oldpos = curpos() + (strlen($candidates) - strlen($nickpart))
				@:xx = strlen($nickpart)

				^set insert_mode on
				parsekey beginning_of_line
				repeat $xx parsekey delete_character
				xtype -l $candidates: 
				@xx = oldpos - curpos()
				repeat $xx parsekey forward_character
				^set insert_mode $insmode
				return
			}
			@candidates = sort($candidates)
			beep
			lecho ? $C.NICK$nickpart$RST is ambiguous. Potential matches:
			lecho ?   $C.NICK$candidates
			if (LIGHT.Misc.menuCompletion) {
				TabKey.inject 1 $nickpart $word(0 $candidates)
				@LIGHT.Data.menuComplete.is       = 1
				@LIGHT.Data.menuComplete.list     = candidates
				@LIGHT.Data.menuComplete.nick     = 1
				@LIGHT.Data.menuComplete.index    = 0
				@LIGHT.Data.menuComplete.lastPart = word(0 $candidates)
			} {
				@:prefix = prefix($candidates)
				@prefix = left($strlen($prefix) $nickpart) ## rest($strlen($nickpart) $prefix)
				TabKey.inject 0 $nickpart $prefix
			}
		}
		(*) {
			if (Light.Misc.preferCompletion) {
				if ([] == L) {
					if (#LIGHT.Data.TabKey.nickList) {
						TabKey.getNick $direction
					} {
						xtype -l /m 
					}
				} {
					TabKey.nickComplete
				}
			} {
				TabKey.getNick $direction
			}
		}
	}
}

alias TabKey.getNick (index) if (#LIGHT.Data.TabKey.nickList) {
	^local inputline
	^local oldnicklen

	if (word(0 $L) == [/m] || word(0 $L) == [/msg]) {
		@inputline = restw(2 $L)
		@oldnicklen = strlen($word(1 $L))
	} {
		@inputline = L
		@oldnicklen = -4
	}

	if (LIGHT.Data.TabKey.areTabbing) {
		@LIGHT.Data.TabKey.index = (LIGHT.Data.TabKey.index + index) % #LIGHT.Data.TabKey.nickList
		if (LIGHT.Data.TabKey.index < 0) {
			@LIGHT.Data.TabKey.index = #LIGHT.Data.TabKey.nickList - 1
		}
	} {
		@LIGHT.Data.TabKey.index = 0
		@LIGHT.Data.Tabkey.areTabbing = 1
	}

	@:nick = word($LIGHT.Data.TabKey.index $LIGHT.Data.TabKey.nickList)
	@:oldpos = (curpos() - oldnicklen) + strlen($nick) - 1
	parsekey end_of_line
	repeat $strlen($L) parsekey backspace
	xtype -l /m $nick $inputline
	if (strlen($inputline)) {
		parsekey beginning_of_line
		repeat ${oldpos + 1} parsekey forward_character
	} { parsekey end_of_line }
} { lecho ! No nicks to cycle through.  }

alias msg (who, what) if (who) {
	if ([] == what) {
		lecho ! You must specify a message to send.
		return
	}
	@:index = findw($who $LIGHT.Data.TabKey.nickList)
	if (index > -1) {
		@who = word($index $LIGHT.Data.TabKey.nickList)
		TabKey.addNick $who
    } elsif ([%] != left(1 $who) && [=] != left(1 $who) && !ischannel($who)) {
		^local right

		if (index(@ $who) > -1) {
			@right = [@$after(@ $who)]
			@who = before(@ $who)
		}
		@:chan = oncurchannel()
		@:index = match($who $chan) - 1
		if (index > -1) {
			@who = word($index $chan)
		} {
           	userhost $who -cmd {
				bless
				@who = ([$4] == [<UNKNOWN>] ? [<UNKNOWN>] : [$0])
			}
			wait
			if ([<UNKNOWN>] == who) return
		}
		@who = who ## right
		TabKey.addNick $who
    }
	//msg $who $what
	@LIGHT.Data.TabKey.index = 0
	@LIGHT.Data.Tabkey.areTabbing = 0
} { lecho ! You must specify a nick. }

alias describe (who, what) if (who) {
	if (!what) {
		lecho ! You must specify an action to send.
		return
	}
	@:index = findw($who $LIGHT.Data.TabKey.nickList)
	if (index > -1) {
		@who = word($index $LIGHT.Data.TabKey.nickList)
		TabKey.addNick $who
    } elsif ([%] != left(1 $who) && [=] != left(1 $who) && !ischannel($who)) {
		^local right

		if (index(@ $who) > -1) {
			@right = [@$after(@ $who)]
			@who = before(@ $who)
		}
		@:chan = oncurchannel()
		@:index = match($who $chan) - 1
		if (index > -1) {
			@who = word($index $chan)
		} {
           	userhost $who -cmd {
				bless
				@who = ([$4] == [<UNKNOWN>] ? [<UNKNOWN>] : [$0])
			}
			wait
			if ([<UNKNOWN>] == who) return
		}
		@who = who ## right
		TabKey.addNick $who
    }
	//describe $who $what
	@LIGHT.Data.TabKey.index = 0
	@LIGHT.Data.Tabkey.areTabbing = 0
} { lecho ! You must specify a nick. }
