/*
**      $VER: graphics3D2d.c 10.01 (01.11.97)
**
**      External functions for graphics3D.library
**	
**      (C) Copyright 97 Patrizio Biancalani
**      All Rights Reserved.
*/

#include <exec/types.h>
#include <exec/memory.h>
#include <proto/exec.h>
#include <proto/intuition.h>
#include <intuition/intuition.h>
#include <intuition/screens.h>

#include <graphics/rastport.h>
#include <graphics/clip.h>
#include <graphics/regions.h>
#include <graphics/gfx.h>
#include <graphics/gfxmacros.h>
#include <graphics/layers.h>

#include "graphics3Dc.h"
#include "graphics3D.h"
#include "graphics3D2d_proto.h"

 /* Please note, that &Graphics3DBase always resides in register __a6 
    as well, but if we don't need it, we need not reference it here.

    Also note, that registers a0, a1, d0, d1 always are scratch registers,
    so you usually should only *pass* parameters there, but make a copy
    directly after entering the function. To avoid problems of kind
    "implementation defined behaviour", you should make a copy of A6 too,
    when it is actually used.

    In this example case, scratch register saving would not have been 
    necessary (since there are no other function calls inbetween), but we 
    did it nevertheless.
  */

/************ prototipi solo locali ******/
struct RastPort *InitNBuff(struct grafica *graf,struct Screen *screen,
       struct Window *window);

struct BitMap *getBitMap(int wide, int high, int deep, short clear);

void FreeNBuff(struct grafica *graf,
	struct Screen *screen, struct RastPort *rp);

void freeBitMap(struct BitMap *bm);

/************ macro solo locali *******/
#define SMARG 8

/**********************************************************/

/****************************************************
 ** Routin per la gestione della grafica, in stile **
 ** 2.0                                            ** 
 ** (c) 1994 BIANCA HARD&SOFT Vers:1.00            **
 ****************************************************/

/********* FUNZIONI 2D ********************************/

/***********************************
 ** INIZIALIZZO STRUTTURE PER USO **
 ** CON ROUTIN DI AREA FILL       **
 ***********************************
 **** INPUT :			  **
 ** win -> puntatore a finestra   **
 **        su cui lavorare.       **
 ** mxv -> n# massimo vertici da  **
 **        usare.		  **
 **** OUTPUT :                    **
 ** ris >0  - tutto ok.           **
 ** ris =<0 - errore, ini.fallita.**
 ***********************************/
struct grafica *ini_g(REG(a0)struct Window *win,REG(d0)long int mxv)
{
struct grafica *ris;
struct Screen *scr;
struct RastPort *rw;
struct RastPort *r;
struct Layer *la;
long int i;

ris=(struct grafica *)AllocMem(sizeof(Sgrafica),NULL);
if (ris==NULL) return (0);

ris->vpor=0;
ris->rast=0;
ris->rast1=0;
ris->rast2=0;
ris->fdouble=1;
ris->b_af=0;
ris->pras=0;

/** default si single buffer **/
ris->clipx=0;
ris->clipy=0;
ris->clipdx=0;
ris->clipdy=0;
ris->NB_rinfo=0;       
ris->NB_bmap=0;        
ris->NB_layerinfo=0;   
ris->NB_layer=0;       

/* se per buffer richiesto meno di 1 vertice e' meglio evitare */
if (mxv<=1) {
	close_g(ris);
	return(-5);
	}
/* faccio in modo di allineare alle words la dimensione dell'area */
i=mxv;
if (((mxv>>1)<<1)!=mxv) i++;
ris->b_af=AllocMem(MAXVER(i),NULL);
if (ris->b_af==NULL) {
	close_g(ris);
	return(-1);	
	}
ris->lb_af=i;
		
rw=win->RPort;
scr=win->WScreen;
ris->vpor=&(scr->ViewPort);
la=win->WLayer;
ris->wind=win;
ris->larg=win->Width+SMARG;
ris->alte=win->Height+SMARG;

r=InitNBuff(ris,scr,win);
if ((long int)r==NULL) {
	close_g(ris);
	return(-4);
	}
ris->rast1=rw;
ris->rast2=r;
ris->rast=r;
ris->o_ai=r->AreaInfo;
ris->o_tr=r->TmpRas;
/*
ris->rast1=rw;
ris->rast2=rw;
ris->rast=rw;
ris->o_ai=rw->AreaInfo;
ris->o_tr=rw->TmpRas;
*/

InitArea(&ris->n_ai,ris->b_af,mxv);
r->AreaInfo=&ris->n_ai;

/*
InitArea(&ris->n_ai,ris->b_af,mxv);
rw->AreaInfo=&ris->n_ai;
*/

ris->pras=(char *)AllocRaster(ris->larg,ris->alte);
if (ris->pras==NULL) {
	close_g(ris);
	return (-4);
	}

InitTmpRas(&ris->n_tr,ris->pras,RASSIZE(ris->larg,ris->alte));

r->TmpRas=&ris->n_tr;

/*
rw->TmpRas=&ris->n_tr;
*/

return(ris);
}

