//
// GWindow.cc
//
// (c) Copyright 1993, San Diego State University -- College of Sciences
//       (See the COPYRIGHT file for more Copyright information)
//
// Implementation of the Window class
//
#include "GWBinary.h"
#include "GWDirectory.h"
#include "GWBookmarks.h"
#include "GWFile.h"
#include "GWIndex.h"
#include "GWSound.h"
#include "GWImage.h"
#include "GWTelnet.h"
#include "Connection.h"
#include <unistd.h>
#include <xview/notice.h>
#include "xvgopher.h"
#include "icons.h"
#include "cursor.h"

int GWindow::next_x = 0;
int GWindow::next_y = 0;


//***************************************************************************
// char *good_strtok(char *str, char *term)
// PURPOSE:
//   This is a replacement for the standard strtok() which has a MAJOR
//   problem when it comes to having multiple delimiters in a row.
//   This one will return after EVERY terminator that is found.
//
char *good_strtok(char *str, char *term)
{
	static char	*string;
	if (str)
	{
		string = str;
	}

	if (string == NULL || *string == '\0')
		return NULL;

	char *p = string;
	while (*string && strchr(term, *string) == NULL)
		string++;
	if (*string)
		*string++ = '\0';
	return p;
}


//***************************************************************************
// GWindow::GWindow()
//
GWindow::GWindow()
{
	gopher = NULL;
	frame = parent = NULL;
	panel = NULL;
	info = NULL;
}


//***************************************************************************
// GWindow::~GWindow()
//
GWindow::~GWindow()
{
	if (gopher)
		delete gopher;
	if (info)
		delete info;

	//
	// If this is the parent of all windows, also delete the bookmarks so that
	// all changes get written to disk.
	//
	if (!parent)
		delete bookmarks;

	xv_destroy_safe(frame);
}


//***************************************************************************
// void GWindow::main_loop()
// PURPOSE:
//    This is the main loop for the windowing program.
//    When we return from this, the program should terminate
//
void GWindow::main_loop()
{
	xv_main_loop(frame);
}


//***************************************************************************
// void GWindow::display()
// PURPOSE:
//    Display information from the gopher server in our window
//
void GWindow::display()
{
	frame_unbusy(frame);
}


//***************************************************************************
// int GWindow::open(Response *resp)
//
int GWindow::open(Response *resp)
{
	resp = resp;
	printf("ERROR!!!  THIS SHOULD NEVER HAPPEN!\n");
	exit(42);
	return 0;
}


//***************************************************************************
// void GWindow::quit_proc(Frame frame, Event *event)
// PURPOS:
//   This gets called when the user presses 'q' in a window.
//
void GWindow::quit_proc(Frame frame, Event *event)
{
	event = event;
	GWindow	*win = (GWindow *) xv_get(frame, XV_KEY_DATA, KEY_GWINDOW);
	delete win;
	xv_destroy_safe(frame);
}


//***************************************************************************
// void GWindow::done_proc(Frame frame)
// PURPOSE:
//   This gets called whenever a command frame is destroyed.  We need to
//   clean up the associated GWindow structure.  Since we want all children
//   to go away as well, we will call this function recursively on our subframes.
//
void GWindow::done_proc(Frame frame)
{
	if (!frame)
		return;
	GWindow	*win = (GWindow *) xv_get(frame, XV_KEY_DATA, KEY_GWINDOW);
	Frame	subframe = (Frame) xv_get(frame, FRAME_NTH_SUBFRAME, 1);
	done_proc(subframe);
	delete win;
	xv_destroy_safe(frame);
}


//***************************************************************************
// void GWindow::status(char *str)
// PURPOSE:
//   Display an informative message in the footer of the window
//
void GWindow::status(char *str)
{
	xv_set(frame,
		FRAME_LEFT_FOOTER,				str,
		NULL);
}


