#include "stdafx.h"

#include "FITSHeader.h"

void IoAFITSHeader::Set( const char *FieldName, int Value )
{
         if ( ::strncmp( FieldName, "BITPIX  ", 8 ) == 0 ) m_BITPIX   = Value;
    else if ( ::strncmp( FieldName, "NAXIS   ", 8 ) == 0 ) m_NAXIS    = Value;
    else if ( ::strncmp( FieldName, "NAXIS1  ", 8 ) == 0 ) m_NAXIS1   = Value;
    else if ( ::strncmp( FieldName, "NAXIS2  ", 8 ) == 0 ) m_NAXIS2   = Value;
    else if ( ::strncmp( FieldName, "EXTEND  ", 8 ) == 0 ) m_EXTEND   = Value;
    else if ( ::strncmp( FieldName, "CAMERAID", 8 ) == 0 ) m_CAMERAID = Value;
    else if ( ::strncmp( FieldName, "FRAMENUM", 8 ) == 0 ) m_FRAMENUM = Value;
    else if ( ::strncmp( FieldName, "FRAMETIM", 8 ) == 0 ) m_FRAMETIM = Value;
    else if ( ::strncmp( FieldName, "LC_FORMT", 8 ) == 0 ) m_LC_FORMT = Value;
    else if ( ::strncmp( FieldName, "LC_CBITS", 8 ) == 0 ) m_LC_CBITS = Value;
    else if ( ::strncmp( FieldName, "LC_UBITS", 8 ) == 0 ) m_LC_UBITS = Value;
    else if ( ::strncmp( FieldName, "LC_OFFST", 8 ) == 0 ) m_LC_OFFST = Value;
    else if ( ::strncmp( FieldName, "LCL_ZERO", 8 ) == 0 ) m_LCL_ZERO = Value;
    else if ( ::strncmp( FieldName, "CPUCLKHI", 8 ) == 0 ) m_CPUCLKHI = Value;
    else if ( ::strncmp( FieldName, "CPUCLKLO", 8 ) == 0 ) m_CPUCLKLO = Value;
    else if ( ::strncmp( FieldName, "DMABUFID", 8 ) == 0 ) m_DMABUFID = Value;
}

