//========================================================================
//
// Apdfcommon.cc
//
// Copyright 1999 Emmanuel Lesueur
//
//========================================================================

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#define Object ZZObject
#ifdef __PPC__
#   include <powerup/ppcproto/exec.h>
#   include <powerup/ppcproto/dos.h>
#else
#   include <proto/exec.h>
#   include <proto/dos.h>
#endif
#undef Object
#include "parseargs.h"
#include "GString.h"
#include "gmem.h"
#include "Object.h"
#include "Stream.h"
#include "Array.h"
#include "Dict.h"
#include "XRef.h"
#include "Catalog.h"
#include "Page.h"
#include "Link.h"
#include "PDFDoc.h"
#include "AOutputDev.h"
#include "TextOutputDev.h"
#include "PSOutputDev.h"
#include "ImageOutputDev.h"
#include "FontOutputDev.h"
#include "Error.h"
#include "config.h"
#include "AComm.h"

#ifdef __SASC
#   undef  try
#   undef  catch
#   define try
#   define catch(x)    if(const char* msg=NULL)
#endif


#ifdef __PPC__
#   define LINKAGE
#else
#   define LINKAGE extern "C"
#endif

static int userFontMapLen,userFontMapSize;
static char* fontPathInit[]={NULL};
char **fontPath = fontPathInit;

static PDFDoc* doc;
static AGfx* gfx;
static AOutputDev* AOut;
int maxColors;
const char* uncompressCmd;
static FontOutputDev* fOut;

extern FILE* errFile;

static BPTR olddir;
static BPTR lock;

#ifdef POWERUP
extern PPCPort* port68k;
#endif

extern "C" {
    unsigned long _MSTEP=0x10000;
}

LINKAGE void clear_fontmap() {
    if(userFontMap) {
	for(int i=0;i<userFontMapLen;++i) {
	    gfree(userFontMap[i].pdfFont);
	    gfree(userFontMap[i].xFont);
	}
	userFontMap[0].pdfFont=NULL;
	userFontMapLen=0;
    }
    //clearFontMap();
}

void cleanup2() {
    CurrentDir(olddir);
    UnLock(lock);
    delete fOut;
    delete AOut;
    delete gfx;
    delete doc;
    clear_fontmap();
    gfree(userFontMap);
    gfree((void*)uncompressCmd);
    Object::memCheck(errFile);
    gMemReport(errFile);
}

LINKAGE int init(int colors,const char* gzip) {
    int ok=1;

    try {

	maxColors=colors;
	olddir=CurrentDir(0);
	CurrentDir(olddir);

	errorInit();

	atexit(&cleanup2);

	userFontMapSize=8;
	userFontMap=(FontMapEntry*)gmalloc(userFontMapSize*sizeof(FontMapEntry));
	userFontMap->pdfFont=NULL;

	uncompressCmd=copyString((char*)gzip);

#ifdef POWERUP
	gfx=new AGfx(port68k);
#else
	gfx=new AGfx;
#endif

    }
    catch(const char* msg) {
	printf("Exception: %s\n",msg);
	ok=0;
    }
    catch(...) {
	printf("Exception caught.");
	ok=0;
    }

    return ok;
}




LINKAGE int create_doc(BPTR dir,const char* filename,size_t maxbuf,size_t blocsz) {
    BPTR olddir=CurrentDir(dir);
    try {
	myFILE::bufsizes(blocsz,maxbuf);
	PDFDoc* d=new PDFDoc(new GString((char*)filename));
	if(d->isOk()) {
	    delete doc;
	    doc=d;
	    UnLock(lock);
	    lock=DupLock(dir);
	    CurrentDir(lock);
	    return 1;
	}
	delete d;
    }
    catch(const char* msg) {
	printf("Exception: %s\n",msg);
    }
    catch(...) {
	printf("Exception caught.");
    }
    CurrentDir(olddir);
    return 0;
}

#if 0
LINKAGE void do_delete_doc() {
    delete doc;
    doc=NULL;
}
#endif

