#include "stdafx.h"
#include "FITSFrameFormat.h"

int IoAFrameFITSFileBuffer::Allocate( int FrameXSize, int FrameYSize, int MaxFrameCount, int ChannelCount )
{
    Free( );

    if ( FrameXSize    <= 0 || FrameYSize   <= 0 ) return FALSE;
    if ( MaxFrameCount <= 0 || ChannelCount <= 0 ) return FALSE;

    int AxisCount;

    if ( ChannelCount > 1 )
    {
        AxisCount = 4;
    }
    else if ( MaxFrameCount > 1 )
    {
        AxisCount = 3;
    }
    else
    {
        AxisCount = 2;
    }

    int MaxKeywordCount   = 14 + AxisCount + ChannelCount;
    int MaxDataBytes      = MaxFrameCount * FrameXSize * FrameYSize * sizeof( unsigned short );
    int MaxExtensionBytes = MaxFrameCount * ( 2 * sizeof( int ) + 2 * sizeof( __int64 ) );

    if ( ! IoAFITSFileBuffer::Allocate( MaxKeywordCount, MaxDataBytes, MaxExtensionBytes ) ) return FALSE;

    int FrameCount = MaxFrameCount * ChannelCount;

    m_FrameNumber = new   int  [ FrameCount ];
    m_FrameTime   = new __int64[ FrameCount ];
    m_CPUClock    = new __int64[ FrameCount ];
    m_DMABufferID = new   int  [ FrameCount ];

    if ( m_FrameNumber == NULL || m_FrameTime == NULL || m_CPUClock == NULL || m_DMABufferID == NULL )
    {
        Free( );

        return FALSE;
    }

    m_AxisCount = AxisCount;

    m_FrameXSize = FrameXSize;
    m_FrameYSize = FrameYSize;

    m_MaxFrameCount = MaxFrameCount;
    m_ChannelCount  = ChannelCount;

    return TRUE;
}

void IoAFrameFITSFileBuffer::Free( )
{
    IoAFITSFileBuffer::Free( );

    delete [ ] m_FrameNumber;
    delete [ ] m_FrameTime;
    delete [ ] m_CPUClock;
    delete [ ] m_DMABufferID;

    Reset( );
}

