#include "stdafx.h"
#include "ImageFileSequence.h"

IoAImageFileSequence::IoAImageFileSequence( )
{
    m_RootFolder = m_FileName = NULL;

    for ( int ParameterIndex = 0; ParameterIndex < eParameterCount; ParameterIndex++ )
    {
        for ( int CameraIndex = 0; CameraIndex < maxCameras; CameraIndex++ )
        {
            m_Data[ ParameterIndex ][ CameraIndex ] = NULL;
        }
    }

    m_FilePath[ 0 ] = '\0';

    m_CameraCount = 0;

    for ( int CameraIndex = 0; CameraIndex < maxCameras; CameraIndex++ )
    {
        m_CameraNumber[ CameraIndex ] = -1;

        m_FolderCount[ CameraIndex ] = 0;
        m_FileCount  [ CameraIndex ] = 0;
        m_FrameCount [ CameraIndex ] = 0;
    }
}

void IoAImageFileSequence::Free( )
{
    delete m_RootFolder; m_RootFolder = NULL;
    delete m_FileName;   m_FileName   = NULL;

    for ( int ParameterIndex = 0; ParameterIndex < eParameterCount; ParameterIndex++ )
    {
        for ( int CameraIndex = 0; CameraIndex < maxCameras; CameraIndex++ )
        {
            delete m_Data[ ParameterIndex ][ CameraIndex ]; m_Data[ ParameterIndex ][ CameraIndex ] = NULL;
        }
    }
}

void IoAImageFileSequence::Clear( )
{
    Free( );

    m_FileBuffer.Free( );

    m_FilePath[ 0 ] = '\0';

    for ( int CameraIndex = 0; CameraIndex < maxCameras; CameraIndex++ )
    {
        m_FolderCount[ CameraIndex ] = 0;
        m_FileCount  [ CameraIndex ] = 0;
        m_FrameCount [ CameraIndex ] = 0;
    }

    m_CameraCount = 0;
}