LINKAGE int create_output_dev(int depth,struct ColorMap* colormap) {
    try {
	delete AOut;
	AOut=NULL; //in case the following line throws an exception
	AOut=new AOutputDev(*gfx, depth, colormap);
	AOutputFont::reset();
	return 1;
    }
    catch(const char* msg) {
	printf("Exception: %s\n",msg);
    }
    catch(...) {
	printf("Exception caught.");
    }
    return 0;
}

LINKAGE void delete_output_dev() {
    delete AOut;
    AOut=NULL;
}

LINKAGE int get_info(int* num_pages) {
    if(doc) {
	*num_pages=doc->getNumPages();
	return 1;
    } else {
	*num_pages=1;
	return 0;
    }
}

LINKAGE int get_page_size(int page,int* width,int* height) {
    if(doc) {
	int w=int(doc->getPageWidth(page)+0.5);
	int h=int(doc->getPageHeight(page)+0.5);
	int r=doc->getPageRotate(page);
	if(r==90 || r==270) {
	    *width=h;
	    *height=w;
	} else {
	    *width=w;
	    *height=h;
	}
	return 1;
    } else {
	*width=612;
	*height=792;
	return 0;
    }
}

LINKAGE int simple_find(const char* str,int top,int* xmin,int* ymin,int* xmax,int* ymax) {
    try {
	if(AOut && AOut->findText((char*)str,top,gTrue,xmin,ymin,xmax,ymax))
	    return 1;
    }
    catch(const char* msg) {
	printf("Exception: %s\n",msg);
    }
    catch(...) {
	printf("Exception caught.");
    }
    return 0;
}

static char* find_str;
static TextOutputDev* find_dev;

LINKAGE void end_find() {
    delete find_dev;
    find_dev=NULL;
    gfree(find_str);
    find_str=NULL;
}

LINKAGE int init_find(const char* str) {
    try {
	find_str=copyString((char*)str);
	find_dev=new TextOutputDev(NULL,gFalse);
	if(find_dev->isOk())
	    return 1;
    }
    catch(const char* msg) {
	printf("Exception: %s\n",msg);
    }
    catch(...) {
	printf("Exception caught.");
    }
    end_find();
    return 0;
}

LINKAGE int find(int start,int stop,int* xmin,int* ymin,int* xmax,int* ymax,int* page) {
    int r=0;
    try {
	if(find_dev && find_str) {
	    for(int pg=start;pg<stop;++pg) {
		double x1,x2,y1,y2;
		doc->displayPage(find_dev,pg,72,0,gFalse);
		if(find_dev->findText(find_str,gTrue,gTrue,&x1,&y1,&x2,&y2)) {
		    r=1;
		    *page=pg;
		    break;
		}
	    }
	}
    }
    catch(const char* msg) {
	printf("Exception: %s\n",msg);
    }
    catch(...) {
	printf("Exception caught.");
    }
    return r;
}

LINKAGE int chklink(int mx,int my,void** maction,char* str,size_t len) {
    int r=0;
    try {
	double x,y;
	LinkAction *action;
	const char *s;
	if(doc && AOut) {
	    AOut->cvtDevToUser(mx,my,&x,&y);
	    if(action=doc->findLink(x,y)) {
		if(action!=*maction) {
		    *maction=action;
		    s=NULL;
		    switch(action->getKind()) {
		      case actionGoTo:
			s="[internal link]";
			break;
		      case actionGoToR:
			s=((LinkGoToR*)action)->getFileName()->getCString();
			break;
		      case actionLaunch:
			s=((LinkLaunch*)action)->getFileName()->getCString();
			break;
		      case actionURI:
			s=((LinkURI*)action)->getURI()->getCString();
			break;
		      case actionUnknown:
			s="[unknown link]";
			break;
		    }
		    strncpy(str,s,len-1);
		    str[len-1]='\0';
		}
		r=1;
	    }
	}
    }
    catch(const char* msg) {
	printf("Exception: %s\n",msg);
    }
    catch(...) {
	printf("Exception caught.");
    }
    return r;
}

