//=============================================================================
//
//   File : KviKvsCoreSimpleCommands_gl.cpp
//   Creation date : Fri 31 Oct 2003 00:04:25 by Szymon Stefanek
//
//   This file is part of the KVIrc IRC client distribution
//   Copyright (C) 2003-2010 Szymon Stefanek <pragma at kvirc dot net>
//
//   This program is FREE software. You can redistribute it and/or
//   modify it under the terms of the GNU General Public License
//   as published by the Free Software Foundation; either version 2
//   of the License, or (at your opinion) any later version.
//
//   This program is distributed in the HOPE that it will be USEFUL,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//   See the GNU General Public License for more details.
//
//   You should have received a copy of the GNU General Public License
//   along with this program. If not, write to the Free Software Foundation,
//   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
//=============================================================================

#include "KviKvsCoreSimpleCommands.h"
#include "KviKvsTimerManager.h"
#include "KviKvsAsyncDnsOperation.h"
#include "KviKvsScript.h"
#include "KviFileUtils.h"
#include "KviIrcConnection.h"
#include "KviIrcConnectionServerInfo.h"
#include "KviLocale.h"
#include "kvi_out.h"

namespace KviKvsCoreSimpleCommands
{
	/*
		@doc: halfop
		@type:
			command
		@title:
			halfop
		@syntax:
			halfop <nickname_list>
		@short:
			Sets halfop status to the specified users
		@description:
			Sets channel half operator status to the users specified in <nickname_list>,
			which is a comma separated list of nicknames.
			This command works only if executed in a channel window.
			The command is translated to a set of MODE messages containing
			a variable number of +h flags.
			This command is [doc:connection_dependant_commands]connection dependant[/doc].
		@examples:
			[example]
			halfop Pragma,Crocodile
			[/example]
		@seealso:
			[cmd]dehalfop[/cmd], [cmd]op[/cmd], [cmd]deop[/cmd], [cmd]voice[/cmd], [cmd]devoice[/cmd]
	*/