//***************************************************************************
// void GWindow::compute_location(int width, int height)
// PURPOSE:
//   Compute the location of the next window.  The size of the next window is
//   given so that we can make sure that the whole window is displayed on the
//   screen.
//
void GWindow::compute_location(int width, int height)
{
	GWindow	* gwindow = (GWindow *) xv_get(parent, XV_KEY_DATA, KEY_GWINDOW);
	static int	first_time = 2;

	if (!gwindow->parent && first_time)
	{
		//
		// The current window is the second window of the program.  This is because
		// our parent doesn't have a parent.
		//
		next_x = (int) xv_get(parent, XV_X);
		next_y = (int) xv_get(parent, XV_Y);
		first_time--;
	}

	//
	// We now have next_x and next_y to work with as the location of the previous window.
	// Now we need to increment them and possibly wrap them if the window will not fit
	// on the screen.
	//
	next_x += 10;
	next_y += 25;

	//
	// Get the size of the screen.  We will use the parent frame for this since
	// we are not sure that our frame has been initialized, yet.
	//
	Display	*dpy = (Display *) xv_get(parent, XV_DISPLAY);
	int screen_width = DisplayWidth(dpy, DefaultScreen(dpy));
	int screen_height = DisplayHeight(dpy, DefaultScreen(dpy));
	if (next_x + width + 20 > screen_width)
		next_x = 0;
	if (next_y + height + 40 > screen_height)
		next_y = 0;
}


//***************************************************************************
// GWindow *CreateWindow(Response *resp, Frame par)
// PURPOSE:
//   This function will create a new window which will display the
//   data specified in the arguments correctly.
//
GWindow *CreateWindow(Response *resp, Frame par)
{
	GWindow	*gw;

	switch (resp->get_type())
	{
		case GOPHER_FILE:
			gw = new GWFile(par);
			break;
		case GOPHER_DIRECTORY:
			gw = new GWDirectory(par);
			break;
		case GOPHER_SPECIAL:
			gw = new GWDirectory(par);
			break;
		case GOPHER_SOUND:
			gw = new GWSound(par);
			break;
		case GOPHER_BIN:
			gw = new GWBinary(par);
			break;
		case GOPHER_CSO:
		case GOPHER_ERROR:
			xv_create(par, NOTICE,
				NOTICE_MESSAGE_STRINGS,		"XvGopher currently doesn not support this",
											NULL,
				NOTICE_BUTTON_YES,			"Bummer",
				NOTICE_BLOCK_THREAD,		TRUE,
				NOTICE_LOCK_SCREEN,			TRUE,
				XV_SHOW,					TRUE,
				NULL);
			return NULL;
			break;
		case GOPHER_BINHEX:
		case GOPHER_DOS:
		case GOPHER_UU:
			gw = new GWBinary(par);
			break;
		case GOPHER_INDEX:
			gw = new GWIndex(par);
			break;
		case GOPHER_TELNET:
			gw = new GWTelnet(par);
			break;
		case GOPHER_IMAGE:
			gw = new GWImage(par);
			break;
		case GOPHER_REDUNDANT:
		default:
			printf("Sorry, '%c' not implemented, yet!\n", resp->get_type());
			return NULL;
	}
	if (gw->open(resp) == OK)
		return gw;
	else
	{
		delete gw;
		return NULL;
	}
}


//***************************************************************************
// void GWindow::nothing_found()
// PURPOSE:
//   This function will create a new window which will display the
//   data specified in the arguments correctly.
//
void GWindow::nothing_found()
{
	xv_create(parent, NOTICE,
		NOTICE_BLOCK_THREAD,			TRUE,
		NOTICE_LOCK_SCREEN,				TRUE,
		NOTICE_MESSAGE_STRINGS,			"Nothing was found!", NULL,
		NOTICE_BUTTON_YES,				"Bummer",
		XV_SHOW,						TRUE,
		NULL);
	delete this;
}


//***************************************************************************
// void GWindow::list_full()
//   This will create a popup telling the user that the scrolling list is full.
//   I have imposed a limit because xview will hang when a scrolling list gets
//   more than a couple thousand entries in it.
//
void GWindow::list_full()
{
	xv_create(frame, NOTICE,
		NOTICE_MESSAGE_STRINGS,		"The scrolling list is full.  Sorry",
									NULL,
		NOTICE_BUTTON_YES,			"Oh well",
		NOTICE_BLOCK_THREAD,		TRUE,
		NOTICE_LOCK_SCREEN,			TRUE,
		XV_SHOW,					TRUE,
		NULL);
}