int IoAFITSHeader::Encode( char *FITSHeader, IoAImageFileFormat::ImageFileFormat FileFormat )
{
    int FITSHeaderBytes;

    switch ( FileFormat )
    {
        case IoAImageFileFormat::FITSFormat:
        {
            char FITSHeaderString[ ] =
            {
                "SIMPLE  =                    T / file does conform to FITS standard             "
                "BITPIX  =                nnnnn / number of bits per data pixel = 16             "
                "NAXIS   =                nnnnn / number of data axes = 2                        "
                "NAXIS1  =                nnnnn / length of data axis 1                          "
                "NAXIS2  =                nnnnn / length of data axis 2                          "
                "EXTEND  =                    T / FITS dataset may contain extensions            "
                "COMMENT   FITS (Flexible Image Transport System) format is defined in 'Astronomy"
                "COMMENT   and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H "
                "BZERO   =              32768.0 / offset data range to that of unsigned short    "
                "BSCALE  =                  1.0 / default scaling factor                         "
                "CAMERAID=                   nn / camera id  returned by Lucky camera            "
                "FRAMENUM=                nnnnn / frame number returned by Lucky camera          "
                "FRAMETIM=           uuuuuuuuuu / timestamp gettickcount( )                      "
                "CPUCLKHI=           uuuuuuuuuu / CPU clock count (high-order 32 bits)           "
                "CPUCLKLO=           uuuuuuuuuu / CPU clock count (low-order 32 bits)            "
                "DMABUFID=                nnnnn / DMA buffer ID                                  "
                "END                                                                             "
            };

            // Determine size of header
            FITSHeaderBytes = ::strlen( FITSHeader );

            // Copy header string
            ::CopyMemory( FITSHeader, FITSHeaderString, FITSHeaderBytes );

            // Fill in variable parameters, restoring terminating null bytes to blanks
            FITSHeader[  1 * 80 + 25 + ::sprintf( FITSHeader +  1 * 80 + 25, "%5i",  m_BITPIX   ) ] = ' ';
            FITSHeader[  2 * 80 + 25 + ::sprintf( FITSHeader +  2 * 80 + 25, "%5i",  m_NAXIS    ) ] = ' ';
            FITSHeader[  3 * 80 + 25 + ::sprintf( FITSHeader +  3 * 80 + 25, "%5i",  m_NAXIS1   ) ] = ' ';
            FITSHeader[  4 * 80 + 25 + ::sprintf( FITSHeader +  4 * 80 + 25, "%5i",  m_NAXIS2   ) ] = ' ';
            FITSHeader[ 10 * 80 + 28 + ::sprintf( FITSHeader + 10 * 80 + 28, "%5i",  m_CAMERAID ) ] = ' ';
            FITSHeader[ 11 * 80 + 25 + ::sprintf( FITSHeader + 11 * 80 + 25, "%5i",  m_FRAMENUM ) ] = ' ';
            FITSHeader[ 12 * 80 + 20 + ::sprintf( FITSHeader + 12 * 80 + 20, "%10u", m_FRAMETIM ) ] = ' ';
            FITSHeader[ 13 * 80 + 25 + ::sprintf( FITSHeader + 13 * 80 + 25, "%10u", m_CPUCLKHI ) ] = ' ';
            FITSHeader[ 14 * 80 + 25 + ::sprintf( FITSHeader + 14 * 80 + 25, "%10u", m_CPUCLKLO ) ] = ' ';
            FITSHeader[ 15 * 80 + 20 + ::sprintf( FITSHeader + 15 * 80 + 20, "%5i",  m_DMABUFID ) ] = ' ';
        } break;

        case IoAImageFileFormat::LuckyFormat:
        {
            char FITSHeaderString[ ] =
            {
                "SIMPLE  =                    T / file does conform to FITS standard             "
                "BITPIX  =                nnnnn / number of bits per data pixel = 16             "
                "NAXIS   =                nnnnn / number of data axes = 2                        "
                "NAXIS1  =                nnnnn / length of data axis 1                          "
                "NAXIS2  =                nnnnn / length of data axis 2                          "
                "EXTEND  =                    T / FITS dataset may contain extensions            "
                "COMMENT   FITS (Flexible Image Transport System) format is defined in 'Astronomy"
                "COMMENT   and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H "
                "BZERO   =              32768.0 / offset data range to that of unsigned short    "
                "BSCALE  =                  1.0 / default scaling factor                         "
                "CAMERAID=                nnnnn / camera id  returned by Lucky camera            "
                "FRAMENUM=                nnnnn / frame number returned by Lucky camera          "
                "FRAMETIM=           nnnnnnnnnn / timestamp gettickcount( )                      "
                "LC_FORMT=                   nn / format, 1 = compressed, 2 = compressed + LUT   "
                "LC_CBITS=                   nn / number of bits per compressed value            "
                "LC_UBITS=                   nn / number of bits per uncompressed value          "
                "LC_OFFST=                nnnnn / level offset for compressed value              "
                "CPUCLKHI=           nnnnnnnnnn / CPU clock count (high-order 32 bits)           "
                "CPUCLKLO=           nnnnnnnnnn / CPU clock count (low-order 32 bits)            "
                "DMABUFID=                nnnnn / DMA buffer ID                                  "
                "END                                                                             "
            };

            // Determine size of header
            FITSHeaderBytes = ::strlen( FITSHeaderString );

            // Copy header string
            ::CopyMemory( FITSHeader, FITSHeaderString, FITSHeaderBytes );

            // Fill in variable parameters, restoring terminating null bytes to blanks
            FITSHeader[  1 * 80 + 25 + ::sprintf( FITSHeader +  1 * 80 + 25, "%5i",  m_BITPIX   ) ] = ' ';
            FITSHeader[  2 * 80 + 25 + ::sprintf( FITSHeader +  2 * 80 + 25, "%5i",  m_NAXIS    ) ] = ' ';
            FITSHeader[  3 * 80 + 25 + ::sprintf( FITSHeader +  3 * 80 + 25, "%5i",  m_NAXIS1   ) ] = ' ';
            FITSHeader[  4 * 80 + 25 + ::sprintf( FITSHeader +  4 * 80 + 25, "%5i",  m_NAXIS2   ) ] = ' ';
            FITSHeader[ 10 * 80 + 28 + ::sprintf( FITSHeader + 10 * 80 + 28, "%2i",  m_CAMERAID ) ] = ' ';
            FITSHeader[ 11 * 80 + 25 + ::sprintf( FITSHeader + 11 * 80 + 25, "%5i",  m_FRAMENUM ) ] = ' ';
            FITSHeader[ 12 * 80 + 20 + ::sprintf( FITSHeader + 12 * 80 + 20, "%10u", m_FRAMETIM ) ] = ' ';
            FITSHeader[ 13 * 80 + 28 + ::sprintf( FITSHeader + 13 * 80 + 28, "%2i",  m_LC_FORMT ) ] = ' ';
            FITSHeader[ 14 * 80 + 28 + ::sprintf( FITSHeader + 14 * 80 + 28, "%2i",  m_LC_CBITS ) ] = ' ';
            FITSHeader[ 15 * 80 + 28 + ::sprintf( FITSHeader + 15 * 80 + 28, "%2i",  m_LC_UBITS ) ] = ' ';
            FITSHeader[ 16 * 80 + 25 + ::sprintf( FITSHeader + 16 * 80 + 25, "%5i",  m_LC_OFFST ) ] = ' ';
            FITSHeader[ 17 * 80 + 25 + ::sprintf( FITSHeader + 17 * 80 + 25, "%10u", m_CPUCLKHI ) ] = ' ';
            FITSHeader[ 18 * 80 + 25 + ::sprintf( FITSHeader + 18 * 80 + 25, "%10u", m_CPUCLKLO ) ] = ' ';
            FITSHeader[ 19 * 80 + 20 + ::sprintf( FITSHeader + 19 * 80 + 20, "%5i",  m_DMABUFID ) ] = ' ';
        }
    }

    // Round up header size to multiple of 36 * 80 = 2880
    int FITSHeaderSize = ( ( FITSHeaderBytes + 2880 - 1 ) / 2880 ) * 2880;

    // Pad the rest of the header with blanks
    if ( FITSHeaderBytes < FITSHeaderSize )
    {
        ::FillMemory( FITSHeader + FITSHeaderBytes, FITSHeaderSize - FITSHeaderBytes, ' ' );
    }

    return FITSHeaderSize;
}

