/* Copyright (C) 1998, Jake Hamby.  All rights reserved.

  This file is part of Aladdin Ghostscript.

  Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author
  or distributor accepts any responsibility for the consequences of using it,
  or for whether it serves any particular purpose or works at all, unless he
  or she says so in writing.  Refer to the Aladdin Ghostscript Free Public
  License (the "License") for full details.

  Every copy of Aladdin Ghostscript must include a copy of the License,
  normally in a plain ASCII text file named PUBLIC.  The License grants you
  the right to copy, modify and redistribute Aladdin Ghostscript, but only
  under certain conditions described in the License.  Among other things, the
  License requires that the copyright notice and this notice be preserved on
  all copies.
*/

/* gsfront.cpp */
/* Shared library front-end to Ghostscript.  Simulates traditional "gs" */
/* executable, but opens a window for the native BeOS GUI driver */

/* Note by Sander Stoks:
 * This is mostly Jake Hamby's work.  All I did was modify it to use
 * ArgvReceived() and RefsReceived() properly, so you can set this front-end
 * to be the preferred app for application/postscript files and have it
 * launch with the file opened when you double-click a PS file. 
 * This is only of limited use since I left the "next page" stuff
 * unimplemented.  A "proper" front end would open a small window with
 * backward/forward buttons.  Maybe I'll write one on a rainy Sunday afternoon
 * (and although I have plenty of those here in London, don't hold your breath :-) */

#include <stdio.h>
#include <string.h>
#include <Application.h>
#include <Screen.h>
#include <ScrollView.h>
#include <MenuItem.h>
#include <Alert.h>
#include <Path.h>
#include <Entry.h>
#include <Beep.h>
typedef void *HWND;

extern "C" {
#include "gsdll.h"
void gsdll_draw(unsigned char *device, BView *view, BRect dest, BRect src);
}

// Local class declarations

class PSWindow : public BWindow {
public:
	PSWindow(BRect frame, const char *title, window_type type, ulong flags);
	bool QuitRequested();
};

class PSView : public BView {
public:
        PSView(BRect area);
        void Draw(BRect area);
};

class PSApplication : public BApplication {
public:
    PSApplication(const char *sig);
    void AboutRequested();
    void MessageReceived(BMessage *message);
    bool QuitRequested();
    void RefsReceived(BMessage *message);
    void ReadyToRun();

    PSWindow *mywind;
	PSView *psview;
	BScrollView *myscrollview;
	thread_id my_thread;
	bool files_in_launch;
};

// Private message types
#define SHOW_FILE_PANEL	'SPNL'
#define NEXT_PAGE		'NXPG'

// The BeOS device to draw
unsigned char *bdev = NULL;

// more global variables
PSApplication *my_app;
char buf[256];
int myargc;
char **myargv;
bool terminal;

PSWindow::PSWindow(BRect frame, const char *title, window_type type,
			ulong flags) : BWindow(frame, title, type, flags)
{
}

bool PSWindow::QuitRequested() {
	be_app->PostMessage(B_QUIT_REQUESTED);
	return true;
}


PSView::PSView(BRect frame) : BView(frame, "PSView", B_FOLLOW_ALL_SIDES,
		B_WILL_DRAW) {
}

void PSView::Draw(BRect area) {
	if(bdev) {
		gsdll_lock_device(bdev, 1);
		gsdll_draw(bdev, this, area, area);
		gsdll_lock_device(bdev, 0);
	}
}


// Only used to open and close BWindow
int
gsdll_callback(int message, char *str, unsigned long count)
{
	static bool child_added = 0;
    char *p;
    switch (message) {
    case GSDLL_STDIN:
        p = fgets(str, count, stdin);
        if (p)
           return strlen(str);
        else
           return 0; 
    case GSDLL_STDOUT:
        if (str != (char *)NULL)
			fwrite(str, 1, count, stderr);
        return count;
    case GSDLL_DEVICE:
//		fprintf(stderr, "GSDLL_DEVICE called\n");
		bdev = (unsigned char *)str;
		if (count && !child_added) {	// open window
			child_added = 1;
			my_app->mywind->Lock();
			my_app->mywind->Show();
			my_app->mywind->Unlock();
		} else {	// close window
			// do nothing right now
		}
		break;
    case GSDLL_SYNC:
//        fprintf(stderr,"Callback: SYNC %p\n", str);
    case GSDLL_PAGE:
//       fprintf(stderr,"Callback: PAGE %p\n", str);
        my_app->mywind->Lock();
        my_app->psview->Invalidate();
        my_app->mywind->Unlock();
        break;
    case GSDLL_SIZE:
//        fprintf(stderr,"Callback: SIZE %p width=%d height=%d\n", str,
//            (int)(count & 0xffff), (int)((count>>16) & 0xffff) );
        break;
    case GSDLL_POLL:
        return 0; /* no error */
    default:
//        fprintf(stderr,"Callback: Unknown message=%d\n",message);
        break;
    }
            return 0;
}

