#include "virtdisp.h"

struct VirtualDisplay *OpenVirtualDisplay( struct NewVirtualDisplay * );
void CloseVirtualDisplay( struct VirtualDisplay * );
void RefreshVirtualDisplay( struct VirtualDisplay * );
void free_display_mem( struct VirtualDisplay * );
void free_virtual_mem( struct VirtualDisplay * );
void __asm pgcpy( register __d0 cols,
                  register __d1 rows,
                  register __d2 mod,
                  register __a0 UBYTE *src,
                  register __a1 UBYTE *dst );

struct View *OldView = NULL;
extern struct GfxBase *GfxBase;

void free_display_mem( struct VirtualDisplay *vd )
{
	short i;
		
   for( i = 0; i < vd->BitMap.Depth; i++ ) {
		if( vd->BitMap.Planes[i] ) FreeRaster( vd->BitMap.Planes[i], vd->BitMap.BytesPerRow*8, vd->BitMap.Rows );
      vd->BitMap.Planes[i] = NULL;
   }
	if( vd->ColorMap ) {
      FreeColorMap( vd->ColorMap );
      vd->ColorMap = NULL;
   	FreeVPortCopLists( &(vd->ViewPort) );
	   FreeCprList( vd->View.LOFCprList );
	   FreeCprList( vd->View.SHFCprList );
   }
}

void free_virtual_mem( struct VirtualDisplay *vd )
{
   short i;

   for( i = 0; i < vd->VirtualBitMap.Depth; i++ ) {
		if( vd->VirtualBitMap.Planes[i] ) FreeMem( vd->VirtualBitMap.Planes[i], vd->VirtualBitMap.BytesPerRow * vd->VirtualBitMap.Rows );
      vd->VirtualBitMap.Planes[i] = NULL;
   }
}

struct VirtualDisplay *OpenVirtualDisplay( struct NewVirtualDisplay *nvd )
{
   struct VirtualDisplay *vd;
   short i;
   
   vd = AllocMem( sizeof( struct VirtualDisplay ), MEMF_PUBLIC|MEMF_CLEAR );
   if( vd == NULL ) {
      fprintf( stderr, "Can't alloc VirtualDisplay structure\n" );
      return( NULL );
   }
   OldView = GfxBase->ActiView;
   vd->VBXOffset = nvd->VBXOffset;
   vd->VBYOffset = nvd->VBYOffset;
   InitView( &(vd->View) );
   InitVPort( &(vd->ViewPort) );
   vd->View.ViewPort = &(vd->ViewPort);
   vd->View.Modes = nvd->Modes;
   vd->ViewPort.Modes = nvd->Modes;
   InitBitMap( &(vd->BitMap), nvd->Depth, nvd->DWidth, nvd->DHeight );
   InitRastPort( &(vd->RastPort) );
   vd->RastPort.BitMap = &(vd->BitMap);
   vd->RasInfo.BitMap = &(vd->BitMap);
   vd->RasInfo.RxOffset = 0;
   vd->RasInfo.RyOffset = 0;
   vd->RasInfo.Next = NULL;
   vd->ViewPort.DWidth = nvd->DWidth;
   vd->ViewPort.DHeight = nvd->DHeight;
   vd->ViewPort.DxOffset = nvd->DLeft;
   vd->ViewPort.DyOffset = nvd->DTop;
   vd->ViewPort.RasInfo = &(vd->RasInfo);
   vd->ColorMap = GetColorMap( 1<<nvd->Depth );
   if( vd->ColorMap == NULL ) {
      free_display_mem( vd );
      return( NULL );
   }
   vd->ViewPort.ColorMap = vd->ColorMap;
	for( i = 0; i < nvd->Depth; i++ ) 
		if( ( vd->BitMap.Planes[i] = (PLANEPTR)AllocRaster( nvd->DWidth, nvd->DHeight ) ) != NULL )
			BltClear( vd->BitMap.Planes[i], RASSIZE( nvd->DWidth, nvd->DHeight ), 1 );
		else {
			fprintf( stderr, "Can't allocate planes\n" );
			free_display_mem( vd );
			return( NULL );
		}

   /* initialize VirtualDisplay.VirtualBitMap */
   
   InitBitMap( &(vd->VirtualBitMap), nvd->Depth, nvd->VBitMapWidth, nvd->VBitMapHeight );
   for( i = 0; i < nvd->Depth; i++ )
      if( ( vd->VirtualBitMap.Planes[i] = (PLANEPTR)AllocMem( RASSIZE( nvd->VBitMapWidth, nvd->VBitMapHeight), MEMF_PUBLIC|MEMF_CLEAR )) == NULL ) {
         fprintf( stderr, "Can't allocate virtual planes\n" );
         free_display_mem( vd );
         free_virtual_mem( vd );
         return( NULL );
      }
   return( vd );
}

void CloseVirtualDisplay( struct VirtualDisplay *vd )
{
   if( vd ) {
      LoadView( OldView );
      free_display_mem( vd );
      free_virtual_mem( vd );
      FreeMem( vd, sizeof( struct VirtualDisplay ) );
   }
}

#define GETVAL(p,c,b) (  ((p[0][c]>>b)&1)     |  \
                        (((p[1][c]>>b)&1)<<1) |  \
                        (((p[2][c]>>b)&1)<<2) |  \
                        (((p[3][c]>>b)&1)<<3) )