/***********************************
 ** CHIUDO TUTTE LE STRUTTURE     **
 ** APERTE CON LA FUNZIONE PRECE- **
 ** DENTE.                        **
 ***********************************
 **** INPUT :                     **
 ** graf -> valore >0 ritornato   **
 **         dalla funzione d'ini- **
 **         zializzazione         **
 ***********************************/
void close_g(REG(a0)struct grafica *graf)
{
struct RastPort *r;
struct Layer *la;
long int c;

if ((long int)graf > NULL)
	{
	la=graf->wind->WLayer;
	if (graf->fdouble!=NULL) la=graf->NB_layer;
	if (la->ClipRegion!=NULL)
		{
		c=InstallClipRegion(la,0);
		DisposeRegion(c);
		}
	r=graf->rast;
	if ((long int)r > NULL)
		{
		r->AreaInfo=graf->o_ai;
		r->TmpRas=graf->o_tr;
		}
	if (graf->b_af!=NULL) FreeMem(graf->b_af,MAXVER(graf->lb_af));
	if (graf->pras!=NULL) FreeRaster(graf->pras,graf->larg,graf->alte);
	if (graf->fdouble!=NULL) FreeNBuff(graf,graf->wind->WScreen,
				 graf->rast2);
	FreeMem(graf,sizeof(Sgrafica));
	}
}

/************************************
 ** FUNZIONE PER VISUALIZZARE LA   **
 ** RASTPORT NASCOSTA SULLA        **
 ** FINESTRA.			   **
 ************************************
 **** INPUT :			   **
 ** graf -> valore >0 ritornato    **
 **         dalla funzione d'ini-  **
 **         zializzazione.         **
 **** OUTPUT:			   **
 ************************************/
void switch_rp(graf)
REG(a0)struct grafica *graf;
{

ClipBlit(graf->rast2,graf->clipx,graf->clipy,graf->rast1,graf->clipx,
	graf->clipy,graf->clipdx,graf->clipdy,0xc0);

/** 
con questa e' un po' piu' rapido ma puo' risultare piu' instabile 
(forse)
**/
/*
BltBitMap(graf->rast2->BitMap,graf->clipx,graf->clipy,graf->rast1->BitMap,
	  graf->wind->LeftEdge+graf->clipx,graf->wind->TopEdge+graf->clipy,
	  graf->clipdx,graf->clipdy,0xC0,0xFF,0);
*/

}

/************************************
 ** FUNZIONE PER DEFINIRE UN BOX   **
 ** DI CLIP SULLA FINESTRA         **
 ************************************
 **** INPUT :			   **
 ** graf -> valore >0 ritornato    **
 **         dalla funzione d'ini-  **
 **         zializzazione.         **
 ** minx - valore minimo x box.    **
 ** miny - valore minimo y box.    **
 ** dx - larghezza box.		   **
 ** dy - altezza box.		   **
 **** OUTPUT:			   **
 ** > 0 tutto ok.		   ** 
 ** = 0 errore.			   **
 **** NOTA:			   **
 ** elimina eventuali clip region  **
 ** preesistenti.   		   **
 ************************************/
long int clipbox(REG(a0)struct grafica *graf,REG(d0)long int minx,
           REG(d1)long int miny,REG(d2)long int dx,REG(d3)long int dy)
{
struct Layer *la;
struct Region *clipr;
struct Rectangle rect;
struct ClipRect *clrt;
long int esi;
long int i;

graf->clipx=minx;
graf->clipy=miny;
graf->clipdx=dx;
graf->clipdy=dy;

la=graf->NB_layer;

/*
if (minx==0 AND miny==0 AND dx==0 AND dy==0)
	{
	if (graf->fdouble!=NULL)
		{
		}
	else
		{
		clipr=(struct Region *)InstallClipRegion(la,NULL);
		if (clipr!=NULL) DisposeRegion(clipr);
		return (0);
		}
	}
if (graf->fdouble==NULL)
	{
	wn=graf->wind;
	la=wn->WLayer;
*/
la=graf->wind->WLayer;
if(graf->fdouble!=NULL) la=graf->NB_layer;

/** elimino eventuali clipregion preesistenti **/
clipr=(struct Region *)InstallClipRegion(la,NULL);
if (clipr!=NULL) DisposeRegion(clipr);

/** inizializzo una nuova clip region vuota **/
clipr=(struct Region *)NewRegion();
if (clipr==NULL) return (0);

rect.MinX=0;
rect.MinY=0;
rect.MaxX=dx;
rect.MaxY=dy;

OrRectRegion(clipr,&rect);
clipr=(struct Region *)InstallClipRegion(la,clipr);
if (clipr==NULL) return(0);
	
return (1);
}