int IoAFrameFITSFileBuffer::Open( )
{
    Clear( );  // clear header, data and extension records

    if ( ! SetValue( NewKeyword( "SIMPLE" ), "T", "file does conform to FITS standard" ) return FALSE;

    if ( ! SetValue( NewKeyword( "BITPIX" ), 16, "number of bits per data pixel = 16" ) return FALSE;

    if ( ! SetValue( NewKeyword( "NAXIS"  ), m_AxisCount, "number of data axes" ) return FALSE;
    if ( ! SetValue( NewKeyword( "NAXIS1" ), m_FrameXSize, "frame X size" ) return FALSE;
    if ( ! SetValue( NewKeyword( "NAXIS2" ), m_FrameYSize, "frame Y size" ) return FALSE;

    if ( m_AxisCount >= 3 )
    {
        if ( ! NewKeyword( "NAXIS3" ) ) return FALSE;
    }

    if ( m_AxisCount >= 4 )
    {
        if ( ! SetValue( NewKeyword( "NAXIS4" ), m_ChannelCount, "number of channels per frame" ) return FALSE;
    }

    if ( ! SetValue( NewKeyword( "EXTEND" ), "T", "FITS dataset may contain extensions" ) return FALSE;

    if ( ! NewComment( "FITS (Flexible Image Transport System) format is defined in 'Astronomy" ) ) return FALSE;
    if ( ! NewComment( "and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H"  ) ) return FALSE;

    if ( ! SetValue( NewKeyword( "BZERO" ), "32768.0", "offset data range to that of unsigned short" ) return FALSE;
    if ( ! SetValue( NewKeyword( "BSCALE" ), "1.0", "default scaling factor" ) return FALSE;

    if ( ! SetValue( NewKeyword( "MAXNFRAM" ), m_MaxFrameCount, "maximum number of frames" ) return FALSE;

    // Keywords set on arrival of first frame in buffer
    for ( int i = 0; i < m_ChannelCount; i++ )
    {
        CString Name;

        Name.Format( "CCDNUM%d", i );

        if ( NewKeyword( Name ) < 0 ) return FALSE;  // CCD number = camera ID returned by Lucky camera
    }

    if ( NewKeyword( "GRABTIME" ) < 0 ) return FALSE;  // 64-bit integer FILETIME value

    if ( NewKeyword( "SEQFNUM"  ) < 0 ) return FALSE;  // frame number for first frame in image sequence
    if ( NewKeyword( "CAMFNUM"  ) < 0 ) return FALSE;  // frame number for first frame in buffer
    if ( NewKeyword( "GRABFNDX" ) < 0 ) return FALSE;  // frame index  for first frame in buffer

    // Note: camera frame index = CAMFNUM - SEQFNUM = number of frames output by camera - 1
    //         grab frame index = GRABFNDX          = number of frames grabbed by data acquisition system - 1

    //           dropped frames = (camera frame index) - (grab frame index) = (CAMFNUM - SEQFNUM) - GRABFNDX

    if ( ! NewKeyword( "END" ) ) return FALSE;  // always last record in FITS header

    m_FrameCount = 0;

    return TRUE;
}

int IoAFrameFITSFileBuffer::Close( )
{
    // Make sure FITS buffer is open
    if ( ! IsOpen( ) ) return FALSE;

    // Encode frame count in FITS header
    if ( m_AxisCount > 2 )
    {
        if ( ! SetValue( NewKeyword( "NAXIS3" ), m_FrameCount, "number of frames" ) return FALSE;
    }

    // Copy frame numbers, frame times, CPU clock counts and DMA buffer IDs into FITS extension record
    int FrameCount = m_FrameCount * m_ChannelCount;

    if ( ! NewExtension( m_FrameNumber, FrameCount * sizeof(   int   ) ) return FALSE;
    if ( ! NewExtension( m_FrameTime,   FrameCount * sizeof( __int64 ) ) return FALSE;
    if ( ! NewExtension( m_CPUClock,    FrameCount * sizeof( __int64 ) ) return FALSE;
    if ( ! NewExtension( m_DMABufferID, FrameCount * sizeof(   int   ) ) return FALSE;

    // Calculate final FITS file size according to contents of FITS file buffer
    if ( ! SetDataSize( FileByteCount( ) ) ) return FALSE;

    return TRUE;
}

int IoAFrameFITSFileBuffer::AddFrame( const IoAFrameBuffer *FrameBuffers[ ], int ChannelCount )
{
    // Make sure FITS buffer is open
    if ( ! IsOpen( ) ) return FALSE;

    // Provide default value if channel count argument omitted
    if ( ChannelCount = -1 ) ChannelCount = m_ChannelCount;

    if ( FrameBuffers == NULL ) return FALSE;

    if ( ChannelCount != m_ChannelCount ) return FALSE;

    const IoAFrameBuffer& FrameBuffer = *( FrameBuffers[ 0 ] );

    int FrameNumber, FrameIndex;

    // Make sure frame buffer contents are valid
    for ( int i = 0; i < ChannelCount; i++ )
    {
        const IoAFrameBuffer& FrameBuffer = *( FrameBuffers[ i ] );

        if ( ! FrameBuffer.IsAllocated( ) ) return FALSE;

        if ( ! FrameBuffer.IsFrameValid( ) ) return FALSE;

        if ( FrameBuffer.XSize( ) != m_FrameXSize ) return FALSE;
        if ( FrameBuffer.YSize( ) != m_FrameYSize ) return FALSE;

        // Make sure frame numbers and frame index values are the same across all channels
        if ( i == 0 )
        {
            FrameNumber = FrameBuffer.FrameNumber( );
            FrameIndex  = FrameBuffer.FrameIndex ( );
        }
        else
        {
            if ( FrameBuffer.FrameNumber( ) != FrameNumber ) return FALSE;
            if ( FrameBuffer.FrameIndex ( ) != FrameIndex  ) return FALSE;
        }
    }

    // Make sure there is room in file buffer
    if ( m_FrameCount < m_MaxFrameCount ) return FALSE;

    int FrameCount = m_FrameCount * m_ChannelCount;

    // Copy frame data into file buffer
    for ( int j = 0; j < ChannelCount; j++ )
    {
        const IoAFrameBuffer& FrameBuffer = *( FrameBuffers[ j ] );

        if ( NewData( FrameBuffer.Address( ), FrameBuffer.FrameBytes( ) ) < 0 ) return FALSE;

        m_FrameNumber[ FrameCount + j ] = FrameBuffer.FrameNumber( );
        m_FrameTime  [ FrameCount + j ] = FrameBuffer.FrameTime( );
        m_CPUClock   [ FrameCount + j ] = FrameBuffer.CPUClock( );
        m_DMABufferID[ FrameCount + j ] = FrameBuffer.DMABufferID( );
    }

    if ( m_FrameCount == 0 )
    {
    }

    m_FrameCount += 1;

    // If FITS buffer is full, automatically close it
    if ( m_FrameCount == m_MaxFrameCount )
    {
        if ( ! Close( ) ) return FALSE;
    }

    return TRUE;
}