LINKAGE int dolink(int* mx,int* my,int* state,int* page,char* buf,size_t len) {
    int r=0;
    *state=st_unchanged;
    *page=-1;
    LinkDest* dest=NULL;
    GString* namedDest=NULL;
    GString* fileName=NULL;
    PDFDoc* d=NULL;
    try {
	double x,y;
	AOut->cvtDevToUser(*mx,*my,&x,&y);
	*mx=-1;
	*my=-1;
	if(LinkAction* action=doc->findLink(x,y)) {
	    switch(LinkActionKind kind=action->getKind()) {
		// GoTo / GoToR action
	      case actionGoTo:
	      case actionGoToR:
		if(kind==actionGoTo) {
		    if(dest=((LinkGoTo*)action)->getDest())
			dest=dest->copy();
		    else if(namedDest=((LinkGoTo*)action)->getNamedDest())
			namedDest=namedDest->copy();
		} else {
		    if(dest=((LinkGoToR*)action)->getDest())
			dest=dest->copy();
		    else if(namedDest=((LinkGoToR*)action)->getNamedDest())
			namedDest=namedDest->copy();
		    char* s=((LinkGoToR*)action)->getFileName()->getCString();
		    //~ translate path name for VMS (deal with '/')
		    /*if (isAbsolutePath(s))
		    fileName = new GString(s);
		    else
			fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);*/
		    fileName=new GString(s);
		    d=new PDFDoc(fileName);
		    fileName=NULL;
		    if(d->isOk()) {
			delete doc;
			doc=d;
			d=NULL;
			strncpy(buf,fileName->getCString(),len-1);
			buf[len-1]='\0';
			*state=st_changed;
			*page=1;
		    } else
			break;
		}
		if(namedDest) {
		    dest=doc->findDest(namedDest);
		    //delete namedDest;
		    //namedDest=NULL;
		}
		if(!dest) {
		    if(kind==actionGoToR)
			*page=1;
		} else {
		    Ref pageRef;
		    if(dest->isPageRef()) {
			pageRef=dest->getPageRef();
			*page=doc->findPage(pageRef.num,pageRef.gen);
		    } else
			*page=dest->getPageNum();
		    switch(dest->getKind()) {
		      case destXYZ: {
			int dx,dy;
			AOut->cvtUserToDev(dest->getLeft(),dest->getTop(),&dx,&dy);
			if(dest->getChangeLeft() || dest->getChangeTop()) {
			    if(dest->getChangeLeft())
				*mx=dx;
			    if(dest->getChangeTop())
				*my=dy;
			}
			//~ what is the zoom parameter?
			break;
		      }
		      case destFit:
		      case destFitB:
			//~ do fit
			*mx=0;
			*my=0;
			break;
		      case destFitH:
		      case destFitBH:
			//~ do fit
			AOut->cvtUserToDev(0,dest->getTop(),mx,my);
			break;
		      case destFitV:
		      case destFitBV:
			//~ do fit
			AOut->cvtUserToDev(dest->getLeft(),0,mx,my);
			break;
		      case destFitR:
			//~ do fit
			AOut->cvtUserToDev(dest->getLeft(),dest->getTop(),mx,my);
			break;
		    }
		    //delete dest;
		    //dest=NULL;
		}
		r=1;
		break;

		// Launch action
	      case actionLaunch: {
		fileName=((LinkLaunch *)action)->getFileName();
		char* s=fileName->getCString();
		if(!strcmp(s+fileName->getLength()-4,".pdf") ||
		   !strcmp(s+fileName->getLength()-4,".PDF")) {
		    //~ translate path name for VMS (deal with '/')
		    /*if (isAbsolutePath(s))
			fileName = fileName->copy();
		    else
			fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);*/
		    fileName=new GString(s);
		    d=new PDFDoc(fileName);
		    fileName=NULL;
		    if(d->isOk()) {
			delete doc;
			doc=d;
			d=NULL;
			strncpy(buf,fileName->getCString(),len-1);
			buf[len-1]='\0';
			*state=st_changed;
			*page=1;
		    } else
			break;
		} else {
		    fileName=fileName->copy();
		    if(((LinkLaunch*)action)->getParams()) {
			fileName->append(' ');
			fileName->append(((LinkLaunch*)action)->getParams());
		    }
		    fileName->insert(0,"run ");
		    strncpy(buf,fileName->getCString(),len-1);
		    buf[len-1]='\0';
		    *state=st_run;
		}
		r=1;
		break;
	      }
		// URI action
	      case actionURI:
		strncpy(buf,((LinkURI*)action)->getURI()->getCString(),len-1);
		buf[len-1]='\0';
		*state=st_url;
		r=1;
		break;

		// unknown action type
	      case actionUnknown:
		error(-1,"Unknown link action type: '%s'",((LinkUnknown *)action)->getAction()->getCString());
		break;
	    }
	}
    }
    catch(const char* msg) {
	printf("Exception: %s\n",msg);
    }
    catch(...) {
	printf("Exception caught.");
    }
    delete d;
    delete dest;
    delete namedDest;
    delete fileName;
    return r;
}