/************************************
 ** FUNZIONE PER VISUALIZZARE UN   **
 ** POLIGONO MA SOLO NEI CONTORNI. **
 ************************************
 **** INPUT :			   **
 ** graf -> valore >0 ritornato da **
 **         ini_g().               **
 ** nvert-> n# di vertici poligono.**
 ** pvert-> puntatore ad array di  **
 **         int con elenco vertici **
 **** OUTPUT:			   **
 ** nessuno.			   **
 ************************************/
void polw(REG(a0)struct grafica *graf,REG(d0)long int nvert,
		REG(a1)short int *pvert)
{
if (((long int)graf<=NULL) OR ((long int)(graf->rast)<=NULL)) return(0); 

Move(graf->rast,pvert[0],pvert[1]);
PolyDraw(graf->rast,nvert+1,pvert);
}


/************************************
 ** FUNZIONE PER VISUALIZZARE POL. **
 ** PIENO			   **
 ************************************
 **** INPUT :			   **
 ** graf -> valore >0 ritornato da **
 **         ini_g().               **
 ** nvert-> n# di vertici poligono.**
 ** pvert-> puntatore ad array di  **
 **         int con elenco vertici **
 **** OUTPUT:			   **
 ** nessuno.			   **
 ************************************/
void polf(REG(a0)struct grafica *graf,REG(d0)long int nvert,
		REG(a1)short int *pvert)
{
long int i,np;

if (((long int)graf<=NULL) OR ((long int)(graf->rast)<=NULL)) return(0); 

np=nvert<<1;
AreaMove(graf->rast,pvert[0],pvert[1]);

for (i=2 ;i<np ;i=i+2) AreaDraw(graf->rast,pvert[i],pvert[i+1]);

AreaEnd(graf->rast);

}

/************************************
 ** FUNZIONE PER VISUALIZZARE POL. **
 ** PIENO CON BORDO		   **
 ************************************
 **** INPUT :			   **
 ** graf -> valore >0 ritornato da **
 **         ini_g().               **
 ** nvert-> n# di vertici poligono.**
 ** pvert-> puntatore ad array di  **
 **         int con elenco vertici **
 ** colb -> colore da assegnare al **
 **         bordo.		   **
 **** OUTPUT:			   **
 ** nessuno.			   **
 ************************************/
void polwf(REG(a0)struct grafica *graf,REG(d0)long int nvert,
	REG(a1)short int *pvert,REG(d1)long int colb)
{
long int i,np,x0,y0;

if (((long int)graf<=NULL) OR ((long int)(graf->rast)<=NULL)) return(0); 

x0=pvert[0];
y0=pvert[1];

SetOutlinePen(graf->rast,colb);

np=nvert<<1;
AreaMove(graf->rast,x0,y0);

for(i=2; i<np; i=i+2) AreaDraw(graf->rast,pvert[i],pvert[i+1]);

AreaEnd(graf->rast);

}

/*************************************
 ** FUNZIONE PER CANCELLARE UN BOX  **
 ** NELLA FINESTRA .		    **
 *************************************
 **** INPUT :                       **
 ** graf -> valore >0 ritornato da  **
 **         ini_g().                **
 ** x0   -> coord. x punto in alto  **
 **         a sinistra box.         **
 ** y0   -> coord. y punto in alto  **
 **         a sinistra box.         **
 ** x1   -> coord. x punto in basso **
 **         a destra box.	    **
 ** y1   -> coord. y punto in basso **
 **	    a destra box.	    **
 **** NOTA :			    **
 ** usa il colore dello sfondo, e   **
 ** non influenza le altre funzioni **
 *************************************/
void cls_b(REG(a0)struct grafica *graf,REG(d0)long int x0,
	REG(d1)long int y0,REG(d2)long int x1,REG(d3)long int y1)
{
if (((long int)graf<=NULL) OR ((long int)(graf->rast)<=NULL)) return(0); 

EraseRect(graf->rast,x0,y0,x1,y1);
}

