#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <libraries/locale.h>
#include <workbench/startup.h>
#include <dos/dos.h>
#include <stdio.h>
#include <string.h>

#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/gadtools.h>
#include <proto/dos.h>
#include <proto/utility.h>
#include <proto/commodities.h>
#include <proto/locale.h>
#include <clib/alib_protos.h>

#include "/include/server.h"

/* Per la localizzazione */
#define CATCOMP_NUMBERS
#define CATCOMP_BLOCK
#define CATCOMP_CODE
#include "BServer_strings.h"

extern STRPTR __asm GetString(register __a0 struct LocaleInfo *li,
                       register __d0 LONG stringNum);

/* Pulizia di una porta messaggi */
extern void ClearPendingMessages(struct MsgPort *);

/* Controllo commodity */
extern BOOL SetUpCommodity(int,char**);
extern void RemoveCommodity(void);
extern void HandleCxMessages(void);

/* Gestione finestra */
extern void DetachGadgets( void );
extern void AttachGadgets( void );
extern BOOL PopUpWindow(void);
extern void ShutWindow(void);
extern void HandleWindowMessages(void);
extern UWORD wleft, wtop;

/* Builtin blanker */
extern BOOL PopUpBlackScreen(void);
extern void CloseBlackScreen(void);

/* Fa partire i clienti nella ClientList */
extern BOOL StartClient( struct ClientNode * );
extern void GetClientNames( char * );
extern void DropClientNames( void );

/* Alloca una dinfo per un cliente */
extern struct DisplayIDInformation *AllocDisplayIDInformation( ULONG );
extern struct Window *window;

/* Per la trasmissione del displayID */
extern ULONG DisplayID;
extern char DefaultModeName[];
extern struct List *CreateModeList( void );
extern void GetDisplayNodeFromName( void );
extern void DeleteModeList( void );

extern struct IntuitionBase *IntuitionBase;
extern struct Library *IconBase, *GadToolsBase, *UtilityBase, *CxBase;
extern struct Gadget *list_gdg;

struct Library *LocaleBase;
struct LocaleInfo li;
char *msg_timeout, *key_timeout, *msg_changetime, *key_changetime, 
     *msg_random, *key_random, *msg_hide, *key_hide, *msg_quit, *key_quit,
     *msg_blank, *key_blank, *msg_killsel, *key_killsel,
     *msg_addcli, *key_addcli, *msg_brilevel, *key_brilevel,
     *msg_sndlevel, *key_sndlevel;

extern struct NewBroker newbroker;
extern struct TagItem sourcetags[];

char *version = "$VER: "PROGRAMNAME" "PROGRAMVERSION" "__AMIGADATE__;

/* Per la notifica dei vari eventi */
struct MsgPort *handlerPort, *timerMPort, *clientMPort;
struct Task *thisTask;
UBYTE handlerSigBit, timerSigBit;
ULONG handlerSignal, timerSignal;
extern ULONG CXSignal, windowSignal, timerSignal;


BOOL StayCool = TRUE;			/* Terminazione */
BOOL RandomClient = TRUE;		/* Deve essere scelto a caso? */
BOOL CommodityActive = TRUE;	/* Deve effettuare il blank? */
BOOL PopUp = FALSE;				/* Deve aprire la finestra? */
UWORD timeElapsed;				/* Tempo passato dall'ultimo evento */
UWORD delaySecs, delayEvents;	/* Secondi da passare trasform. in Ticks */
UWORD changeSecs, changeEvents; /* Secondi da passare per cambiare client */
UBYTE briLevel = 100;			/* Livello di luminosità */
UBYTE sndLevel = 100;           /* Livello sonoro massimo */
UWORD DefaultClient = 0;
UWORD TotalClients = 0;	
BOOL stop_on_mouse = TRUE, stop_on_disk = TRUE;

UBYTE BlankStatus = IDLE;

/* Usate nella scelta del cliente per non toccare le originali  */
/* In caso di fallimento infatti vanno modificate per scandire  */
/* la lista dalla fine all'inizio alla ricerca di un nuovo cli. */