int IoAImageFileSequence::ParseIndexFiles( const char *RootFolder, const char *FileName )
{
    Clear( );  // free up any old resources

    // Make sure root folder exists
    if ( RootFolder == NULL ) return FALSE;  // undefined root folder

    DWORD FileAttributes = ::GetFileAttributes( RootFolder );

    if ( FileAttributes == 0xffffffff ) return FALSE;  // root folder does not exist
    
    if ( ( FileAttributes & FILE_ATTRIBUTE_DIRECTORY ) == 0 ) return FALSE;  // pathname does not point to folder

    HANDLE hFind = INVALID_HANDLE_VALUE;

    // Save copy of root folder string
    m_RootFolder = new char[ ::strlen( RootFolder ) + 1 ];

    if ( m_RootFolder == NULL ) return FALSE;

    ::strcpy( m_RootFolder, RootFolder );

    // Convenient scope for cleaning up after any errors
    for ( ; ; )
    {
        // If file name string is not defined, extract it from index file name
        WIN32_FIND_DATA FindData;  // keep structure in scope long enough to make copy of file name string

        if ( FileName == NULL )
        {
            ::sprintf( m_FilePath, "%s\\*_Index_*.log", RootFolder );  // any index file will do

            hFind = ::FindFirstFile( m_FilePath, &FindData );

            if ( hFind == INVALID_HANDLE_VALUE ) break;  // no index files in root folder

            if ( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) break;

            ::strrchr( FindData.cFileName, '_' )[ -6 ] = '\0';  // terminate string on first character of "_Index_"

            FileName = FindData.cFileName;

            ::FindClose( hFind ); hFind = INVALID_HANDLE_VALUE;
        }

        // Save copy of file name string
        m_FileName = new char[ ::strlen( FileName ) + 1 ];

        if ( m_FileName == NULL ) break;

        ::strcpy( m_FileName, FileName );

        // Count cameras by searching for index files
        ::sprintf( m_FilePath, "%s\\%s_Index_*.log", m_RootFolder, m_FileName );

        HANDLE hFind = ::FindFirstFile( m_FilePath, &FindData );

        if ( hFind == INVALID_HANDLE_VALUE ) break;

        char Format[ 100 ];

        ::sprintf( Format, "%s_Index_%%d.log", m_FileName );

        int maxIndexFileSize = 0;
        unsigned int CameraFlags = 0x00000000;

        for ( int BadCameraNumber = FALSE; ; )
        {
            int CameraNumber;

            if ( ::sscanf( FindData.cFileName, Format, &CameraNumber ) != 1 ) break;

            if ( CameraNumber < 0 || CameraNumber > 15 )  // camera numbers limited to 4 bits
            {
                BadCameraNumber = TRUE; break;
            }

            CameraFlags |= 0x00000001 << CameraNumber;

            int IndexFileSize = (int) FindData.nFileSizeLow;

            if ( IndexFileSize > maxIndexFileSize ) maxIndexFileSize = IndexFileSize;

            if ( ! ::FindNextFile( hFind, &FindData ) ) break;
        }

        if ( BadCameraNumber || GetLastError( ) != ERROR_NO_MORE_FILES ) break;

        ::FindClose( hFind ); hFind = INVALID_HANDLE_VALUE;

        int CameraCount = 0;

        for ( int CameraNumber = 0; CameraNumber < maxCameras; CameraNumber++ )
        {
            if ( CameraFlags & 0x00000001 << CameraNumber )
            {
                m_CameraNumber[ CameraCount++ ] = CameraNumber;
            }
        }

        if ( CameraCount == 0 ) break;

        m_CameraCount = CameraCount;

        // Allocate buffer space for reading index file contents
        if ( ! m_FileBuffer.Allocate( maxIndexFileSize + 1 ) ) break;

        // Parse index files
        int maxLCCFileSize = 0;

        for ( int CameraIndex = 0; CameraIndex < CameraCount; CameraIndex++ )
        {
            int CameraNumber = m_CameraNumber[ CameraIndex ];

            // Read index file contents
            ::sprintf( m_FilePath, "%s\\%s_Index_%d.log", m_RootFolder, m_FileName, CameraNumber );

            m_FileBuffer.SynchronousRead( m_FilePath );

            int IndexFileSize = m_FileBuffer.ByteDataSize( );

            char *Buffer = (char *) m_FileBuffer.Address( );

            Buffer[ IndexFileSize ] = '\0';  // so that sscanf( ) can't run off the end of the buffer

            // Parse header record
            char sRootFolder[ 300 ], sFileName[ 100 ]; 
            int HeaderBytes;

            if ( ::sscanf( Buffer, "%[^,],Camera,Folder,%[^,],lcz %n", sRootFolder, sFileName, &HeaderBytes ) != 2 ) break;

            if ( ::strcmp( sFileName, m_FileName ) ) break;  // check file name string

            // First pass through data records: check camera number and update index limits and LCC file size limit
            int maxFolderIndex = 0;
            int maxFileIndex   = 0;
            int maxFrameIndex  = 0;

            for ( int ByteCount = HeaderBytes; ByteCount < IndexFileSize; )
            {
                int x, y, z, fi, fo, fs, fn, ft, n;

                if ( ::sscanf( Buffer + ByteCount, "%d,%d,%d,%d,%d,%d,%d,%d %n",
                               &x, &y, &z, &fi, &fo, &fs, &fn, &ft, &n ) != 8 ) break;

                if ( x != CameraNumber ) break;

                int FolderIndex = y;
                int FileIndex   = z;
                int FrameIndex  = fi;
                int LCCFileSize = fs;

                if ( FolderIndex > maxFolderIndex ) maxFolderIndex = FolderIndex;
                if ( FileIndex   > maxFileIndex   ) maxFileIndex   = FileIndex;
                if ( FrameIndex  > maxFrameIndex  ) maxFrameIndex  = FrameIndex;
                if ( LCCFileSize > maxLCCFileSize ) maxLCCFileSize = LCCFileSize;

                ByteCount += n;
            }

            if ( ByteCount < IndexFileSize ) break;

            int FolderCount = maxFolderIndex + 1;
            int FileCount   = maxFileIndex   + 1;
            int FrameCount  = maxFrameIndex  + 1;

            // Allocate space for index file data
            for ( int ParameterIndex = 0; ParameterIndex < eParameterCount; ParameterIndex++ )
            {
                int DataCount;

                     if ( ParameterIndex == eFolderFlag ) DataCount = FolderCount;
                else if ( ParameterIndex == eFileFlag   ) DataCount = FileCount;
                else                                      DataCount = FrameCount;

                m_Data[ ParameterIndex ][ CameraIndex ] = new int[ DataCount ];

                if ( m_Data[ ParameterIndex ][ CameraIndex ] == NULL ) break;

                ::ZeroMemory( m_Data[ ParameterIndex ][ CameraIndex ], DataCount * sizeof( int ) );
            }

            if ( ParameterIndex < eParameterCount ) break;

            // Second pass through data records: store index file data
            for ( ByteCount = HeaderBytes; ByteCount < IndexFileSize; )
            {
                int x, y, z, fi, fo, fs, fn, ft, n;

                if ( ::sscanf( Buffer + ByteCount, "%d,%d,%d,%d,%d,%d,%d,%d %n",
                               &x, &y, &z, &fi, &fo, &fs, &fn, &ft, &n ) != 8 ) break;

                int FolderIndex = y;
                int FileIndex   = z;
                int FrameIndex  = fi;

                if ( 0 <= FolderIndex && FolderIndex < FolderCount )
                {
                    m_Data[ eFolderFlag ][ CameraIndex ][ FolderIndex ] = 1;
                }

                if ( 0 <= FileIndex && FileIndex < FileCount )
                {
                    if ( m_Data[ eFileFlag ][ CameraIndex ][ FileIndex ] )
                    {
                        if ( FolderIndex != m_Data[ eFileFlag ][ CameraIndex ][ FileIndex ] - 1 ) break;
                    }
                    else
                    {
                        m_Data[ eFileFlag ][ CameraIndex ][ FileIndex ] = FolderIndex + 1;
                    }
                }

                if ( 0 <= FrameIndex && FrameIndex < FrameCount )
                {
                    m_Data[ eFolderIndex ][ CameraIndex ][ FrameIndex ] = y;
                    m_Data[ eFileIndex   ][ CameraIndex ][ FrameIndex ] = z;
                    m_Data[ eFileOffset  ][ CameraIndex ][ FrameIndex ] = fo;
                    m_Data[ eFileSize    ][ CameraIndex ][ FrameIndex ] = fs;
                    m_Data[ eFrameNumber ][ CameraIndex ][ FrameIndex ] = fn;
                    m_Data[ eTimeStamp   ][ CameraIndex ][ FrameIndex ] = ft;
                }

                ByteCount += n;
            }

            if ( ByteCount < IndexFileSize ) break;

            m_FolderCount[ CameraIndex ] = FolderCount;
            m_FileCount  [ CameraIndex ] = FileCount;
            m_FrameCount [ CameraIndex ] = FrameCount;
        }

        if ( CameraIndex < CameraCount ) break;

        // Allocate buffer space for reading LCC file contents
        if ( ! m_FileBuffer.Allocate( maxLCCFileSize ) ) break;

        return TRUE;
    }

    // Clean up the mess
    if ( hFind != INVALID_HANDLE_VALUE ) ::FindClose( hFind );

    Clear( );

    return FALSE;
}