	KVSCSC(halfop)
	{
		return multipleModeCommand(__pContext,__pParams,__pSwitches,'+','h');
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////

	/*
		@doc: halt
		@type:
			command
		@title:
			halt
		@syntax:
			halt
		@short:
			Stops the execution of the current script
		@description:
			Stops the execution of the current script.[br]
			Note that this is different from [cmd]return[/cmd]: halt doesn't
			set the return value (use [cmd]setreturn[/cmd] for that) and
			in the event handlers this command may have additional semantics:
			refer to the specific event documentation for more info.[br]
			In fact, this command should be used [b]only[/b] in the event handlers,
			use [cmd]return[/cmd] in any other context.[br]
	*/

	KVSCSC(halt)
	{
		Q_UNUSED(__pSwitches);
		Q_UNUSED(__pParams);

		KVSCSC_pContext->setHaltCalled();
		return false;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////

	/*
		@doc: help
		@type:
			command
		@title:
			help
		@syntax:
			help.open [-n] [-m] [document: string]
		@short:
			Shows a help document
		@description:
			Finds the first available help browser in the current frame
			then opens the specified [document].
			If no [document] is specified it the documentation index is shown.
			If no help browser is available, a new one is created.
			[document] can be an absolute path or a relative one: in this case
			the document is searched in the KVIrc documentation directory.[br]
			If no document has been found using absolute and relative paths,
			the first document matching [document] in the help search database
			is shown. Otherway, an error page is displayed.[br/]
			The help browser has limited html browsing capabilities: you can
			use it to view simple html files on your filesystem.[br]
			This command is an alias for the [cmd]help.open[cmd] command exported
			by the "help" module.
		@switches:
			!sw: -m | --mdi
			The created browser is a MDI window,
			otherwise it is a static window.
			!sw: -n | --new
			The window creation is forced even
			if there are other help browsers already open.[br]
		@seealso:
			[cmd]help.open[/cmd]
		@examples:
			[example]
			help /home/pragma/myfile.html	//absolute path
			help cmd_snd.play.html		//relative path
			help "Binding operator"		//keyword search, remember quoting
			help OnNickServAuth		//keyword search for an event
			help \$my.user			//keyword search, $ needs to be escaped
			[/example]
	*/

	KVSCSC(help)
	{
		QString szParams;
		KVSCSC_PARAMETERS_BEGIN
			KVSCSC_PARAMETER("params",KVS_PT_STRING,KVS_PF_OPTIONAL | KVS_PF_APPENDREMAINING,szParams)
		KVSCSC_PARAMETERS_END

		// We just alias the help.open function
		QString szSwitches="";
		if(KVSCSC_pSwitches->find('m',"mdi"))szSwitches.append("-m ");
		if(KVSCSC_pSwitches->find('n',"new"))szSwitches.append("-n ");

		if(szParams.endsWith("()"))
			szParams.chop(2);

		KviQString::escapeKvs(&szParams);
		KviKvsScript s("help","help.open " + szSwitches + szParams);
		s.run(KVSCSC_pContext->window());

		return true;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////

	/*
		@doc: host
		@type:
			command
		@title:
			host
		@syntax:
			host [-i] [-a] <dnsquery:string>
		@short:
			DNS lookup
		@switches:
			!sw: --ipv6 | -i
			Causes the command to run in IPv6 mode
			!sw: --any | -a
			Causes the command to run in unspecified mode and lookup both IPv4 and IPv6 addresses
		@description:
			Starts a DNS lookup for the <dnsquery> (hostname) and prints
			the results in the current window.[br]
			The -i switch causes the command to execute
			in IPv6 mode (and lookup ONLY IPv6 hosts!).[br]
			The -a switch causes the command to run in "unspecified" mode
			and return any available address: IPv4 or Ipv6.[br]
			This command also performs reverse lookups (if you pass an IP address as <hostname>).[br]
		@examples:
			[example]
			host localhost
			host irc.flashnet.it
			host -i irc.flashnet.it
			host -i A0E0:FFFF:::1010:1111:E0E0:A0A0
			host -a 127.0.0.1
			host 255.41.31.23
			host -a irc.stealth.net
			[/example]
		@seealso:
			[cmd]ahost[/cmd]
	*/

	KVSCSC(host)
	{
		QString szQuery;
		KVSCSC_PARAMETERS_BEGIN
			KVSCSC_PARAMETER("dnsquery",KVS_PT_NONEMPTYSTRING,0,szQuery)
		KVSCSC_PARAMETERS_END

		KviDnsResolver::QueryType queryType = KviDnsResolver::IPv4;
		if(KVSCSC_pSwitches->find('i',"ipv6"))queryType = KviDnsResolver::IPv6;
		if(KVSCSC_pSwitches->find('a',"any"))queryType = KviDnsResolver::Any;

		new KviKvsAsyncDnsOperation(
			KVSCSC_pContext->window(),
			szQuery,
			queryType);

		return true;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////

	/*
		@doc: include
		@type:
			command
		@title:
			include
		@syntax:
			include [-q] [-e] [-f] [-r] <filename:string> [<parameter1:variant> [<parameter2:variant> [...]]]
		@short:
			A builtin alias for parse
		@description:
			This is a builtin alias for [cmd]parse[/cmd] that gives scripts a "more C-ish" look :)
		@seealso:
			[cmd]parse[/cmd]
	*/

	///////////////////////////////////////////////////////////////////////////////////////////////////////

	/*
		@doc: info
		@type:
			command
		@title:
			info
		@syntax:
			info [target server]
		@short:
			Requests the server daemon info from a server
		@description:
			Requests server daemon information from the specified server or the current server if no [target server] is specified.[br]
			This command is a [doc:rfc2821wrappers]RFC2821 command wrapper[/doc]; see that document for more information.[br]
	*/
	// RFC2821 wrapper

	///////////////////////////////////////////////////////////////////////////////////////////////////////

	/*
		@doc: inject
		@type:
			command
		@title:
			inject
		@syntax:
			inject <text>
		@short:
			Injects <text> to the socket
		@switches:
			!sw: -q | --quiet
			Does not print injection warnings on the console
		@description:
			Injects <text> to the socket as if it comes from the server.[br]
			It's useful to test scripts without spamming or flooding the server.[br]
			Each injection causes a warning to be displayed in the console window and in
			the socketspy window (if open). The -q switch suppresses only the console warnings.
		@examples:
			[example]
			[comment]Injects the string as we were sending a query message[/comment][br]
			inject :foo!bar@foobar PRIVMSG HelLViS69 :message
			[/example]
	*/
	KVSCSC(inject)
	{
		Q_UNUSED(__pSwitches);

		QString szText;
		KVSCSC_PARAMETERS_BEGIN
			KVSCSC_PARAMETER("text",KVS_PT_NONEMPTYSTRING,KVS_PF_APPENDREMAINING,szText)
		KVSCSC_PARAMETERS_END

		KVSCSC_REQUIRE_CONNECTION

		if(!((KVSCSC_pWindow->type() == KviWindow::Channel) ||
			(KVSCSC_pWindow->type() == KviWindow::Query) ||
			(KVSCSC_pWindow->type() == KviWindow::Console)))
		{
			KVSCSC_pContext->warning(__tr2qs_ctx("The current window is not a channel, a query or a console","kvs"));
			return false;
		}

		// Get the active window where we ran the command
		KviWindow * pActive = KVSCSC_pWindow->context()->console()->activeWindow();

		// Get the list of open windows
		KviPointerList<KviWindow> * pList = KVSCSC_pWindow->frame()->windowList();

		// Scan the windows
		for(KviWindow * pWnd = pList->first(); pWnd; pWnd = pList->next())
		{
			// Search for the right socketspy
			if(
					(pWnd->type() == KviWindow::SocketSpy) &&
					(pWnd->context() == pActive->context())
				)
			{
				// Ok, found... send the warning
				pWnd->outputNoFmt(KVI_OUT_SOCKETMESSAGE,__tr2qs_ctx("The following string was injected by the user:","kvs"));
				break;
			}
		}

		// Send the warning to the right console
		if(!KVSCSC_pSwitches->find('q',"quiet"))
			pActive->console()->output(KviWindow::SocketSpy,__tr2qs_ctx("Injected incoming data: %1","kvs").arg(szText));

		// Encode the text for the socket
		QByteArray szT = KVSCSC_pConnection->encodeText(szText);

		// Send the converted text to the socket
		KVSCSC_pConnection->incomingMessage(szT.constData());

		return true;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////

	/*
		@doc: invite
		@type:
			command
		@title:
			invite
		@syntax:
			invite <nickname> <channel>
		@short:
			Invites an user to a channel
		@description:
			Invites the specified user to the <channel>.[br]
			This command is a [doc:rfc2821wrappers]RFC2821 command wrapper[/doc]; see that document for more information.[br]
	*/
	// RCF2821 wrapper

	///////////////////////////////////////////////////////////////////////////////////////////////////////

	/*
		@doc: join
		@type:
			command
		@title:
			join
		@syntax:
			join <channel list> [key list]
		@short:
			Joins the specified channels
		@description:
			Joins the channels specified in <channel list> by using the
			optional keys in the [key list].[br]
			This command is "server based"; you will see its results only
			after the server acknowledges it.[br]
			This command is [doc:connection_dependant_commands]connection dependant[/doc].[br]
		@examples:
			[example]
			join #kvirc
			join #linux-it,#xmms,#kde linus
			[/example]
	*/

	KVSCSC(join)
	{
		Q_UNUSED(__pSwitches);

		QString szChans,szKeys;
		KVSCSC_PARAMETERS_BEGIN
			KVSCSC_PARAMETER("chans",KVS_PT_NONEMPTYSTRING,0,szChans)
			KVSCSC_PARAMETER("keys",KVS_PT_STRING,KVS_PF_OPTIONAL,szKeys)
		KVSCSC_PARAMETERS_END

		KVSCSC_REQUIRE_CONNECTION

		QStringList slChans = szChans.split(",",QString::SkipEmptyParts);

		QString szChanTypes = KVSCSC_pConnection->serverInfo()->supportedChannelTypes();
		for(QStringList::Iterator it=slChans.begin();it != slChans.end();++it)
		{
			if(!(*it).isEmpty())
			{
				bool bFound = false;
				int id = 0;
				QChar f = (*it)[0];
				while((id < szChanTypes.length()) && !bFound)
				{
					if(f == szChanTypes[id])bFound = true;
					id++;
				}
				if(!bFound)
					(*it).prepend("#");
			}
		}

		szChans = slChans.join(",");
		QByteArray szEncodedChans = KVSCSC_pConnection->encodeText(szChans);

		if(szKeys.isEmpty())
		{
			if(!(KVSCSC_pConnection->sendFmtData("JOIN %s",szEncodedChans.data())))
				return KVSCSC_pContext->warningNoIrcConnection();
		} else {
			QByteArray szEncodedKeys  = KVSCSC_pConnection->encodeText(szKeys);
			if(!(KVSCSC_pConnection->sendFmtData("JOIN %s %s",szEncodedChans.data(),szEncodedKeys.data())))
				return KVSCSC_pContext->warningNoIrcConnection();
		}

		return true;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////

	/*
		@doc: kick
		@type:
			command
		@title:
			kick
		@syntax:
			kick <user:string> [reason:string]
		@short:
			Kicks an user from a channel
		@description:
			Kicks the specified <user> from the current channel.[br]
			This command is a simple wrapper over the IRC KICK command.
			This command is [doc:connection_dependant_commands]connection dependant[/doc].
		@examples:
			[example]
			kick Pragma Go coding in Pascal!
			[/example]
	*/

	KVSCSC(kick)
	{
		Q_UNUSED(__pSwitches);

		QString szUser;
		QString szReason;
		KVSCSC_PARAMETERS_BEGIN
			KVSCSC_PARAMETER("user",KVS_PT_NONEMPTYSTRING,0,szUser)
			KVSCSC_PARAMETER("reason",KVS_PT_STRING,KVS_PF_OPTIONAL | KVS_PF_APPENDREMAINING,szReason)
		KVSCSC_PARAMETERS_END

		KVSCSC_REQUIRE_CONNECTION

		if(KVSCSC_pWindow->type() != KviWindow::Channel)
		{
			KVSCSC_pContext->warning(__tr2qs_ctx("The current window is not a channel","kvs"));
			return false;
		}

		QByteArray szC = KVSCSC_pConnection->encodeText(KVSCSC_pWindow->target());
		QByteArray szU = KVSCSC_pConnection->encodeText(szUser);
		QByteArray szR = KVSCSC_pConnection->encodeText(szReason);

		if(szR.isEmpty())
		{
			if(!KVSCSC_pConnection->sendFmtData("KICK %s %s",szC.data(),szU.data()))
				return KVSCSC_pContext->warningNoIrcConnection();
		} else {
			if(!KVSCSC_pConnection->sendFmtData("KICK %s %s :%s",szC.data(),szU.data(),szR.data()))
				return KVSCSC_pContext->warningNoIrcConnection();
		}

		return true;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////

	/*
		@doc: killtimer
		@title:
			killtimer
		@type:
			command
		@short:
			Stops a timer
		@syntax:
			killtimer [-a]
			killtimer [-q] [name]
		@switches:
			!sw: -a | --active
			Requests to kill all the active timers
			!sw: -q | --quiet
			Does not print warnings if the specified timer is not running
		@description:
			The first version (with -a) kill all the running timers.[br]
			The second verdion kills a running timer named [name].[br]
			The timer has been started by the [cmd]timer[/cmd].[br]
			[name] can be omitted if this command is called from a [cmd]timer[/cmd] callback:
			in this case the current timer will be scheduled for killing immediately
			after it has returned control to KVIrc.
		@seealso:
			[cmd]timer[/cmd], [fnc]$isTimer[/fnc], [cmd]listtimers[/cmd], [fnc]$listtimers[/fnc]
	*/

	KVSCSC(killtimer)
	{
		QString szName;
		KVSCSC_PARAMETERS_BEGIN
			KVSCSC_PARAMETER("name",KVS_PT_STRING,KVS_PF_OPTIONAL,szName)
		KVSCSC_PARAMETERS_END

		if(KVSCSC_pSwitches->find('a',"active"))
		{
			KviKvsTimerManager::instance()->deleteAllTimers();
			return true;
		}

		if(szName.isEmpty())
		{
			if(!KviKvsTimerManager::instance()->deleteCurrentTimer())
			{
				if(!KVSCSC_pSwitches->find('q',"quiet"))
					KVSCSC_pContext->warning(__tr2qs_ctx("Timer name omitted but there is not current timer (this is not a timer callback)","kvs"));
			}
		} else {
			if(!KviKvsTimerManager::instance()->deleteTimer(szName))
			{
				if(!KVSCSC_pSwitches->find('q',"quiet"))
					KVSCSC_pContext->warning(__tr2qs_ctx("Can't kill the timer '%Q' since it is not running","kvs"),&szName);
			}
		}
		return true;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////

	/*
		@doc: leave
		@type:
			command
		@title:
			leave
		@syntax:
			leave [-k] [-q] [-s] <channel list> [part message]
		@short:
			Alias for part
		@description:
			Builtin alias for the command [cmd]part[/cmd].
	*/
	// Internally aliased to leave

	///////////////////////////////////////////////////////////////////////////////////////////////////////

	/*
		@doc: links
		@type:
			command
		@title:
			links
		@syntax:
			links
		@short:
			Lists server links
		@description:
			List server links.[br]
			The syntax of the parameters of this command is different on many servers.[br]
			This command is a [doc:rfc2821wrappers]RFC2821 command wrapper[/doc]; see that document for more information.[br]
	*/
	// RFC2821 wrapper

	///////////////////////////////////////////////////////////////////////////////////////////////////////

	/*
		@doc: list
		@type:
			command
		@title:
			list
		@syntax:
			list [ <channel> *( "," <channel> ) [ <target> ] ]
		@short:
			Lists channels
		@description:
			List channels.[br]
			The syntax of the parameters of this command is different on many servers.[br]
			This command is a [doc:rfc2821wrappers]RFC2821 command wrapper[/doc]; see that document for more information.[br]
	*/
	// RFC2821 wrapper

	///////////////////////////////////////////////////////////////////////////////////////////////////////

	/*
		@doc: listtimers
		@title:
			listtimers
		@type:
			command
		@short:
			Lists the active timers
		@syntax:
			listtimers
		@description:
			Lists the currently active timers
		@seealso:
			[cmd]timer[/cmd], [fnc]$isTimer[/fnc], [cmd]killtimer[/cmd]
	*/

	KVSCSC(listtimers)
	{
		Q_UNUSED(__pSwitches);
		Q_UNUSED(__pParams);

		KviPointerHashTable<QString,KviKvsTimer> * pTimerDict = KviKvsTimerManager::instance()->timerDict();

		if(!pTimerDict)
			return true;

		KviPointerHashTableIterator<QString,KviKvsTimer> it(*pTimerDict);

		KVSCSC_pContext->window()->outputNoFmt(KVI_OUT_VERBOSE,__tr2qs_ctx("List of active timers","kvs"));

		unsigned int uCnt = 0;

		while(KviKvsTimer * pTimer = it.current())
		{
			QString szName = pTimer->name();
			QString szLifetime;
			switch(pTimer->lifetime())
			{
				case KviKvsTimer::Persistent:
					szLifetime = __tr2qs_ctx("Persistent","kvs");
				break;
				case KviKvsTimer::WindowLifetime:
					szLifetime = __tr2qs_ctx("WindowLifetime","kvs");
				break;
				case KviKvsTimer::SingleShot:
					szLifetime = __tr2qs_ctx("SingleShot","kvs");
				break;
				default:
					szLifetime = __tr2qs_ctx("Unknown","kvs");
				break;
			}
			QString szDelay;
			szDelay.setNum(pTimer->delay());
			QString szWindow;
			szWindow = pTimer->window() ? pTimer->window()->id() : __tr2qs_ctx("None","kvs");

			KVSCSC_pContext->window()->output(KVI_OUT_VERBOSE,
					"Timer \"%Q\": Lifetime: %Q, Delay: %Q, Window: %Q",
					&szName,&szLifetime,&szDelay,&szWindow
				);

			uCnt++;
			++it;
		}

		KVSCSC_pContext->window()->output(KVI_OUT_VERBOSE,__tr2qs_ctx("Total: %u timers running","kvs"),uCnt);

		return true;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////

	/*
		@doc: lusers
		@type:
			command
		@title:
			lusers
		@syntax:
			lusers [ <mask> [ <target> ] ]
		@short:
			Requests statistics about users on IRC
		@description:
			The LUSERS command is used to get statistics about the size of the
			IRC network.[br]If no parameter is given, the reply will be about the
			whole net.[br]If a <mask> is specified, then the reply will only
			concern the part of the network formed by the servers matching the
			mask.[br]Finally, if the <target> parameter is specified, the request
			is forwarded to that server which will generate the reply.[br]
			This command is a [doc:rfc2821wrappers]RFC2821 command wrapper[/doc]; see that document for more information.[br]
	*/
	// RFC2821 wrapper
};