UWORD copy_of_DefaultClient = (UWORD)~0;
BOOL copy_of_RandomClient = (UWORD)~0;

#define BLANKING_NONE      0
#define BLANKING_BUILTIN   1
#define BLANKING_EXTERN    2

/* Prototipi delle funzioni */
struct ClientMessage *AllocClientMessage( ULONG );
void FreeClientMessage( struct ClientMessage * );
struct ClientNode *FindClientNode( UWORD );
void RemoveClient( UWORD, char * );

void SendCommandToClient( ULONG );
void QuitClients( void );
void HandleClients(void);
struct MsgPort *SetUpServerPort(void);
void RemoveServerPort(void);

void StartBlanking(void);
void StopBlanking(void);

void ProcessUserMessages(void);

 /*************************
 *                        *
 * HANDLE EXTERN PROGRAMS *
 *                        *
 *************************/

struct MsgPort *ServerPort;
ULONG serverSignal;
UBYTE blankingAction;

struct List ClientsList;


struct ServerMessage *AllocServerMessage( ULONG command )
{
struct ServerMessage *bmsg;

if ( bmsg = AllocVec( sizeof(struct ServerMessage), MEMF_PUBLIC | MEMF_CLEAR ) )
	{
	bmsg->sm_Msg.mn_Node.ln_Type = NT_MESSAGE;
	bmsg->sm_Msg.mn_ReplyPort = ServerPort;
	bmsg->sm_Msg.mn_Length = sizeof(struct ServerMessage);
	bmsg->sm_Command = command;
	}
return( bmsg );
}


void FreeServerMessage( struct ServerMessage *bmsg )
{
FreeVec( bmsg );
}


struct ClientNode *FindClientNode( UWORD clientNumber )
{
struct ClientNode *node = (struct ClientNode *)ClientsList.lh_Head;
UWORD counter = 0;

if ( clientNumber == ~0 )
	{
	DisplayBeep( NULL );
	clientNumber = 0;
	}

while ( counter < clientNumber )
	{
	counter++;
	node = (struct ClientNode *)node->cn_Node.ln_Succ;
	}

return( node );
}


void RemoveClient( UWORD clientNumber, char *clientName )
{
struct ClientNode *node;

node = FindClientNode( clientNumber );

if ( node != (struct ClientNode *)ClientsList.lh_Head )
	{
	DetachGadgets();
	Remove( (struct Node *)node );
	FreeMem( node, sizeof(struct ClientNode) );
	TotalClients--;

	if ( clientNumber < TotalClients )
		DefaultClient = clientNumber;
	else
		DefaultClient = TotalClients;

	AttachGadgets();
	}
}


void SendCommandToClient( ULONG command )
{
struct ServerMessage *bmsg;

if ( clientMPort && (bmsg = AllocServerMessage( command )))
	{
	PutMsg( clientMPort, (struct Message *)bmsg );
	do
		WaitPort( ServerPort );
	while ( !(GetMsg( ServerPort )) );

	FreeServerMessage( bmsg );
	}
}


void HandleClients( void )
{
struct ClientMessage *bmsg;

while( bmsg = (struct ClientMessage *)GetMsg( ServerPort ) )
	{
	switch ( bmsg->cm_Action )
		{
		case ACTION_ARRIVED:
			if ( BlankStatus == MUST_BLANK || BlankStatus == IDLE )
				{
				timeElapsed = 0;
				if ( copy_of_DefaultClient == ~0 )
					{
					copy_of_DefaultClient = DefaultClient;
					copy_of_RandomClient = RandomClient;
					}
				clientMPort = bmsg->cm_Msg.mn_ReplyPort;
				bmsg->DInfo = AllocDisplayIDInformation( DisplayID );
				blankingAction = BLANKING_EXTERN;
				BlankStatus = BLANKING;
				}
			break;
		case ACTION_FAILED:
			if ( BlankStatus == BLANKING )
				{
				BlankStatus = MUST_BLANK;
				if ( RandomClient != 0 )
					{
					RandomClient = 0;
					DefaultClient = TotalClients;
					}
				else
					DefaultClient--;
				StartBlanking();
				}
		default:
			break;
		}
	ReplyMsg( (struct Message *)bmsg );
	}
}