/*************************************
 ** FUNZIONE PER CAMBIARE IL MODO   **
 ** VIDEO DI TRACCIAMENTO.          **
 *************************************
 **** INPUT :                       **
 ** graf -> valore >0 ritornato da  **
 **         ini_g().                **
 ** mod  -> nuovo modo video.       **
 **** NOTA :			    **
 ** valori per mod :		    **
 ** 0 > JAM1 (over 2)		    **
 ** 1 > JAM2 (over 0) (def.)	    **
 ** 2 > COMPLEMENT (over 1) 	    **
 ** 4 > INVERSVID  (inverse 1)      **
 *************************************/
void over(REG(a0)struct grafica *graf,REG(d0)long int mod)
{
if (((long int)graf<=NULL) OR ((long int)(graf->rast)<=NULL)) return(0); 

SetDrMd(graf->rast,mod);
}

/********* ROUTIN INTERNE PER PSEUDO DOUBLE BUFFERING ***************/
/*********************************************
 ** INIZIALIZZO NUOVA RASTPORT PER USARLA   **
 ** COME AREA DI RENDERING NASCOSTA.	    **
 *********************************************
 **** INPUT :				    **
 **** OUTPUT:				    **
 ** se > 0 allora puntatore a nuova rastport**
 *********************************************
 ** nota: inizializzo una bitmap uguale     **
 ** alla finestra usata per visualizzare la **
 ** scena effettivamente.	            **
 *********************************************/
struct RastPort *InitNBuff(struct grafica *graf,struct Screen *screen,
          struct Window *window)
{
struct RastPort *rport = NULL;  
struct Window *win;
short int err = 0;
unsigned char depth;

win=graf->wind;
depth=screen->BitMap.Depth;

if (!(graf->NB_layerinfo = (struct Layer_Info *)NewLayerInfo())) 
	return (NULL);
/*
if (!(graf->NB_bmap = getBitMap(screen->Width, screen->Height, depth, 1)))
   goto NBInit_done;
*/
if (!(graf->NB_bmap =(struct BitMap *)AllocBitMap(win->Width+SMARG,
	win->Height+SMARG,depth,BMF_CLEAR|BMF_DISPLAYABLE,
	graf->rast1->BitMap)))
   goto NBInit_done;

if (!(graf->NB_layer = (struct Layer *)CreateBehindLayer(graf->NB_layerinfo,
		graf->NB_bmap,0L, 0L, window->Width-1, window->Height-1,
		LAYERSIMPLE | LAYERBACKDROP, NULL))) {
   err = 4;
   goto NBInit_done;
   }
rport = graf->NB_layer->rp;

SetRast(rport, 0);   

NBInit_done:
if (err) FreeNBuff(graf,screen, rport);
return (rport);
}

/*
non piu' usata, non eliminata del tutto per non dover cambiare troppi moduli
*/
struct BitMap *getBitMap(long int wide,long int high,long int deep,
		short int clear)
{
/*
short int i;
struct BitMap *bitmap;
if (!(bitmap = (struct BitMap *)
               AllocMem(sizeof(struct BitMap), MEMF_PUBLIC|MEMF_CLEAR)))
   goto bitmaperr;
InitBitMap(bitmap, deep, wide, high);
for (i = 0; i < deep; i++) {
   if (!(bitmap->Planes[i] = (PLANEPTR)AllocRaster(wide, high)))
      goto bitmaperr;
   if (clear) BltClear(bitmap->Planes[i], RASSIZE(wide, high), 0);
   }

return (bitmap);

bitmaperr:
freeBitMap(bitmap);
*/
return (NULL);
}

/*
non piu' usata, non eliminata del tutto per non dover cambiare troppi moduli
*/
void freeBitMap(struct BitMap *bm)
{
short int j;
/*
if (bm) {
   for (j = 0; j < bm->Depth; j++)
      if (bm->Planes[j])
         FreeRaster(bm->Planes[j], bm->BytesPerRow << 3, bm->Rows);
   FreeMem(bm, sizeof(struct BitMap));
   }
*/
}


void FreeNBuff(struct grafica *graf,struct Screen *screen, 
	struct RastPort *rp)
{
unsigned char depth;

depth=screen->BitMap.Depth;

if (graf->NB_layer) DeleteLayer(0L, graf->NB_layer);
if (graf->NB_layerinfo) DisposeLayerInfo(graf->NB_layerinfo);

/*
if (graf->NB_bmap) freeBitMap(graf->NB_bmap);  
*/
if (graf->NB_bmap) 
	{
	WaitBlit();
	FreeBitMap(graf->NB_bmap);  
	}
}

/***********************************************************/

