#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <intuition/icclass.h>
#include <libraries/asl.h>
#include <libraries/gadtools.h>
#include <datatypes/datatypes.h>
#include <datatypes/datatypesclass.h>
#include <datatypes/pictureclass.h>

#include <string.h>

#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/datatypes.h>
#include <proto/graphics.h>
#include <proto/gadtools.h>
#include <proto/asl.h>
#include <proto/dos.h>
#include <proto/diskfont.h>
#include <proto/utility.h>

/* in PSM.c */
extern struct Library *DataTypesBase;
extern struct EasyStruct easyr;
extern struct Window *psmwindow;
extern char *thisscrname, pubscrname[];
extern UWORD scrcounter, screens_opened, listv_scr_nmb;
extern struct List *scrlist;
extern struct NewMenu pubscrnewmenus[];
extern struct Gadget *glist;

extern void RefreshList( void );
extern struct PubScrNode *FindNthNode( WORD );

struct PubScrNode {
	struct Node psns_Node;
	struct Screen *psns_Screen;
	struct TextAttr *psns_TextAttr;
	struct TextFont *psns_Font;
	struct Window *psns_BackdropWindow;
	struct Menu *psns_MenuStrip;
	struct VisualInfo *psns_VisualInfo;
	};

/* in locale.h */
extern char *s_noasl, *s_continue, *s_cantclose;



char backdropname[256];
static struct TextAttr *fontattr;


void ErrorNoAsl( void )
{
easyr.es_TextFormat = s_noasl;
easyr.es_GadgetFormat = s_continue;
EasyRequest( psmwindow, &easyr, NULL );
}


void FreeFontAttr( void )
{
if ( fontattr )
	{
	if ( fontattr->ta_Name )
		FreeVec( fontattr->ta_Name );
	FreeVec( fontattr );
	}
}


BOOL AllocTextAttr( STRPTR name, UWORD height )
{
FreeFontAttr();

if ( name && (fontattr = AllocVec( sizeof(struct TextAttr), MEMF_ANY|MEMF_CLEAR )) )
	{
	if ( fontattr->ta_Name = AllocVec( strlen(name) + 1, MEMF_ANY|MEMF_CLEAR ) )
		{
		strcpy( fontattr->ta_Name, name );
		fontattr->ta_Flags = FPF_DISKFONT;
		fontattr->ta_Style = 0;
		fontattr->ta_YSize = height;
		return TRUE;
		}
	}
return FALSE;
}


void FontRequest( void )
{
struct FontRequester *req;

if ( AslBase = OpenLibrary( "asl.library", 38L ) )
	{
	if ( req = AllocAslRequestTags( ASL_FontRequest,ASLFO_PubScreenName,thisscrname,TAG_END ) )
		{
		if ( AslRequestTags( req, TAG_END ) )
			AllocTextAttr( req->fo_Attr.ta_Name, req->fo_Attr.ta_YSize );
		FreeAslRequest( req );
		}
	CloseLibrary( AslBase );
	}
else
	ErrorNoAsl();
}



ULONG __asm Filter ( register __a0 struct Hook *h, register __a2 struct FileRequester *fr, register __a1 struct AnchorPath *ap)
{
struct DataType *dtn;
ULONG use = FALSE;
UBYTE buffer[300];
BPTR lock, handle;

strncpy( buffer, fr->fr_Drawer, sizeof(buffer) );
AddPart( buffer, ap->ap_Info.fib_FileName, sizeof(buffer) );
if ( lock = Lock( buffer, ACCESS_READ ) )
	{
	if ( DataTypesBase && ( dtn = ObtainDataTypeA( DTST_FILE, (APTR)lock, NULL ) ) )
		{
		if ( dtn->dtn_Header->dth_GroupID == GID_PICTURE )
			use = TRUE;
		ReleaseDataType (dtn);
		}
	else
		if ( handle = Open( buffer, MODE_OLDFILE ) )
			{
			if ( 12 == Read( handle, buffer, 12 ) )
				if ( *((ULONG*)buffer) == ID_FORM && *((ULONG*)(buffer + 8)) == ID_ILBM )
					use = TRUE;
			Close( handle );
			}
	UnLock( lock );
	}
return use;
}


static struct FileRequester *request;
static struct Hook filter;
static char filepart[256], pathpart[256];