struct MsgPort *SetUpServerPort( void )
{
if ( ServerPort = CreatePort( SERVERPORTNAME, 0 ) )
	serverSignal = 1L << ServerPort->mp_SigBit;
return( ServerPort );
}


void RemoveServerPort( void )
{
DeletePort( ServerPort );
}


 /*****************
 *                *
 * Blanking stuff *
 *                *
 *****************/

WORD ActiveClient;

void StartBlanking( void )
{
ULONG secs, micr;

clientMPort = NULL;

if ( BlankStatus == MUST_BLANK )
	{
	if ( copy_of_DefaultClient == ~0 )
		{
		copy_of_DefaultClient = DefaultClient;
		copy_of_RandomClient = RandomClient;
		}

	if ( window )
		ClearPendingMessages( window->UserPort );

	if ( TotalClients )
		{
		if ( RandomClient )
			{
			CurrentTime( &secs, &micr );
			ActiveClient = micr % TotalClients + 1;
			}
		else
			ActiveClient = DefaultClient;
		}

	if ( !ActiveClient )
		{
		if ( PopUpBlackScreen() )
			{
			blankingAction = BLANKING_BUILTIN;
			BlankStatus = BLANKING;
			}
		else
			{
			DisplayBeep( NULL );
			blankingAction = BLANKING_NONE;
			BlankStatus = BLANKING;
			}
		}
	else
		{
		if ( !StartClient( FindClientNode( ActiveClient ) ) )
			{
			if ( RandomClient )
				{
				DefaultClient = TotalClients;
				RandomClient = 0;
				}
			else
				DefaultClient--;
			StartBlanking();
			}
		else
			/* BlankStatus = BLANKING <=> ACTION_ARRIVED */
			blankingAction = BLANKING_EXTERN;
		}
	}
}


void StopBlanking( void )
{
if ( blankingAction == BLANKING_BUILTIN )
	CloseBlackScreen();
else
if ( blankingAction == BLANKING_EXTERN )
	SendCommandToClient( COMMAND_QUIT );

if ( copy_of_DefaultClient != ~0 )
	{
	DefaultClient = copy_of_DefaultClient;
	RandomClient = copy_of_RandomClient;

	copy_of_DefaultClient = (UWORD)~0;
	copy_of_RandomClient = (UWORD)~0;
	}

blankingAction = BLANKING_NONE;
BlankStatus = IDLE;
}


 /*******************
 *                  *
 * PROCESS MESSAGES *
 *                  *
 *******************/


void Quit( void )
{
StayCool = FALSE;
}


void ProcessUserMessages(void)
{
ULONG signal;

while( StayCool )
	{
	signal = Wait( CXSignal | windowSignal | serverSignal | handlerSignal | timerSignal | SIGBREAKF_CTRL_C );

	if ( signal & CXSignal )
		HandleCxMessages();

	if ( signal & windowSignal )
		HandleWindowMessages();

	if ( signal & serverSignal )
		HandleClients();

	if ( signal & timerSignal && CommodityActive )
		StartBlanking();

	if ( signal & handlerSignal )
		StopBlanking();

	if ( signal & SIGBREAKF_CTRL_C )
		Quit();
	}

if ( BlankStatus == BLANKING )
	{
	BlankStatus = MUST_RETRY;
	StopBlanking();
	}
}


 /***************
 *              *
 * MAIN PROGRAM *
 *              *
 ***************/

#define POP_KEY_ID      100

extern CxObj *broker , *sender, *translate;
CxObj *hotkeyfilter, *blankkeyfilter, *mousefilter, *timerfilter, *keyfilter;

extern struct NewBroker newbroker;
extern struct MsgPort *broker_mp;