#define GETCODE(p,c,b) ( ((p[4][c]>>b)&1) | \
                        (((p[5][c]>>b)&1)<<1) )
#define RED 2
#define GRN 3
#define BLU 1

void RefreshVirtualDisplay( struct VirtualDisplay *vd )
{
   PLANEPTR *srcplanes, *dstplanes, src, dest;
   short row, col, plane, bit;
   int curcol, yoffset;
   UBYTE buf[2];
   UBYTE fr, fg, fb, ord, into;
   int   currow, numrows, byte, firstbyte;
   int   numbytes, modulo;

   currow = max(vd->currow - vd->VBYOffset, 0);
   numrows = min(vd->BitMap.Rows, currow );
   numbytes = vd->BitMap.BytesPerRow;
   modulo = vd->VirtualBitMap.BytesPerRow - numbytes;

   for( plane = 0; plane < vd->BitMap.Depth; plane++ ) {
      dest = vd->BitMap.Planes[plane];
      src = vd->VirtualBitMap.Planes[plane] + vd->VBXOffset + vd->VBYOffset * vd->VirtualBitMap.BytesPerRow;
      pgcpy( numbytes, numrows, modulo, src, dest ); 
   }

   if( (vd->View.Modes & HAM) && (vd->VBXOffset > 0) ) {
      srcplanes = &(vd->VirtualBitMap.Planes[0]);
      dstplanes = &(vd->BitMap.Planes[0]);
      for( row = 0; row < numrows; row++ ) {
         byte = row * vd->BitMap.BytesPerRow;
         yoffset = (row + vd->VBYOffset) * vd->VirtualBitMap.BytesPerRow;
         firstbyte = vd->VBXOffset+yoffset;
         fr = fg = fb = FALSE;
         ord = 0;
         into = TRUE;
         switch( GETCODE(srcplanes,firstbyte,5) ) {
            case 0:   goto FOUND_ALL;
            case RED: fr = TRUE;
                      break;
            case GRN: fg = TRUE;
                      break;
            case BLU: fb = TRUE;
                      break;
         }
         switch( GETCODE(srcplanes,firstbyte,6) ) {
            case 0:   buf[ord++] = GETVAL(srcplanes,firstbyte,6);
                      goto FOUND_ALL;
            case RED: if( fr ) break;
                      buf[ord++] = GETVAL(srcplanes,firstbyte,6) | CHANGERED;
                      fr = TRUE;
                      break;
            case GRN: if( fg ) break;
                      buf[ord++] = GETVAL(srcplanes,firstbyte,6) | CHANGEGRN;
                      fg = TRUE;
                      break;
            case BLU: if( fb ) break;
                      buf[ord++] = GETVAL(srcplanes,firstbyte,6) | CHANGEBLU;
                      fb = TRUE;
                      break;
         }
         switch( GETCODE(srcplanes,firstbyte,7) ) {
            case 0: buf[ord++] = GETVAL(srcplanes,firstbyte,7);
                    goto FOUND_ALL;
            case RED: if( fr ) break;
                      buf[ord++] = GETVAL(srcplanes,firstbyte,7) | CHANGERED;
                      fr = TRUE;
                      break;
            case GRN: if( fg ) break;
                      buf[ord++] = GETVAL(srcplanes,firstbyte,7) | CHANGEGRN;
                      fg = TRUE;
                      break;
            case BLU: if( fb ) break;
                      buf[ord++] = GETVAL(srcplanes,firstbyte,7) | CHANGEBLU;
                      fb = TRUE;
                      break;
         }
         if( ord == 2 ) goto FOUND_ALL;
         for( col = vd->VBXOffset - 1; col >= 0; col-- ) {
            curcol = col + yoffset;
            for( bit = 0; bit < 8; bit++ ) {
               switch( GETCODE(srcplanes,curcol,bit) ) {
                  case 0:   buf[ord++] = GETVAL(srcplanes,curcol,bit);
                            into = FALSE;
                            goto FOUND_ALL;
                  case RED: if( fr ) break;
                            buf[ord++] = GETVAL(srcplanes,curcol,bit) | CHANGERED;
                            fr = TRUE;
                            into = FALSE;
                  case GRN: if( fg ) break;
                            buf[ord++] = GETVAL(srcplanes,curcol,bit) | CHANGEGRN;
                            fg = TRUE;
                            into = FALSE;
                  case BLU: if( fb ) break;
                            buf[ord++] = GETVAL(srcplanes,curcol,bit) | CHANGEBLU;
                            fb = TRUE;
                            into = FALSE;
               }
               if( ord == 2 ) goto FOUND_ALL;
            }
         }
         FOUND_ALL:
         for( plane = 0; plane < vd->BitMap.Depth; plane++ ) {
            switch( ord ) {
               case 2:
                  dstplanes[plane][ byte ] =
                     (srcplanes[plane][firstbyte] & 0x3F) |
                     ((buf[1]>>plane)&1)<<7 | ((buf[0]>>plane)&1)<<6;
                  break;
               case 1:
                  if( !into ) {
                     dstplanes[plane][ byte ] =
                        (srcplanes[plane][firstbyte] & 0x7F) |
                        ((buf[0]>>plane)&1)<<7;
                  }
                  break;
            }
         }
      }
   }         

   MakeVPort( &(vd->View), &(vd->ViewPort) );
   MrgCop( &(vd->View) );
   LoadView( &(vd->View) );
}
