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

#include <stdio.h>
#include <stdlib.h>
#include "gmem.h"

#ifdef __SASC
#   define throw(x) (printf("Exception: %s\n",x),exit(EXIT_FAILURE))
#endif

#ifdef __PPC__
//#define DEBUGMSG
#include <exec/nodes.h>
#include <exec/memory.h>
#include <powerup/ppclib/message.h>
#include <powerup/ppclib/tasks.h>
#include <powerup/ppclib/memory.h>
#include <powerup/gcclib/powerup_protos.h>
#include "AGfx.h"
#include "AGfxcomm.h"
#include "AComm.h"


AGfx::Image::Image(short w,short h)
    : width(w),height(h),bytes_per_row(((w+15)>>4)<<4) {
    im=(pixel_color*)PPCAllocVec(bytes_per_row*h,MEMF_ANY);
    if(!im)
	throw("No memory.");
}

AGfx::Image::~Image() {
    PPCFreeVec(im);
}

AGfx::Image24::Image24(short w,short h)
    : width(w),height(h) {
    im=(Color*)PPCAllocVec(w*h*sizeof(Color),MEMF_ANY);
    if(!im)
	throw("No memory.");
}

AGfx::Image24::~Image24() {
    PPCFreeVec(im);
}

AGfx::ColorTable::ColorTable(int n) : num(n) {
    entries=(ColorEntry*)PPCAllocVec((n+1)*sizeof(ColorEntry),MEMF_ANY);
    if(!entries)
	throw("No memory.");
}

AGfx::ColorTable::~ColorTable() {
    PPCFreeVec(entries);
}

#ifdef USE_GFX_PIPE

static const int bufsize=50000;
static short stubbuf[10];

AGfx::AGfx(void* port1)
    : port(port1),reply_port(NULL),buf(NULL),pipe(NULL),
      msg(NULL),msg_sent(false),in_page_draw(false) {
    buf=(short*)PPCAllocVec(sizeof(GfxPipe)+bufsize*2,MEMF_NOCACHESYNCPPC|MEMF_NOCACHESYNCM68K);
    if(!buf) {
	static GfxPipe p;
	pipe=&p;
	ptr=stubbuf;
	end=ptr;
    } else {
	pipe=(volatile GfxPipe*)buf;
	ptr=pipe->data;
	end=ptr+bufsize;
    }
    if(buf) {
	if(reply_port=PPCCreatePort(NULL))
	    msg=PPCCreateMessage(reply_port,bufsize*2);
    }
}

AGfx::~AGfx() {
    if(msg_sent) {
	pipe->done=1;
	PPCWaitPort(reply_port);
	PPCGetMessage(reply_port);
    }
    if(msg)
	PPCDeleteMessage(msg);
    if(reply_port)
	PPCDeletePort(reply_port);
    if(buf)
	PPCFreeVec(buf);
}

void AGfx::init() {
    if(!buf)
	ptr=stubbuf;
    else if(!msg_sent && msg) {
	// no need to flush caches here, so MSGLENGTH=0
	pipe->done=0;
	ptr=pipe->data;
	pipe->writep=ptr;
	pipe->readp=ptr;
	PPCSendMessage(port,msg,buf,0,MSGID_GFXPIPE);
	msg_sent=true;
    }
}

void AGfx::flush(bool complete) {
    if(msg_sent) {
	pipe->done=1;
	PPCWaitPort(reply_port);
	PPCGetMessage(reply_port);
	msg_sent=false;
    }
    if(!complete)
	init();
}

#else

static const int bufsize=2000;

AGfx::AGfx(void* port1)
    : port(port1),reply_port(NULL),buf(NULL),buf2(NULL),
      msg(NULL),msg_sent(false),in_page_draw(false) {
    buf=(short*)PPCAllocVec(bufsize*2,MEMF_ANY);
    if(!buf) {
	static short b[10];
	ptr=b;
	sz=10;
    } else {
	ptr=buf;
	sz=bufsize;
    }
    if(buf) {
	buf2=(short*)PPCAllocVec(bufsize*2,MEMF_ANY);
	if(reply_port=PPCCreatePort(NULL))
	    msg=PPCCreateMessage(reply_port,bufsize*2);
    }
}

AGfx::~AGfx() {
    if(msg_sent) {
	PPCWaitPort(reply_port);
	PPCGetMessage(reply_port);
    }
    if(msg)
	PPCDeleteMessage(msg);
    if(reply_port)
	PPCDeletePort(reply_port);
    if(buf)
	PPCFreeVec(buf);
    if(buf2)
	PPCFreeVec(buf2);
}

void AGfx::flush(bool complete) {
    if(msg_sent) {
	PPCWaitPort(reply_port);
	PPCGetMessage(reply_port);
	msg_sent=false;
    }
    if(buf) {
	if(ptr!=buf && msg) {
	    //printf("flush\n");
	    if(buf2) {
		short* t=buf;
		buf=buf2;
		buf2=t;
		PPCSendMessage(port,msg,buf2,(ptr-buf2)*2,MSGID_GFX);
		if(complete) {
		    PPCWaitPort(reply_port);
		    PPCGetMessage(reply_port);
		} else
		    msg_sent=true;
	    } else {
		PPCSendMessage(port,msg,buf,(ptr-buf)*2,MSGID_GFX);
		PPCWaitPort(reply_port);
		PPCGetMessage(reply_port);
	    }
	}
	ptr=buf;
	sz=bufsize;
    } else {
	sz=10;
	ptr=buf;
    }
}
#endif

