// Copyright (C) 2004-5 Alexander R. Pruss
//

#include <PalmOS.h>
#include <VFSMgr.h>


typedef struct {
  UInt16 magic;
  UInt32 fileSize;
  UInt16 res1;
  UInt16 res2;
  UInt32 offset;

  UInt32 infoSize;
  Int32  width;
  Int32  height;
  UInt16 planes;
  UInt16 bitsPerPixel;
  UInt32 compression;
  UInt32 imageSize;
  Int32  xRes;
  Int32  yRes;
  UInt32 nColors;
  UInt32 nImpColors;
} BMPHeaderType;

#define BMP_HEADER_LENGTH (14+40)
#define BMP_INFO_LENGTH 40
#define LINES_PER_BUFFER 20

#define SWAP16(x) ( ( (UInt16)(x) << 8 ) | ( (UInt16)(x) >> 8 ) )
#define SWAP32( x ) ( ( ( x ) >> 24 ) | ( ( ( x ) & 0x00FF0000l ) >> 8 ) | ( ( ( x ) & 0x0000FF00l ) << 8 ) |  ( ( x ) << 24 ) )
#define SET32( x, y ) { UInt32 value32 = ( y ); ( h->x ) = SWAP32( value32 ); }
#define SET16( x, y ) { UInt32 value16 = ( y ); ( h->x ) = SWAP16( value16 ); }

static void DumpBMP( Char* fname );

void DAStart( void )
{
   Char fileName[ 80 ];
   DateTimeType dt;
   UInt16 len;

   TimSecondsToDateTime( TimGetSeconds(), &dt );

   DateToAscii( dt.month, dt.day, dt.year, dfYMDLongWithDot, fileName );
   len = StrLen( fileName );
   fileName[ len ] = '-';
   TimeToAscii( dt.hour, dt.minute, tfDot24h, fileName + len + 1 );
   len = StrLen( fileName );
   fileName[ len++ ] = '.';
   fileName[ len++ ] = ( dt.second / 10 ) + '0';
   fileName[ len++ ] = ( dt.second % 10 ) + '0';
   fileName[ len++ ] = '.';
   fileName[ len++ ] = 'b';
   fileName[ len++ ] = 'm';
   fileName[ len++ ] = 'p';
   fileName[ len++ ] = 0;

   DumpBMP( fileName );
}


static void MakeBMPHeader( BMPHeaderType* h, Coord maxX, Coord maxY )
{
   UInt16 rowLen;
   UInt32 imageSize;

   rowLen = maxX * 3 + ( maxX % 2 );

   MemSet( h, BMP_HEADER_LENGTH, 0 );
   h->magic    = 'BM';

   imageSize = rowLen * maxY + BMP_HEADER_LENGTH;
   SET32( fileSize, imageSize + BMP_HEADER_LENGTH );
   SET32( offset, BMP_HEADER_LENGTH );
   SET32( infoSize, BMP_INFO_LENGTH );
   SET32( width, maxX );
   SET32( height, maxY );
   SET16( planes, 1 );
   SET16( bitsPerPixel, 24 );
   SET32( imageSize, imageSize );
   SET32( xRes, 5669 );
   SET32( yRes, 5669 );
}


static void Invert( Coord x, Coord y )
{
   RGBColorType c;

   WinGetPixelRGB( x, y, &c );

   c.r = 255 - c.r;
   c.g = 255 - c.g;
   c.b = 255 - c.b;

   WinSetForeColorRGB( &c, NULL );

   WinDrawPixel( x, y );

   WinGetPixelRGB( x + 1, y, &c );

   c.r = 255 - c.r;
   c.g = 255 - c.g;
   c.b = 255 - c.b;

   WinSetForeColorRGB( &c, NULL );

   WinDrawPixel( x + 1, y );
}



static void GetBMPRow( UInt8* row, Coord x0, Coord y0, Coord width, Boolean emit )
{
   Coord x;

   if ( emit )
       for ( x = x0 ; x < x0 + width ; x++ ) {
           RGBColorType c;
           WinGetPixelRGB( x, y0, &c );
           *row++ = c.b;
           *row++ = c.g;
           *row++ = c.r;
       }

   Invert( x0, y0 );
}



static UInt16 GetVolRef( void )
{
   UInt16 volRefNum;
   UInt16 firstVolRefNum = 0xFFFF;
   UInt32 volIterator;

   volIterator = vfsIteratorStart;

   while ( volIterator != vfsIteratorStop ) {
       Err err;
       VolumeInfoType info;

       err = VFSVolumeEnumerate( &volRefNum, &volIterator );

       if ( err != errNone )
           return firstVolRefNum;

       if ( firstVolRefNum == 0xFFFF )
           firstVolRefNum = volRefNum;

       if ( errNone == VFSVolumeInfo( volRefNum, &info ) &&
            ( info.mediaType == expMediaType_MemoryStick ||
              info.mediaType == 0x49736472 ) ) {
           return volRefNum;
       }
   }

   return firstVolRefNum;
}

static void DumpBMP( Char* fname )
{
   Coord y;
   void*     buf;
   UInt16    bufLen;
   UInt16    lineLen;
   FileRef      ref;
   UInt16    volRef;
   UInt16    linesInBuffer;
   RectangleType viewRect144;
   WinHandle oldW;

   volRef = GetVolRef();

   if ( volRef == 0xFFFF )
       return;

   oldW = WinSetDrawWindow( WinGetDisplayWindow() );

   WinPushDrawState();
   WinSetCoordinateSystem( 144 );

   viewRect144.topLeft.x = 0;
   viewRect144.topLeft.y = 0;
   WinGetDisplayExtent( &viewRect144.extent.x, &viewRect144.extent.y );

   lineLen = 3 * viewRect144.extent.x;
   while ( lineLen % 4 )
       lineLen++;
   bufLen = lineLen * LINES_PER_BUFFER;
   if ( bufLen < BMP_HEADER_LENGTH )
       bufLen = BMP_HEADER_LENGTH;
   buf = MemPtrNew( bufLen );

   if ( buf == NULL ||
        errNone != VFSFileCreate( volRef, fname ) ||
        errNone != VFSFileOpen( volRef, fname, vfsModeWrite, &ref ) ) {
       if ( buf != NULL )
           MemPtrFree( buf );
       WinPopDrawState();
       return;
   }

   MakeBMPHeader( buf, viewRect144.extent.x, viewRect144.extent.y );
   VFSFileWrite( ref, BMP_HEADER_LENGTH, buf, NULL );
   MemSet( buf, bufLen, 0 );

   linesInBuffer = 0;

   for ( y = viewRect144.topLeft.y + viewRect144.extent.y - 1 ; y >= viewRect144.topLeft.y ; y-- ) {
        GetBMPRow( buf + lineLen * linesInBuffer, viewRect144.topLeft.x, y, viewRect144.extent.x, true );
        linesInBuffer++;

        if ( LINES_PER_BUFFER <= linesInBuffer ) {
            VFSFileWrite( ref, lineLen * linesInBuffer, buf, NULL );
            linesInBuffer = 0;
        }
   }
   if ( 0 < linesInBuffer )
       VFSFileWrite( ref, lineLen * linesInBuffer, buf, NULL );
   VFSFileClose( ref );
   for ( y = viewRect144.topLeft.y ; y < viewRect144.topLeft.y + viewRect144.extent.y  ; y++ ) {
        GetBMPRow( buf, viewRect144.topLeft.x, y, viewRect144.extent.x, true );
   }

   WinPopDrawState();

   WinSetDrawWindow( oldW );
   MemPtrFree( buf );
}