// ----------------------------------------------------------------------------------------
//
//  glcdimageprep.cpp
//  Copyright (C) 2006  Robert Cook
//    
//      This program is free software; you can redistribute it and/or modify it
//      under the terms of the GNU General Public License as published by the
//      Free Software Foundation; either version 2 of the License, or (at your
//      option) any later version.
//    
//      This program is distributed in the hope that it will be useful, but 
//      WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
//      or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License 
//      for more details.
//    
//      You should have received a copy of the GNU General Public License along
//      with this program; if not, write to the Free Software Foundation, Inc., 
//      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
//  This file implements the Graphic LCD Image Preparation logic
//
// ----------------------------------------------------------------------------------------


#include <string>
#include <fstream>
#include <iostream>
#include <vector>

#include <windows.h>

#include "gnu/getopt.h"


// ----------------------------------------------------------------------------------------

// ----------------------------------------------------------------------------------------

// ----------------------------------------------------------------------------------------

// ----------------------------------------------------------------------------------------

namespace
{
    // ------------------------------------------------------------------------------------

    void showHelp( const char* pgm_name )
    {
        std::cerr << std::endl
            << pgm_name << " [-n name] bmpfile outputfile" << std::endl
            << "    -n : C/C++ compatible data name" << std::endl
            << std::endl;
    }

    // ------------------------------------------------------------------------------------

    void writeOutputHeader
        (
            std::ofstream& outFH,
            const char*    imageFN,
            unsigned       imageWidth,
            unsigned       imageHeight,
            const char*    dataName
        )
    {
        int widthBytes = ( imageWidth + 7 ) / 8;

        outFH << "// converted " << imageFN << std::endl;
        outFH << "//" << std::endl;
        outFH << std::endl;
        outFH << "const uint8_t " << dataName << "_w = " << imageWidth << ";" << std::endl;
        outFH << "const uint8_t " << dataName << "_h = " << imageHeight << ";" << std::endl;
        outFH << std::endl;
        outFH << "static const prog_uint8_t " << dataName << "_pix[] =" << std::endl;
        outFH << "{" << std::endl;
    }

    // ------------------------------------------------------------------------------------

    void writeOutputImageRow
        (
            std::ofstream& outFH,
            const char*    dataPtr,
            unsigned       byteCount
        )
    {
        outFH << "    ";

        while ( byteCount-- )
        {
            static const char hexChars[] = "0123456789abcdef";

            outFH << "0x";
            outFH << hexChars[ (*dataPtr >> 4) & 0x0f ];
            outFH << hexChars[ *dataPtr & 0x0f ];
            outFH << ", ";
            dataPtr++;
        }

        outFH << std::endl;
    }

    // ------------------------------------------------------------------------------------

    void writeOutputFooter( std::ofstream& outFH )
    {
        outFH << "};" << std::endl;
        outFH << std::endl;
    }
}


// ----------------------------------------------------------------------------------------

int main( int argc, const char* argv[])
{
    const char* image_data_name = "glcdimage";

    int option;

    while ( ( option = getopt( argc, argv, "n:h" ) ) != EOF )
    {
        switch ( option )
        {
            case 'n': // data name
                image_data_name = optarg;
                break;

            case 'h': // help
                showHelp( argv[ 0 ] );
                return 0;
        }
    }

    if ( argc - optind < 2 )
    {
        showHelp( argv[ 0 ] );
        return -1;
    }

    // get the filenames of the input and output files

    const char* input_image_fn = argv[ optind ];
    const char* output_file_fn = argv[ optind + 1 ];

    std::ifstream inFH( input_image_fn );

    if ( ! inFH.is_open() )
    {
        std::cerr << "cannot open image file \"" << input_image_fn << "\"" << std::endl;
        return -1;
    }

    // validate and read the input file

    BITMAPFILEHEADER fileHeader;
    BITMAPINFOHEADER infoHeader;

    inFH.read( reinterpret_cast< char* >( &fileHeader ), sizeof( fileHeader ) );

    if ( fileHeader.bfType != 0x4D42 )
    {
        std::cerr << "error: image file \"" << input_image_fn << "\" isn't a BMP?" << std::endl;
        return -1;
    }

    inFH.read( reinterpret_cast< char* >( &infoHeader ), sizeof( infoHeader ) );

    if ( ( infoHeader.biPlanes != 1 ) || ( infoHeader.biBitCount != 1 ) )
    {
        std::cerr << "error: image file \"" << input_image_fn << "\" must be 1-bit" << std::endl;
        return -1;
    }

    if ( ( infoHeader.biWidth > 128 ) || ( infoHeader.biHeight > 64 ) )
    {
        std::cerr << "error: image file \"" << input_image_fn << "\" is too large; "
            << "max size: 128 x 64, this: " << infoHeader.biWidth << " x " 
            << infoHeader.biHeight << std::endl;
        return -1;
    }

    int widthBytes = ( infoHeader.biWidth + 7 ) / 8;

    std::vector< char > pixelData;
    pixelData.resize( widthBytes * infoHeader.biHeight );

    inFH.seekg( fileHeader.bfOffBits );
    inFH.read( &pixelData[ 0 ], widthBytes * infoHeader.biHeight );
    
    for ( int i = 0; i < infoHeader.biHeight; i++ )
    {
        for ( int j = 0; j < widthBytes; j++ )
        {
            pixelData[ ( i * widthBytes ) + j ] ^= 0xff;
        }
    }

    // construct the output file

    std::ofstream outFH( output_file_fn );

    if ( ! outFH.is_open() )
    {
        std::cerr << "cannot open output file \"" << output_file_fn << "\"" << std::endl;
        return -1;
    }

    ::writeOutputHeader( outFH,
                         input_image_fn,
                         infoHeader.biWidth, 
                         infoHeader.biHeight, 
                         image_data_name );

    // write the pixel data; the rows are vertically flipped

    for ( int i = infoHeader.biHeight - 1; i >= 0; i-- )
    {
        ::writeOutputImageRow( outFH, &pixelData[ widthBytes * i ], widthBytes );
    }

    // finish the output file

    ::writeOutputFooter( outFH );
    outFH.close();

    return 0;
}


// ----------------------------------------------------------------------------------------
//  end of glcdimageprep.cpp
// ----------------------------------------------------------------------------------------