void AGfx::put_image(const Image& i,short x,short y) {
    check_size(5);
    short* p=get_ptr();
    *p++=AGFX_IMAGE;
    *p++=x;
    *p++=y;
    *p++=i.width;
    *p++=i.height;
    set_ptr(p);
    flush(true);
    if(msg) {
	PPCSendMessage(port,msg,i.im,i.height*i.bytes_per_row,MSGID_GFXSYNC);
	PPCWaitPort(reply_port);
	PPCGetMessage(reply_port);
    }
}

void AGfx::get_image(Image& i,short x,short y) {
    check_size(5);
    short* p=get_ptr();
    *p++=AGFX_GETIMAGE;
    *p++=x;
    *p++=y;
    *p++=i.width;
    *p++=i.height;
    set_ptr(p);
    flush(true);
    if(msg) {
	PPCSendMessage(port,msg,i.im,i.height*i.bytes_per_row,MSGID_GFXSYNC);
	PPCWaitPort(reply_port);
	PPCGetMessage(reply_port);
    }
}

void AGfx::put_image(const Image24& i,short x,short y) {
    check_size(5);
    short* p=get_ptr();
    *p++=AGFX_IMAGE24;
    *p++=x;
    *p++=y;
    *p++=i.width;
    *p++=i.height;
    set_ptr(p);
    flush(true);
    if(msg) {
	PPCSendMessage(port,msg,i.im,i.height*i.width*sizeof(Color),MSGID_GFXSYNC);
	PPCWaitPort(reply_port);
	PPCGetMessage(reply_port);
    }
}

void AGfx::get_image(Image24& i,short x,short y) {
    check_size(5);
    short* p=get_ptr();
    *p++=AGFX_GETIMAGE24;
    *p++=x;
    *p++=y;
    *p++=i.width;
    *p++=i.height;
    set_ptr(p);
    flush(true);
    if(msg) {
	PPCSendMessage(port,msg,i.im,i.height*i.width*sizeof(Color),MSGID_GFXSYNC);
	PPCWaitPort(reply_port);
	PPCGetMessage(reply_port);
    }
}

void AGfx::get_colors(ColorTable& t,int n) {
    check_size(2);
    short* p=get_ptr();
    *p++=AGFX_GETCOLORS;
    *p++=short(n);
    set_ptr(p);
    flush(true);
    if(msg) {
	PPCSendMessage(port,msg,t.entries,sizeof(ColorEntry)*n,MSGID_GFXSYNC);
	PPCWaitPort(reply_port);
	PPCGetMessage(reply_port);
    }
}

#else

#include "AGfx.h"

AGfx::Image::Image(short w,short h)
    : width(w),height(h),bytes_per_row(((w+15)>>4)<<4) {
    im=(pixel_color*)gmalloc(bytes_per_row*h);
    if(!im)
	throw("No memory.");
}

AGfx::Image::~Image() {
    gfree(im);
}

AGfx::Image24::Image24(short w,short h)
    : width(w),height(h) {
    im=(Color*)gmalloc(w*h*sizeof(Color));
    if(!im)
	throw("No memory.");
}

AGfx::Image24::~Image24() {
    gfree(im);
}

AGfx::ColorTable::ColorTable(int n) : num(n) {
    entries=(ColorEntry*)gmalloc((n+1)*sizeof(ColorEntry));
    if(!entries)
	throw("No memory.");
}

AGfx::ColorTable::~ColorTable() {
    gfree(entries);
}


void AGfx::polydraw(const Polygon<short>& p) {
    if(!p.empty()) {
	Polygon<short> q;
	short dx=x0;
	short dy=y0;
	if(dx || dy) {
	    q.reserve(p.size());
	    for(Polygon<short>::const_iterator v(p.begin());v!=p.end();++v) {
		q.push_back(Vertex<short>(v->x-dx,v->y-dy));
	    }
	} else
	    q=p;
	Polygon<short>::const_iterator v(q.begin());
	Move(rp,v->x,v->y);
	PolyDraw(rp,q.size()-1,(short*)(v+1));
    }
}

#endif

AGfx::AreaDrawer::~AreaDrawer() {
    gfx.initarea(n);
    for(PArea<short>::const_iterator p(area.begin());p!=area.end();++p)
	gfx.areapoly(*p);
    gfx.areaend();
}

void AGfx::AreaDrawer::add(const Polygon<short>& p) {
    n+=p.size()+1;
#ifndef __PPC__
    short dx=gfx.x0;
    short dy=gfx.y0;
    if(dx || dy) {
	Polygon<short> q;
	q.reserve(p.size());
	for(Polygon<short>::const_iterator v(p.begin());v!=p.end();++v) {
	    q.push_back(Vertex<short>(v->x-dx,v->y-dy));
	}
	area.push_back(q);
    } else
#endif
	area.push_back(p);
}