struct TagItem rtags[] = {
{ ASLFR_InitialFile, (ULONG)&filepart },
{ ASLFR_InitialDrawer, (ULONG)&pathpart },
{ ASLFR_InitialHeight, 210L },
{ ASLFR_RejectIcons, TRUE },
{ ASLFR_FilterFunc, (ULONG)&filter },
{ TAG_END, NULL } };


void PattRequest( void )
{
if ( AslBase = OpenLibrary( "asl.library", 38L ) )
	{
	if ( request = (struct FileRequester *)AllocAslRequest( ASL_FileRequest, rtags ) )
		{
		filter.h_Entry = (ULONG (*)())Filter;

		strcpy( filepart, FilePart( backdropname ) );
		strcpy( pathpart, backdropname );
		*PathPart( pathpart ) = 0;

		if ( AslRequest( request, rtags ) )
			{
			strcpy( backdropname, request->fr_Drawer );
			if ( *backdropname )
			if ( backdropname[ strlen(backdropname)-1 ] != ':' && backdropname[ strlen(backdropname)-1 ] != '/' )
				strcat( backdropname, "/" );
			strcat( backdropname, request->fr_File );
			}
		FreeAslRequest( request );
		}
	CloseLibrary( AslBase );
	}
}


void OpenPubScreen( void )
{
struct ScreenModeRequester *req;
struct Screen *scr;
struct PubScrNode *node;
STRPTR screen_name;
UBYTE scrnamelen = strlen( pubscrname );
BOOL scrpresent = FALSE;
UWORD neww = 640, newh = 512, newd = 2, newautoscr = FALSE;
ULONG newid = HIRES|LACE, newoscan = OSCAN_TEXT;
struct DrawInfo *dr_info;
struct Screen *pubscr;

if ( pubscr = LockPubScreen( NULL ) )
	{
	neww = pubscr->Width;
	newh = pubscr->Height;
	newd = pubscr->RastPort.BitMap->Depth;
	newid = GetVPModeID( &pubscr->ViewPort );
	UnlockPubScreen( NULL, pubscr );
	}

if ( AslBase = OpenLibrary( "asl.library", 38L ) )
	{
	if ( req = AllocAslRequestTags( ASL_ScreenModeRequest,
				ASLSM_Window, psmwindow,
				ASLSM_SleepWindow, TRUE,
				ASLSM_InitialDisplayID, newid,
				ASLSM_InitialDisplayDepth, newd,
				ASLSM_DoDepth, TRUE,
				ASLSM_DoOverscanType, TRUE,
				ASLSM_DoAutoScroll, TRUE,
				ASLSM_InitialDisplayWidth, neww,
				ASLSM_DoWidth, TRUE,
				ASLSM_InitialDisplayHeight, newh,
				ASLSM_DoHeight, TRUE,
				ASLSM_PubScreenName, thisscrname,
				TAG_END ) )
		{
		if ( AslRequestTags( req, TAG_END ) )
			{
			newid = req->sm_DisplayID;
			neww =  req->sm_DisplayWidth;
			newh = req->sm_DisplayHeight;
			newd = req->sm_DisplayDepth;
			newoscan = req->sm_OverscanType;
			newautoscr = req->sm_AutoScroll;
			scrpresent = TRUE;
			}
		FreeAslRequest( req );
		}
	CloseLibrary( AslBase );
	}

if ( !scrpresent )
	return;

do	{
	scrpresent = FALSE;

	pubscrname[scrnamelen-1] = scrcounter % 10 + '0';
	pubscrname[scrnamelen-2] = (scrcounter / 10) %10 + '0';
	pubscrname[scrnamelen-3] = scrcounter / 100 + '0';

	if ( LockPubScreen( pubscrname ) )
		{
		scrcounter++;
		scrpresent = TRUE;
		UnlockPubScreen( pubscrname, NULL );
		}
	} while( scrpresent );

if ( node = AllocVec( sizeof(struct PubScrNode), MEMF_ANY|MEMF_CLEAR ) )
	{
	if ( screen_name = AllocVec( MAXPUBSCREENNAME+1, MEMF_ANY|MEMF_CLEAR ) )
		{
		node->psns_Node.ln_Name = screen_name;
		strcpy( screen_name, pubscrname );
		if ( node->psns_TextAttr = AllocVec( sizeof(struct TextAttr), MEMF_ANY|MEMF_CLEAR ) )
			{
			node->psns_TextAttr->ta_Flags = FPF_DISKFONT;
			node->psns_TextAttr->ta_Style = 0;
			node->psns_TextAttr->ta_YSize = fontattr->ta_YSize;

			if ( node->psns_TextAttr->ta_Name = AllocVec( strlen(fontattr->ta_Name) + 1, MEMF_ANY|MEMF_CLEAR ) )
				{
				strcpy( node->psns_TextAttr->ta_Name, fontattr->ta_Name );

				node->psns_Font = OpenDiskFont( node->psns_TextAttr );

				dr_info = GetScreenDrawInfo( psmwindow->WScreen );

				if ( scr = OpenScreenTags( NULL,
							SA_DisplayID, newid,
							SA_Width, neww,
							SA_Height, newh,
							SA_Depth, newd,
							SA_Overscan, newoscan,
							SA_AutoScroll, newautoscr,
							SA_Title, pubscrname,
							SA_PubName, pubscrname,
							SA_SharePens, TRUE,
							SA_ColorMapEntries, 8,
							SA_LikeWorkbench, TRUE,
							SA_Behind, TRUE,
							SA_Font, node->psns_TextAttr,
							SA_Pens, dr_info->dri_Pens,
							SA_FullPalette, TRUE,
							TAG_END ) )
					{
					struct Window *bw;

					node->psns_Screen = scr;
					PubScreenStatus( scr, 0 );
					screens_opened++;
					scrcounter++;
					AddTail( scrlist, (struct Node *)node );

					if ( node->psns_BackdropWindow = bw = OpenWindowTags( NULL,
						WA_Top, scr->BarHeight,
						WA_Height, newh - scr->BarHeight,
						WA_Backdrop, TRUE,
						WA_Borderless, TRUE,
							WA_CustomScreen, scr,
							WA_IDCMP, IDCMP_IDCMPUPDATE | IDCMP_REFRESHWINDOW | IDCMP_MENUPICK,
							TAG_END ) )
						{
						struct VisualInfo *vi = GetVisualInfo( scr, NULL );
						if ( vi )
							{
							struct Menu *scrm;
							scrm = CreateMenus( pubscrnewmenus, TAG_END );
							if ( scrm && LayoutMenus( scrm, vi, TAG_END ) )
								{
								SetMenuStrip( bw, scrm );
								node->psns_MenuStrip = scrm;
								node->psns_VisualInfo = vi;
								}
							else
								FreeVisualInfo( vi );
							}

						if ( DataTypesBase && (*FilePart(backdropname)) )
							{
							Object *obj;
							if ( obj = NewDTObject( backdropname, DTA_GroupID, GID_PICTURE, PDTA_Remap, TRUE, TAG_END ) )
								{
								struct TagItem *tstate, *tag;
								struct IntuiMessage *imsg;
								struct BitMap *bm;
								struct BitMapHeader *bmh;
								struct ColorRegister *colorreg;
								ULONG color_count;
								BOOL waiting = TRUE;
								UBYTE sparsetable[256];

								if ( 2 == GetDTAttrs( obj, PDTA_ColorRegisters, &colorreg, PDTA_NumColors, &color_count, TAG_END ) )
									{
									ULONG n;
									for ( n = 0; n < color_count; n++ )
										sparsetable[n] = ObtainPen( scr->ViewPort.ColorMap, -1, colorreg[n].red << 24, colorreg[n].green << 24, colorreg[n].blue << 24, PEN_EXCLUSIVE );

									SetDTAttrs( obj, NULL, NULL, GA_Left, 0, GA_Top, 0, GA_Width, neww, GA_Height, newh, ICA_TARGET, ICTARGET_IDCMP, PDTA_NumSparse, color_count, PDTA_SparseTable, sparsetable, TAG_END );
									AddDTObject( bw, NULL, obj, -1 );
									RefreshDTObjects( obj, bw, NULL, NULL );

									while ( waiting )
										{
										WaitPort( bw->UserPort );
										while ( imsg = (struct IntuiMessage *)GetMsg( bw->UserPort ) )
											switch( imsg->Class )
											{
												case IDCMP_IDCMPUPDATE:
													tstate = (struct TagItem *) imsg->IAddress;
													while (tag = NextTagItem (&tstate))
														{
													    switch (tag->ti_Tag)
														    {
															case DTA_Busy:
												    			break;

															case DTA_Sync:
														    RefreshDTObjects( obj, bw, NULL, NULL);
															waiting = FALSE;
														    break;
														    }
														}
													break;
												}
										}

									RemoveDTObject( bw, obj );
									if ( 2 == GetDTAttrs( obj, PDTA_BitMap, &bm, PDTA_BitMapHeader, &bmh, TAG_END ) )
										{
										UWORD w, h, width = bmh->bmh_Width, height = bmh->bmh_Height;

										for ( w = 0; w < bw->Width; w += width )
											for ( h = 0; h < bw->Height; h += height )
												BltBitMap( bm, 0, 0, bw->RPort->BitMap, w, h + scr->BarHeight, ( w + width >= bw->Width ? bw->Width - w : width ), ( h + height >= bw->Height ? bw->Height - h : height ), 0xC0, 0xFF, NULL );
										}

									} /* Color allocation successful*/
									DisposeDTObject( obj );
								} /* NewObject */
							} /* DT && bdname */
						} /* OpenWindow */

					RefreshList();
					}
				FreeScreenDrawInfo( psmwindow->WScreen, dr_info );
				}
			}
		}
	if ( !scr )
		{
		if ( node->psns_TextAttr->ta_Name )
			FreeVec( node->psns_TextAttr->ta_Name );
		if ( node->psns_TextAttr )
			FreeVec( node->psns_TextAttr );
		if ( screen_name )
			FreeVec( screen_name );
		FreeVec( node );
		}
	}
}