int main (int32 argc, char *argv[])
{
	myargc = argc;
	myargv = argv;
    PSApplication app("application/x-vnd.jehamby.gs");
    my_app = &app;	// so global functions can find the app
    
    terminal = true;
    
    app.Run();
    return 0;
}

long gsloop(void *foo);

PSApplication::PSApplication(const char *sig) : BApplication(sig) {
	BScreen myscreen;
	BRect screensize;
	if (myscreen.IsValid()) {
		screensize = myscreen.Frame();
	} else {
		screensize = BRect(0,0,640,480);
	}
	BRect frame(205, 10, screensize.right, screensize.bottom);
    mywind = new PSWindow(frame, "Main Ghostscript Window", B_DOCUMENT_WINDOW, 0);
	frame.OffsetTo(B_ORIGIN);
	psview = new PSView(BRect(0, 0, screensize.right-206-B_V_SCROLL_BAR_WIDTH,
							screensize.bottom-11-B_H_SCROLL_BAR_HEIGHT));
	myscrollview = new BScrollView("Scroll View", psview, B_FOLLOW_ALL,
			0, true, true, B_FANCY_BORDER);
	myscrollview->ScrollBar(B_HORIZONTAL)->SetRange(0, 611);
	myscrollview->ScrollBar(B_VERTICAL)->SetRange(0, 791);
	mywind->AddChild(myscrollview);
	mywind->Run();
}

void PSApplication::ReadyToRun ()
{
	my_thread = spawn_thread(gsloop, "GS Loop", B_NORMAL_PRIORITY, (void *)psview);
	resume_thread(my_thread);
}

long gsloop(void *psview)
{
    int code;
    code = gsdll_init(gsdll_callback, NULL, myargc, myargv);
//	fprintf(stderr, "gsdll_init returns %d\n", code);
    code = gsdll_execute_begin();
    if (code==0)
    {
		fprintf(stderr, "GS>");
		fflush(stderr);
		while(1)
		{
		    if(!fgets(buf, sizeof(buf), stdin))
				break;

			code = gsdll_execute_cont(buf, strlen(buf));
			// fprintf(stderr, "gsdll_execute returns %d\n", code);
			if (code < 0)
		    	break;

			fprintf(stderr, "GS>");
			fflush(stderr);
		}
		if (!code)
			code = gsdll_execute_end();
		code = gsdll_exit();
		// fprintf(stderr, "gsdll_exit returns %d\n", code);
	}

	be_app->PostMessage(B_QUIT_REQUESTED);
    return 0;
}

void PSApplication::AboutRequested() {
   BAlert *alert;

   alert = new BAlert("", "Ghostscript 6\nCopyright 2000 Aladdin Enterprises.\n\
BeOS port by Jake Hamby & Sander Stoks.\nPlease send all comments to:\n    sanders@sumware.demon.nl",
                      "OK", NULL, NULL, B_WIDTH_AS_USUAL, B_INFO_ALERT);
   alert->Go();
}

void PSApplication::MessageReceived(BMessage *message)
{
	switch (message->what) {
		case SHOW_FILE_PANEL:
//			RunFilePanel();         // Use default values
			fprintf(stderr, "Open file...\n");
			break;
        case NEXT_PAGE:
			fprintf(stderr, "Next page...\n");
			break;
		default:
			BApplication::MessageReceived(message);
            break;
	}
}

bool PSApplication::QuitRequested() {
	kill_thread(my_thread);	// Kill Ghostscript thread if still running
	return true;			// and exit app
}

void PSApplication::RefsReceived(BMessage *message)
{
	terminal = false;
	BPath path;
	entry_ref ref;
	if (message->FindRef ("refs", &ref) == B_OK)
	{
		myargc = 2;
		myargv[0] = new char[16];
		strcpy (myargv[0], "gs");
		path.SetTo (&ref);
		myargv[1] = new char[strlen(path.Path()) + 1];
		strcpy (myargv[1], path.Path());
	}
}