void __interrupt __saveds BlankerAction(CxMsg *CxMsg,CxObj *CO)
{
struct InputEvent *IE=(struct InputEvent *)CxMsgData(CxMsg);

if ( IE->ie_Class == IECLASS_TIMER )
	{
	/* timeElapsed viene posto a 0 all'arrivo del cliente */

	if ( delaySecs && (BlankStatus == IDLE) && (++timeElapsed >= delayEvents) )
		{
		BlankStatus = MUST_BLANK;
		Signal( thisTask, timerSignal );
		}

	if ( BlankStatus == BLANKING && changeSecs && RandomClient && ++timeElapsed >= changeEvents )
		{
		BlankStatus = MUST_RETRY;
		Signal( thisTask, handlerSignal );
		timeElapsed = delayEvents - 1;
		}
	}
else
	{
	timeElapsed = 0;
	if ( BlankStatus == BLANKING && (
		(IE->ie_Class == IECLASS_RAWMOUSE && (IE->ie_Code != IECODE_NOBUTTON || stop_on_mouse)) ||
		((IE->ie_Class == IECLASS_DISKREMOVED || IE->ie_Class == IECLASS_DISKINSERTED) && stop_on_disk ) ||
		(IE->ie_Class == IECLASS_RAWKEY && IE->ie_Code != *key_blank) ))
		{
		BlankStatus = MUST_RETRY;
		Signal( thisTask, handlerSignal );
		}
	}
}


extern struct NewMenu bservernewmenus[];
extern struct EasyStruct easyabout;