static OutputDev* saveDev;
static int saveZoom;
static int saveRotate;

LINKAGE void end_write() {
    delete saveDev;
    saveDev=NULL;
}

LINKAGE int init_write(const char* filename,int first_page,int last_page,int zoom,int rotate,int type) {
    int r=0;
    if(doc) {
	try {
	    switch(type) {
	      case 0: // Postscript
	      case 1: // Postscript Level 1
		if(doc->okToPrint()) {
		    psOutLevel1=type==1;
		    saveDev=new PSOutputDev((char*)filename,doc->getCatalog(),
					    first_page,last_page,gTrue,gFalse);
		}
		break;
	      case 2: // Text
		saveDev=new TextOutputDev((char*)filename,gFalse);
		zoom=72;
		rotate=0;
		break;
	      case 3: // PBM/PPM images
	      case 4: // JPEG images
		saveDev=new ImageOutputDev((char*)filename,type==4);
		zoom=72;
		rotate=0;
		break;
	    }
	    saveZoom=zoom;
	    saveRotate=rotate;
	    r=saveDev->isOk();
	}
	catch(const char* msg) {
	    printf("Exception: %s\n",msg);
	}
	catch(...) {
	    printf("Exception caught.");
	}
	if(!r)
	    end_write();
    }
    return r;
}

LINKAGE void writefile(int first_page,int last_page) {
    if(doc && saveDev) {
	try {
	    doc->displayPages(saveDev,first_page,last_page-1,saveZoom,saveRotate);
	}
	catch(const char* msg) {
	    printf("Exception: %s\n",msg);
	}
	catch(...) {
	    printf("Exception caught.");
	}
    }
}


LINKAGE char* gettext(int minx,int miny,int maxx,int maxy) {
    char* r=NULL;
    if(doc && AOut) {
	GString* s=NULL;
	try {
	    s=AOut->getText(minx,miny,maxx,maxy);
	    size_t sz=s->getLength();
	    if(r=(char*)malloc(sz+1))
		strcpy(r,s->getCString());
	}
	catch(const char* msg) {
	    printf("Exception: %s\n",msg);
	}
	catch(...) {
	    printf("Exception caught.");
	}
	delete s;
    }
    return r;
}



LINKAGE int show_page(int page,int zoom) {
    int r=0;
    if(doc && AOut) {
	try {
	    doc->displayPage(AOut,page,zoom,0,gTrue);
	    r=1;
	}
	catch(const char* msg) {
	    printf("Exception: %s\n",msg);
	}
	catch(...) {
	    printf("Exception caught.");
	}
	gfx->flush();
    }
    return r;
}