int IoAImageFileSequence::VerifyFolders( int CameraIndex )
{
    if ( CameraIndex < 0 || CameraIndex >= m_CameraCount ) return FALSE;

    int CameraNumber = m_CameraNumber[ CameraIndex ];

    ::sprintf( m_FilePath, "%s\\Camera_%d", m_RootFolder, CameraNumber );

    DWORD FileAttributes = ::GetFileAttributes( m_FilePath );

    if ( FileAttributes == 0xffffffff ) return FALSE;  // camera folder does not exist

    if ( ( FileAttributes & FILE_ATTRIBUTE_DIRECTORY ) == 0 ) return FALSE;  // pathname does not point to folder

    int FolderCount = m_FolderCount[ CameraIndex ];

    for ( int FolderIndex = 0; FolderIndex < FolderCount; FolderIndex++ )
    {
        if ( m_Data[ eFolderFlag ][ CameraIndex ][ FolderIndex ] )
        {
            ::sprintf( m_FilePath, "%s\\Camera_%d\\Folder_%d", m_RootFolder, CameraNumber, FolderIndex );

            DWORD FileAttributes = ::GetFileAttributes( m_FilePath );

            if ( FileAttributes == 0xffffffff ) return FALSE;  // camera sub-folder does not exist

            if ( ( FileAttributes & FILE_ATTRIBUTE_DIRECTORY ) == 0 ) return FALSE;  // pathname does not point to folder
        }
    }

    return TRUE;
}

int IoAImageFileSequence::VerifyFiles( int CameraIndex )
{
    if ( CameraIndex < 0 || CameraIndex >= m_CameraCount ) return FALSE;

    int CameraNumber = m_CameraNumber[ CameraIndex ];

    int FileCount = m_FileCount[ CameraIndex ];

    for ( int FileIndex = 0; FileIndex < FileCount; FileIndex++ )
    {
        if ( m_Data[ eFileFlag ][ CameraIndex ][ FileIndex ] )
        {
            int FolderIndex = m_Data[ eFileFlag ][ CameraIndex ][ FileIndex ] - 1;

            ::sprintf( m_FilePath, "%s\\%s_%d\\%s_%d\\%s_%d_%d.%s", RootFolder( ), CameraName( ), CameraNumber,
                       FolderName( ), FolderIndex, FileName( ), CameraNumber, FileIndex, FileExtension( ) );

            DWORD FileAttributes = ::GetFileAttributes( m_FilePath );

            if ( FileAttributes == 0xffffffff ) return FALSE;  // file does not exist

            if ( ( FileAttributes & FILE_ATTRIBUTE_DIRECTORY ) != 0 ) return FALSE;  // pathname does not point to file
        }
    }

    return TRUE;
}