void main( int argc, char *argv[] )
{
struct WBStartup *WBenchMsg;
struct ClientNode *firstNode;
CxObj *customobj;
char hotkey[50], blankkey[50];
char ListName[100];
struct List *list;

li.li_Catalog = NULL;
if ( LocaleBase = OpenLibrary( "locale.library", 38L ) )
	{
	li.li_LocaleBase = LocaleBase;
	li.li_Catalog = OpenCatalogA( NULL, "BServer.catalog", NULL );

	msg_random = GetString( &li, MSG_RANDOM );
	msg_hide = GetString( &li, MSG_HIDE );
	msg_quit = GetString( &li, MSG_QUIT );
	msg_blank = GetString( &li, MSG_BLANK );
	msg_killsel = GetString( &li, MSG_REMOVECLIENT );
	msg_addcli = GetString( &li, MSG_ADDCLIENT );
	}
else
	{
	msg_random = "Random";
	msg_hide = "Hide";
	msg_quit = "Quit";
	msg_blank = "Blank";
	msg_killsel = "Remove client";
	msg_addcli = "Add client";
	}

sourcetags[0].ti_Data = (ULONG)GetString( &li, MSG_SELECT_CLIENTS );
newbroker.nb_Descr = GetString( &li, MSG_BROKER );
msg_timeout = GetString( &li, MSG_TIMEOUT );
key_timeout = GetString( &li, MSG_TIMEOUT_KEY );
msg_changetime = GetString( &li, MSG_CHANGETIME );
key_changetime = GetString( &li, MSG_CHANGETIME_KEY );
key_random = GetString( &li, MSG_RANDOM_KEY );
key_hide = GetString( &li, MSG_HIDE_KEY );
key_quit = GetString( &li, MSG_QUIT_KEY );
key_blank = GetString( &li, MSG_BLANK_KEY );
key_killsel = GetString( &li, MSG_REMOVECLIENT_KEY );
key_addcli = GetString( &li, MSG_ADDCLIENT_KEY );
msg_brilevel = GetString( &li, MSG_BRILEVEL );
key_brilevel = GetString( &li, MSG_BRILEVEL_KEY );
msg_sndlevel = GetString( &li, MSG_SNDLEVEL );
key_sndlevel = GetString( &li, MSG_SNDLEVEL_KEY );
bservernewmenus[0].nm_Label = GetString( &li, MSG_MENUPROJECT );
bservernewmenus[1].nm_Label = GetString( &li, MSG_MENUABOUT );
bservernewmenus[1].nm_CommKey = GetString( &li, MSG_MENUABOUT_HK );
bservernewmenus[2].nm_Label = GetString( &li, MSG_MENUHIDE );
bservernewmenus[2].nm_CommKey = GetString( &li, MSG_MENUHIDE_HK );
bservernewmenus[3].nm_Label = GetString( &li, MSG_MENUQUIT );
bservernewmenus[3].nm_CommKey = GetString( &li, MSG_MENUQUIT_HK );
bservernewmenus[4].nm_Label = GetString( &li, MSG_MENUINPUTEVENTS );
bservernewmenus[5].nm_Label = GetString( &li, MSG_MENUIECLASSRAWMOUSE );
bservernewmenus[5].nm_CommKey = GetString( &li, MSG_MENUIECLASSRAWMOUSE_HK );
bservernewmenus[6].nm_Label = GetString( &li, MSG_MENUIECLASSDISK );
bservernewmenus[6].nm_CommKey = GetString( &li, MSG_MENUIECLASSDISK_HK );
easyabout.es_TextFormat = GetString( &li, MSG_MENUABOUTREPLY  );
easyabout.es_GadgetFormat = GetString( &li, MSG_MENUABOUTGADS );

if ( IntuitionBase = (struct IntuitionBase *)OpenLibrary( "intuition.library", 37L ) )
	{
	if ( UtilityBase = OpenLibrary( "utility.library", 37L ) )
		{
		if ( argc )
			{
			struct RDArgs *rdargs;
			LONG argres[15] = { 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L };

			if ( rdargs = ReadArgs( "CX_PRIORITY/K/N,CX_POPUP/S,CX_HOTKEY/K,BLANKKEY/K,WLEFT/N,WTOP/N,R=RANDOM/S,T=TIMEOUT/K/N,CT=CHANGETIME/K/N,D=DISPLAY/K,L=LIST/K,B=BRIGHTNESS/K/N,V=VOLUME/K/N,NM=NOMOUSE/S,ND=NODISK/S", argres, NULL ) )
				{
				newbroker.nb_Pri = ( argres[0] ? *(ULONG *)argres[0] : 0 );
				PopUp = ( argres[1] ? TRUE : FALSE );
				strcpy( hotkey, ( argres[2] ? (char *)argres[2] : "lalt b" ) );
				strcpy( blankkey, ( argres[3] ? (char *)argres[3] : "ctrl lalt b" ) );
				wleft = ( argres[4] ? (UWORD)(*(ULONG *)argres[4]) : ~0 );
				wtop = ( argres[5] ? (UWORD)(*(ULONG *)argres[5]) : ~0 );
				RandomClient = ( argres[6] ? TRUE : FALSE );
				delaySecs = ( argres[7] ? *(ULONG *)argres[7] : 60 );
				changeSecs = ( argres[8] ? *(ULONG *)argres[8] : 60 );
				if ( argres[9] )
					strcpy( DefaultModeName, (char *)argres[9] );
				strcpy( ListName, ( argres[10] ? (char *)argres[10] : "ClientList" ) );
				briLevel = ( argres[11] ? *(ULONG *)argres[11] : 100 );
				sndLevel = ( argres[12] ? *(ULONG *)argres[12] : 100 );
				stop_on_mouse = ( argres[13] ? FALSE : TRUE );
				stop_on_disk = ( argres[14] ? FALSE : TRUE );
				FreeArgs( rdargs );
				}
			else PrintFault( IoErr(), "BServer" );
			}
		else
			{
			if ( IconBase = OpenLibrary( "icon.library", 37L ) )
				{
				char **tooltypes = (char **)ArgArrayInit( argc, argv );
				newbroker.nb_Pri = ArgInt( tooltypes, "CX_PRIORITY", 0 );
				strcpy( hotkey, ArgString( tooltypes, "CX_HOTKEY", "lalt b" ) );
				strcpy( blankkey, ArgString( tooltypes, "BLANKKEY", "ctrl lalt b" ) );
				delaySecs = ArgInt( tooltypes, "TIMEOUT", 60 );
				changeSecs = ArgInt( tooltypes, "CHANGETIME", 60 );
				briLevel = ArgInt( tooltypes, "BRIGHTNESS", 100 );
				sndLevel = ArgInt( tooltypes, "VOLUME", 100 );
				wleft = ArgInt( tooltypes, "WLEFT", ~0 );
				wtop = ArgInt( tooltypes, "WTOP", ~0 );
				strcpy( ListName, ArgString( tooltypes, "LIST", "ClientList" ) );
				PopUp =
					!Stricmp(ArgString(tooltypes, "CX_POPUP", "NO"), "YES" );
				RandomClient =
					!Stricmp(ArgString(tooltypes, "RANDOM", "YES"), "YES" );
				strcpy( DefaultModeName, ArgString( tooltypes, "DISPLAY", NULL ) );
				stop_on_mouse = 
					!Stricmp(ArgString(tooltypes, "NOMOUSE", "YES"), "YES" );
				stop_on_disk = 
					!Stricmp(ArgString(tooltypes, "NODISK", "YES"), "YES" );

				ArgArrayDone();
				CloseLibrary( IconBase );
				}

			WBenchMsg = (struct WBStartup *)argv;
			if ( WBenchMsg->sm_NumArgs - 1 )
				{
				register struct WBArg *arg = WBenchMsg->sm_ArgList;
				arg++;
				CurrentDir( arg->wa_Lock );
				strcpy( ListName, arg->wa_Name );
				}
			}
		CloseLibrary( UtilityBase );
		}

	if ( stop_on_mouse )
		bservernewmenus[5].nm_Flags |= CHECKED;
	if ( stop_on_disk )
		bservernewmenus[6].nm_Flags |= CHECKED;

	if ( list = CreateModeList() )
		{
		GetDisplayNodeFromName();
		DeleteModeList();
		}

	delayEvents = delaySecs * 10;
	changeEvents = changeSecs * 10;

	if ( CxBase = OpenLibrary( "commodities.library", 37L ) )
		{
		if ( GadToolsBase = OpenLibrary( "gadtools.library", 0L ) )
			{
			if ( broker_mp = CreateMsgPort() )
				{
				CXSignal = 1L << broker_mp->mp_SigBit;
				newbroker.nb_Port = broker_mp;

				if ( broker = CxBroker( &newbroker, NULL ) )
					{
					if ( (handlerSigBit = AllocSignal( -1 )) != -1 )
						{
						handlerSignal = 1L << handlerSigBit;

						if ( (timerSigBit = AllocSignal( -1 )) != -1 )
							{
							timerSignal = 1L << timerSigBit;

							thisTask = FindTask( NULL );

							if ( customobj = CxCustom( BlankerAction, 0L ) )
								{
								AttachCxObj( broker, customobj );

								if ( hotkeyfilter = HotKey( hotkey, broker_mp, EVT_HOTKEY ) )
									{
									AttachCxObj( broker, hotkeyfilter );
									if ( blankkeyfilter = HotKey( blankkey, broker_mp, EVT_BLANKKEY ) )
										AttachCxObj( broker, blankkeyfilter );
									}

								if ( !CxObjError( blankkeyfilter ) )
									{
									if ( firstNode = AllocVec( sizeof(struct ClientNode), MEMF_CLEAR ) )
										{
										NewList( &ClientsList );
										firstNode->cn_Node.ln_Name = GetString( &li, MSG_BUILTIN_BLANKER );
										AddTail( &ClientsList, (struct Node *)firstNode );

										if ( SetUpServerPort() )
											{
											GetClientNames( ListName );

											if ( PopUp )
												PopUpWindow();

											ActivateCxObj( broker, 1L );
											ProcessUserMessages();
											ShutWindow();
											DropClientNames();
											DeletePort( ServerPort );
											}
										else
											FreeVec( firstNode );
										}
									}
								}
							FreeSignal( timerSigBit );
							}
						FreeSignal( handlerSigBit );
						}
					DeleteCxObjAll( broker );
					}
				DeleteMsgPort( broker_mp );
				}
			CloseLibrary( GadToolsBase );
			}
		CloseLibrary( CxBase );
		}
	CloseLibrary( (struct Library *)IntuitionBase );
	}

if ( LocaleBase )
	{
	if ( li.li_Catalog )
		CloseCatalog( li.li_Catalog );
	CloseLibrary( LocaleBase );
	}
}