LINKAGE void add_fontmap(const char* pdffont,
			 const char* afont,
			 int m,
			 int style,
			 int encoding) {
    try {
	for(int k=0;k<userFontMapLen;++k)
	    if(!strcmp(userFontMap[k].pdfFont,pdffont)) {
		gfree(userFontMap[k].pdfFont);
		gfree(userFontMap[k].xFont);
		--userFontMapLen;
		if(k!=userFontMapLen)
		    memmove(userFontMap+k,userFontMap+k+1,(userFontMapLen-k+1)*sizeof(FontMapEntry));
		break;
	    }
	if(userFontMapLen>=userFontMapSize-1) {
	    userFontMapSize+=8;
	    userFontMap=(FontMapEntry*)grealloc(userFontMap,userFontMapSize*sizeof(FontMapEntry));
	}
	userFontMap[userFontMapLen].pdfFont=copyString((char*)pdffont);
	userFontMap[userFontMapLen].xFont=copyString((char*)afont);
	userFontMap[userFontMapLen].mWidth=m;
	userFontMap[userFontMapLen].style=style;
	userFontMap[userFontMapLen].encoding=encoding;
	userFontMap[++userFontMapLen].pdfFont=NULL;
    }
    catch(const char* msg) {
	printf("Exception: %s\n",msg);
    }
    catch(...) {
	printf("Exception caught.");
    }
}

#if 0
LINKAGE void rem_fontmap(const char* pdffont) {
    for(int k=0;k<devFontMapLen;++k)
	if(!strcmp(devFontMap[k].pdfFont,pdffont)) {
	    gfree(devFontMap[k].pdfFont);
	    gfree(devFontMap[k].devFont);
	    --devFontMapLen;
	    if(k!=devFontMapLen)
		memmove(devFontMap+k,devFontMap+k+1,(devFontMapLen-k)*sizeof(DevFontMapEntry));
	    break;
	}
}

LINKAGE void set_fontmap() {
    //resetFontMap();
}
#endif

LINKAGE void end_scan_fonts() {
    delete fOut;
    fOut=NULL;
}

LINKAGE int init_scan() {
    int r=0;
    if(doc) {
	try {
	    end_scan_fonts();
	    fOut=new FontOutputDev;
	    r=fOut->isOk();
	}
	catch(const char* msg) {
	    printf("Exception: %s\n",msg);
	}
	catch(...) {
	    printf("Exception caught.");
	}
	if(r==0)
	    end_scan_fonts();
    }
    return r;
}

LINKAGE int scan_fonts(int first_page,int last_page) {
    int r=0;
    if(doc && fOut) {
	try {
	    doc->displayPages(fOut,first_page,last_page,72,0);
	    r=fOut->size();
	}
	catch(const char* msg) {
	    printf("Exception: %s\n",msg);
	}
	catch(...) {
	    printf("Exception caught.");
	}
    }
    return r;
}

LINKAGE int scan_default_fonts() {
    int r=0;
    try {
	if(fOut) {
	    delete fOut;
	    fOut=NULL;
	}
	fOut=new DefaultFontOutputDev;
	if(fOut->isOk())
	    r=fOut->size();
    }
    catch(const char* msg) {
	printf("Exception: %s\n",msg);
    }
    catch(...) {
	printf("Exception caught.");
    }
    if(r==0) {
	delete fOut;
	fOut=NULL;
    }
    return r;
}

LINKAGE int get_font(int n,char* pdffont,int pdffontlen,
		      char* afont,int afontlen,
		      int* m,int* style,int* encoding) {
    if(fOut) {
	const char* p;
	const char* q;
	fOut->get(n,p,q,*m,*style,*encoding);
	if(p) {
	    strncpy(pdffont,p,pdffontlen-1);
	    pdffont[pdffontlen-1]='\0';
	    strncpy(afont,q,afontlen-1);
	    afont[afontlen-1]='\0';
	    return 1;
	} else
	    return 0;
    } else
	return 0;
}