int IoAFITSHeader::Decode( const char *pFITSHeader )
{
    Clear( );

    if ( pFITSHeader == NULL ) return FALSE;

    char String[ 21 ], Value[ 21 ]; Value[ 20 ] = '\0';

	int i;

    for ( i = 0; i < 2880; i += 80 )
    {
        const char *pRecord = pFITSHeader + i;

        if ( ::strncmp( pRecord, "END     ", 8 ) == 0 ) break;  // end-of-header record

        if ( ::strncmp( pRecord, "COMMENT ", 8 ) == 0 ) continue;  // skip comment

        ::CopyMemory( Value, pRecord + 10, 20 );

        if ( ::strncmp( pRecord, "SIMPLE  ", 8 ) == 0 )
        {
            if ( ::sscanf( Value, "%s", String ) != 1 ) return FALSE;

            m_SIMPLE = ::strlen( String ) == 1 && String[ 0 ] == 'T';
        }
        else if ( ::strncmp( pRecord, "BITPIX  ", 8 ) == 0 )
        {
            if ( ::sscanf( Value, "%d", &m_BITPIX ) != 1 ) return FALSE;
        }
        else if ( ::strncmp( pRecord, "NAXIS   ", 8 ) == 0 )
        {
            if ( ::sscanf( Value, "%d", &m_NAXIS ) != 1 ) return FALSE;
        }
        else if ( ::strncmp( pRecord, "NAXIS1  ", 8 ) == 0 )
        {
            if ( ::sscanf( Value, "%d", &m_NAXIS1 ) != 1 ) return FALSE;
        }
        else if ( ::strncmp( pRecord, "NAXIS2  ", 8 ) == 0 )
        {
            if ( ::sscanf( Value, "%d", &m_NAXIS2 ) != 1 ) return FALSE;
        }
        else if ( ::strncmp( pRecord, "EXTEND  ", 8 ) == 0 )
        {
            if ( ::sscanf( Value, "%s", String ) != 1 ) return FALSE;

            m_EXTEND = ::strlen( String ) == 1 && String[ 0 ] == 'T';
        }
        else if ( ::strncmp( pRecord, "BZERO   ", 8 ) == 0 )
        {
            float Float;

            if ( ::sscanf( Value, "%f", &Float ) != 1 ) return FALSE;

            m_BZERO = (double) Float;
        }
        else if ( ::strncmp( pRecord, "BSCALE  ", 8 ) == 0 )
        {
            float Float;

            if ( ::sscanf( Value, "%f", &Float ) != 1 ) return FALSE;

            m_BSCALE = (double) Float;
        }
        else if ( ::strncmp( pRecord, "FRAMETIM", 8 ) == 0 )
        {
            if ( ::sscanf( Value, "%d", &m_FRAMETIM ) != 1 ) return FALSE;
        }
        else if ( ::strncmp( pRecord, "CAMERAID", 8 ) == 0 )
        {
            if ( ::sscanf( Value, "%d", &m_CAMERAID ) != 1 ) return FALSE;
        }
        else if ( ::strncmp( pRecord, "FRAMENUM", 8 ) == 0 )
        {
            if ( ::sscanf( Value, "%d", &m_FRAMENUM ) != 1 ) return FALSE;
        }
        else if ( ::strncmp( pRecord, "LC_FORMT", 8 ) == 0 )
        {
            if ( ::sscanf( Value, "%d", &m_LC_FORMT ) != 1 ) return FALSE;
        }
        else if ( ::strncmp( pRecord, "LC_CBITS", 8 ) == 0 )
        {
            if ( ::sscanf( Value, "%d", &m_LC_CBITS ) != 1 ) return FALSE;
        }
        else if ( ::strncmp( pRecord, "LC_UBITS", 8 ) == 0 )
        {
            if ( ::sscanf( Value, "%d", &m_LC_UBITS ) != 1 ) return FALSE;
        }
        else if ( ::strncmp( pRecord, "LC_OFFST", 8 ) == 0 )
        {
            if ( ::sscanf( Value, "%d", &m_LC_OFFST ) != 1 ) return FALSE;
        }
        else if ( ::strncmp( pRecord, "LCL_ZERO", 8 ) == 0 )
        {
            if ( ::sscanf( Value, "%d", &m_LCL_ZERO ) != 1 ) return FALSE;
        }
        else if ( ::strncmp( pRecord, "m_CPUCLKHI", 8 ) == 0 )
        {
            if ( ::sscanf( Value, "%d", &m_CPUCLKHI ) != 1 ) return FALSE;
        }
        else if ( ::strncmp( pRecord, "m_CPUCLKLO", 8 ) == 0 )
        {
            if ( ::sscanf( Value, "%d", &m_CPUCLKLO ) != 1 ) return FALSE;
        }
        else if ( ::strncmp( pRecord, "m_DMABUFID", 8 ) == 0 )
        {
            if ( ::sscanf( Value, "%d", &m_DMABUFID ) != 1 ) return FALSE;
        }
    }

    if ( ::strncmp( pFITSHeader + i, "END     ", 8 ) != 0 ) return FALSE;  // missing end-of-header record

    // Verify header entries
    if ( ! m_SIMPLE || ! m_EXTEND ) return FALSE;

    if ( m_BITPIX != 16 || m_NAXIS != 2 || m_NAXIS1 <= 0 || m_NAXIS2 <= 0 ) return FALSE;

    if ( m_BZERO != 32768.0 || m_BSCALE != 1.0 ) return FALSE;

    if ( m_LC_FORMT < 0 || m_LC_FORMT > 1 ) return FALSE;

    m_Valid = TRUE;

    return TRUE;
}
