// 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 );
}

