#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);

/* 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, *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;

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 Blanking = FALSE;
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 */
UBYTE briLevel = 100;			/* Livello di luminosità */
UWORD DefaultClient = 0;
UWORD TotalClients = 0;	
BOOL stop_on_mouse = TRUE, stop_on_disk = TRUE;

#define MUST_RETRY 0
#define MUST_BLANK 1

BOOL BlankStatus = MUST_RETRY;

/* 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);
void main(int,char**);

 /*************************
 *                        *
 * 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;

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 ( !Blanking )
				{
				clientMPort = bmsg->cm_Msg.mn_ReplyPort;
				bmsg->DInfo = AllocDisplayIDInformation( DisplayID );
				blankingAction = BLANKING_EXTERN;
				BlankStatus = MUST_BLANK;
				Blanking = TRUE;
				}
			break;
		case ACTION_FAILED:
			if ( Blanking )
				{
				Blanking = FALSE;
				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 *
 *                *
 *****************/

UWORD ActiveClient;

void StartBlanking( void )
{
ULONG secs, micr;

clientMPort = NULL;

if ( !Blanking )
	{
	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 = secs % TotalClients + 1;
			}
		else
			ActiveClient = DefaultClient;
		}

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


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

Blanking = FALSE;
blankingAction = BLANKING_NONE;

DefaultClient = copy_of_DefaultClient;
RandomClient = copy_of_RandomClient;

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


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

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 )
		StayCool = FALSE;
	}

if ( Blanking )
	StopBlanking();
}


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

#define POP_KEY_ID      100

extern CxObj *broker , *sender, *translate;
CxObj *hotkeyfilter;
CxObj *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 )
	{
	if ( delaySecs && ++timeElapsed >= delayEvents && BlankStatus != MUST_BLANK )
		{
		BlankStatus = MUST_BLANK;
		Signal( thisTask, timerSignal );
		}
	}
else
	{
	if ( !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 )
		{
		timeElapsed = 0;
		if ( BlankStatus != MUST_RETRY )
			{
			BlankStatus = MUST_RETRY;
			Signal( thisTask, handlerSignal );
			}
		}
	}
}


extern char wname[100];
extern struct NewMenu bservernewmenus[];
extern struct EasyStruct easyabout;

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

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_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 );
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[12] = { 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,R=RANDOM/S,T=TIMEOUT/K/N,D=DISPLAY/K,L=LIST/K,B=BRIGHTNESS/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" ) );
				RandomClient = ( argres[3] ? TRUE : FALSE );
				delaySecs = ( argres[4] ? *(ULONG *)argres[5] : 60 );
				if ( argres[5] )
					strcpy( DefaultModeName, (char *)argres[5] );
				strcpy( ListName, ( argres[6] ? (char *)argres[6] : "ClientList" ) );
				briLevel = ( argres[7] ? *(ULONG *)argres[7] : 100 );
				stop_on_mouse = ( argres[8] ? FALSE : TRUE );
				stop_on_disk = ( argres[9] ? 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" ) );
				delaySecs = ArgInt( tooltypes, "TIMEOUT", 3 );
				briLevel = ArgInt( tooltypes, "BRIGHTNESS", 100 );
				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;

	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, 1L ) )
									{
									AttachCxObj( broker, hotkeyfilter );
									strcpy(wname, PROGRAMNAME": HotKey = <");
									len = sizeof(PROGRAMNAME": HotKey = <") - 1;
									strncpy(wname + len, hotkey, 100 - len);
									len = strlen(wname);
									strncpy(wname + len, ">", 100 - len);
									}

								if ( !CxObjError( hotkeyfilter ) )
									{
									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 );
	}
}
