// ------------------------------------------------
// File : servent.h
// Date: 4-apr-2002
// Author: giles
// Desc: 
//
// (c) 2002 peercast.org
// ------------------------------------------------
// 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 option) 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.
// ------------------------------------------------


#ifndef _SERVENT_H
#define _SERVENT_H

// ----------------------------------
#include "socket.h"
#include "sys.h"
#include "gnutella.h"
#include "channel.h"
#include "http.h"
#include "rtsp.h"

class HTML;


// ----------------------------------
// Servent handles the actual connection between clients
class Servent
{
public:

	enum 
	{
		MAX_HASH = 4000,			// max. amount of packet hashes Servents can store
		MAX_OUTPACKETS = 32		// max. output packets per queue (normal/priority)
	};

    enum TYPE					
    {
		T_NONE,					// Not allocated
		T_ALLOCATED,			// Allocated and waiting to be used
        T_OUTGOING,				// An Outgoing connection
        T_INCOMING,				// Created by the Server to process an incoming connection
        T_SERVER,				// The main server 
		T_CHECK,				// Host cache checker, probably too noisy to be useful.
		T_STREAM,				// Servent handshakes then sends data
		T_LOOKUP,				// Lookup servent, find other nodes
    };

    enum STATUS
    {
        S_NONE,
        S_CONNECTING,
        S_PROTOCOL,
        S_HANDSHAKE,
        S_CONNECTED,
        S_CLOSING,
		S_LISTENING,
		S_TIMEOUT,
		S_REFUSED,
		S_VERIFIED,
		S_ERROR,
		S_WAIT
    };


	enum SORT
	{
		SORT_NAME = 0,
		SORT_BITRATE,
		SORT_LISTENERS,
		SORT_HOSTS,
		SORT_TYPE,
		SORT_GENRE
	};

	enum ALLOW
	{
		ALLOW_HTML = 0x01,
		ALLOW_DATA = 0x02,
		ALLOW_BROADCAST = 0x04,
		ALLOW_SERVENT = 0x08,
		ALLOW_ALL = 0xff
	};


	// init functions, a servent must be initiated using one of these
	void	init();
	void	reset(bool = true);
    bool	initServer(Host &);
    void	initIncoming(ClientSocket *,unsigned int);
    void	initOutgoing(Host &, TYPE);
	void	initGiv(Host &, int, GnuID &);
	void	checkFree();



	//	funcs for handling status/type
	void	setStatus(STATUS);
	char *	getTypeStr() {return typeMsgs[type];}
	char *	getStatusStr() {return statusMsgs[status];}
	int		getOutput();
	void	addBytes(unsigned int);
	bool	isOlderThan(Servent *s)
	{
		if (s)
		{
			unsigned int t = sys->getTime();
			return ((t-lastConnect) > (t-s->lastConnect));
		}else
			return true;
	}



	// static funcs that do the actual work in the servent thread
	static	THREAD_PROC		serverProc(ThreadInfo *);
	static	THREAD_PROC		outgoingProc(ThreadInfo *);
	static	THREAD_PROC		incomingProc(ThreadInfo *);
	static	THREAD_PROC		givProc(ThreadInfo *);



	bool	getLocalURL(char *);

	// various types of handshaking are needed
	void	handshakePLS(ChanHitList **, int, bool);
	void	handshakePLS(Channel **, int, bool);
	void	handshakeHTML(char *);
	void	handshakeXML();
	void	handshakeCMD(char *);
	void	handshakeIn();
	void	handshakeOut();
	void	handshakeStream(Channel *,bool);
	void	handshakeGiv(Channel *);
	void	handshakeICY(Channel::SRC_TYPE,bool);
	void	handshakeIncoming();
	void	handshakePOST();
	void	handshakeRTSP(RTSP &);
	void	handshakeHTTP(HTTP &,bool);

	bool	waitForChannelHeader(Channel *);
	Channel *getChannel(char *str);

	// html
	void	addWinampChansPage(HTML &,const char *, const char *,bool);
	void	addWinampSettingsPage(HTML &);
	void	addMyChannelsPage(HTML &);
	void	addAllChannelsPage(HTML &, SORT, bool, ChanInfo *);
	void	addAdminPage(HTML &);
	void	addSettingsPage(HTML &);
	void	addConnectionsPage(HTML &);
	void	addLogoutPage(HTML &);
	void	addShutdownPage(HTML &);
	void	addLoginPage(HTML &);
	void	addLogPage(HTML &);
	void	addBroadcastPage(HTML &);
	void	addNetStatsPage(HTML &);

	void	addChanInfo(HTML &, ChanInfo *, Channel *);
	void	addChanHits(HTML &, ChanHitList *, ChanHit *,ChanInfo &);


	void	addServerOptions(HTML &);
	void	addClientOptions(HTML &);
	void	addConnectOptions(HTML &);
	void	addRelayOptions(HTML &);
	void	addLogOptions(HTML &);
	void	addRootOptions(HTML &);
	void	addSecurityOptions(HTML &);
	void	addFilterOptions(HTML &);
	void	addAuthOptions(HTML &);
	void	addInformation(HTML &);
	void	addStatistics(HTML &);
	void	addBasicHeader(HTML &);
	void	addHeader(HTML &,int);
	void	addFooter(HTML &);


	// the "mainloop" of servents 
	void	process();
	void	processRelay();
	void	processServent();
	void	processStream(bool,bool);

	void	triggerChannel(char *,bool);
	void	sendChannel(Channel *);
	void	sendRawChannel(Channel *);
	void	sendRawMetaChannel(Channel *, int);
	static void	readICYHeader(HTTP &, ChanInfo &, char *);
	bool	checkPreview(unsigned int);
	bool	canPreview();


	bool	isAllowed(int);

	// connection handling funcs
	void	close();
	void	abort();
	void	createSocket();
	void	endThread();
	bool	isPrivate();
	bool	isLocal();


	Host	getHost();

	bool	outputPacket(GnuPacket &,bool);
	bool	hasSeenPacket(GnuPacket &p) {return seenIDs.contains(p.id);}


	TYPE type;
	STATUS status;

	static char *statusMsgs[],*typeMsgs[];
	GnuStream gnuStream;
	GnuPacket pack;
	unsigned int	lastConnect,lastPing,lastPacket;
	String	agent;

	GnuIDList	seenIDs;
	GnuID	networkID;

	bool	permAlloc;

	ThreadInfo	thread;

	//ChanInfo	chanInfo;
	char	loginPassword[64];
	char	loginMount[64];

	bool	priorityConnect;
	bool	addMetadata;
	unsigned int currPos;

	unsigned	int allow;

    ClientSocket *sock;

	// giv/push
	int		chanIndex;
	GnuID	chanID;
	GnuID	pushID;
	WLock	lock;


	GnuPacketBuffer	outPacketsNorm,outPacketsPri;

	int		outputBitrate;
	unsigned int bytesPerSecond;
	bool	flowControl;

};

extern char *nextCGIarg(char *cp, char *cmd, char *arg);


#endif