static void ErrorCloseScreen( void )
{
easyr.es_TextFormat = s_cantclose;
easyr.es_GadgetFormat = s_continue;
EasyRequest( psmwindow, &easyr, NULL );
}


void ClosePubScreen( struct PubScrNode *node )
{
/*struct PubScrNode *node = FindNthNode( listv_scr_nmb );*/
struct PubScreenNode *pubnode;
struct List *publiclist;
struct Screen *scr;
BOOL canbeclosed = TRUE;

if ( publiclist = LockPubScreenList() )
	{
	for ( pubnode = (struct PubScreenNode *)publiclist->lh_Head; pubnode->psn_Node.ln_Succ; pubnode = (struct PubScreenNode *)pubnode->psn_Node.ln_Succ )
		if ( !strcmp( pubnode->psn_Node.ln_Name, node->psns_Node.ln_Name ) )
			break;

	if ( pubnode->psn_VisitorCount > (node->psns_BackdropWindow ? 1 : 0) )
		canbeclosed = FALSE;
	UnlockPubScreenList();
	}

if ( psmwindow && node->psns_Screen == psmwindow->WScreen )
	canbeclosed = FALSE;

if ( !canbeclosed )
	{
	ErrorCloseScreen();
	return;
	}

GT_SetGadgetAttrs( glist, psmwindow, NULL, GTLV_Selected, 0, TAG_END );
listv_scr_nmb = 0;

Forbid();
scr = LockPubScreen( NULL );
if ( scr == node->psns_Screen )
	SetDefaultPubScreen( "Workbench" );
UnlockPubScreen( NULL, scr );
Permit();

if ( node->psns_BackdropWindow )
	{
	if ( node->psns_MenuStrip )
		{
		ClearMenuStrip( node->psns_BackdropWindow );
		FreeMenus( node->psns_MenuStrip );
		FreeVisualInfo( node->psns_VisualInfo );
		}
	CloseWindow( node->psns_BackdropWindow );
	node->psns_BackdropWindow = NULL;
	}

if ( CloseScreen( node->psns_Screen ) )
	{
	if ( node->psns_Font )
		CloseFont( node->psns_Font );
	if ( node->psns_TextAttr )
		{
		if ( node->psns_TextAttr->ta_Name )
			FreeVec( node->psns_TextAttr->ta_Name );
		FreeVec( node->psns_TextAttr );
		}
	Remove( (struct Node *)node );
	FreeVec( node->psns_Node.ln_Name );
	FreeVec( node );
	RefreshList();
	screens_opened--;
	}
else
	ErrorCloseScreen();
}
