#include "geometryCacheFile.h"
#include "geometryCacheBlockBase.h"
#include "geometryCacheBlockStringData.h"
#include "geometryCacheBlockIntData.h"
#include "geometryCacheBlockDVAData.h"
#include "geometryCacheBlockFVAData.h"
#include <maya/flib.h>
#include <maya/MString.h>
#include <maya/MGlobal.h>
#include <fstream>
#if defined (OSMac_)
#include <stdlib.h>
#else
#include <malloc.h>
#endif
geometryCacheFile::geometryCacheFile( 
const MString& fileName, 
MIffFile* iffFile )
    :cacheFileName( fileName )
{   
    readStatus = false;
    iffFilePtr = iffFile;
}
geometryCacheFile::~geometryCacheFile()
{
    
    
    
    
    while( blockList.size() )
    {   
        
        
        geometryCacheBlockBase * block = blockList.back();
        delete block;
        
        
        blockList.pop_back();
    }
    
    
    
}
const MString& geometryCacheFile::fileName()
 
{
    return cacheFileName;
}
const bool& geometryCacheFile::isRead()
{
    return readStatus;
}
bool geometryCacheFile::readCacheFiles()
{   
    
    
    status = iffFilePtr->open( cacheFileName );
    if( !status ) return false;
    
    
    if( iffFilePtr->isActive() ) {
        
        
        readStatus  = readHeaderGroup( status );
        if( !readStatus ) return false;
        
        
        do {
            
            
            readStatus = readChannelGroup( status );
            if( !readStatus ) return false;
    } else
        
        
        return false;
    return true;
}
bool geometryCacheFile::convertToAscii()
{
    
    
    int loc = cacheFileName.rindex('.');
    outputFileName += ".txt";
    
    
    std::ofstream oFile( outputFileName.
asChar() );
    if( !oFile.bad() ) 
    {
        
        
        cacheBlockIterator blockIt;
        
        
        for( blockIt = blockList.begin();
            blockIt != blockList.end();
            blockIt++ )
        {
            
            
            geometryCacheBlockBase* block = *blockIt;
            
            
            
            block->outputToAscii( oFile );
        }
    } else
        
        
        return false;
    
    
                            cacheFileName +
                            "\" to file \"" +
                            outputFileName + 
                            "\"" );
    return true;
}
bool geometryCacheFile::readHeaderGroup( 
MStatus& status )
 
{
    status = iffFilePtr->beginReadGroup( tmpTag, cachTag );
    if( status ) {
        if( cachTag == 
MIffTag(
'C', 
'A', 
'C', 
'H') ) {
 
            
            
            storeCacheBlock( "CACH" );
            
            
            readStatus = readHeaderVersion();
            if( !readStatus ) return false;
            
            
            readStatus = readHeaderTimeRange();
            if( !readStatus ) return false;
            
            
            storeCacheBlock( "/CACH" );
        }       
    }
    iffFilePtr->endReadGroup();
    return true;
}
bool geometryCacheFile::readHeaderVersion()
{
    unsigned int byteCount;
    
    status = iffFilePtr->beginGet( tmpTag, (unsigned *)&byteCount );
    if( !status ) return false;
    char *tmpName = (char*)alloca(sizeof(char)* (byteCount+1));
    
    
    if( tmpName && tmpTag == 
MIffTag(
'V', 
'R', 
'S', 
'N') ) {
 
        
        
        iffFilePtr->get( tmpName, byteCount, &status);
        if( !status ) return false;
        
        
        storeCacheBlock( "VRSN", tmpName );
        status = iffFilePtr->endGet();
        if( !status ) return false;
    }
    return true;
}
bool geometryCacheFile::readHeaderTimeRange()
{
    unsigned int byteCount;
    
#if (!defined BYTE_ORDER) || (BYTE_ORDER == BIG_ENDIAN)
    const int *startTime = (int *)iffFilePtr->getChunk( 
                                tmpTag, 
                                (unsigned int *)&byteCount );  
    if( startTime == NULL ||
        !(tmpTag == 
MIffTag (
'S', 
'T', 
'I', 
'M')) || 
        byteCount != sizeof(int) )
        return false;
    
    
    storeCacheBlock( "STIM", *startTime );
    const int *endTime = (int *)iffFilePtr->getChunk( 
                                tmpTag, 
                                (unsigned int *)&byteCount );  
    if( endTime == NULL ||
        !(tmpTag == 
MIffTag(
'E', 
'T', 
'I', 
'M')) ||
        byteCount != sizeof(int) )
        return false;               
    
    
    storeCacheBlock( "ETIM", *endTime );
#else
    const int *startTime = (int *)iffFilePtr->getChunk( tmpTag, (unsigned int *)&byteCount );  
    if( startTime == NULL ||
        
        !(tmpTag == 
MIffTag (
'S', 
'T', 
'I', 
'M')) ||
        byteCount != sizeof(int) )
        return false;
    
    
    storeCacheBlock( "STIM", FLswapword(*startTime) );
    const int *endTime = (int *)iffFilePtr->getChunk( tmpTag, (unsigned int *)&byteCount );  
    if( endTime == NULL ||
        !(tmpTag == 
MIffTag(
'E', 
'T', 
'I', 
'M')) ||
        byteCount != sizeof(int) )
        return false;
    
    
    storeCacheBlock( "ETIM", FLswapword(*endTime) );
#endif
    return true;
}
bool geometryCacheFile::readChannelGroup( 
MStatus& groupStatus )
 
{
    unsigned int byteCount;
    groupStatus = iffFilePtr->beginReadGroup(tmpTag, mychTag);
        if( mychTag == 
MIffTag(
'M', 
'Y', 
'C', 
'H')) {
 
            
            
            storeCacheBlock( "MYCH" );
            
            
            MStatus stat = iffFilePtr->beginGet(tmpTag, (
unsigned *)&byteCount);
 
            if (!stat) return false;
            
            
            
            if( tmpTag == 
MIffTag(
'T', 
'I', 
'M', 
'E') )
 
            {
                
                
                readStatus = readChannelTime();
                if ( !readStatus ) return false;
                
                
                stat = iffFilePtr->endGet();
                if (!stat) return false;
            }
            
            
            do {
                
                
                readStatus = readChannel( channelStatus );
                if( !readStatus ) return false;
            
            
            storeCacheBlock( "/MYCH" );
        }
    } 
    iffFilePtr->endReadGroup();
    return readStatus;
}
bool geometryCacheFile::readChannelTime()
{
    unsigned int byteCount;
    const int *tmpNum = (int*) iffFilePtr->getChunk( tmpTag, (unsigned *)&byteCount );
    if( tmpNum && 
        tmpTag == 
MIffTag(
'T', 
'I', 
'M', 
'E') && 
        byteCount == sizeof(int)) {
#if (!defined BYTE_ORDER) || (BYTE_ORDER == BIG_ENDIAN)
        
        
        storeCacheBlock( "TIME", *tmpNum );
#else
        
        
        storeCacheBlock( "TIME", FLswapword(*tmpNum) );
#endif      
    } 
    return true;
}
bool geometryCacheFile::readChannel( 
MStatus& channelStatus )
 
{
    readStatus = readChannelName( channelStatus );
    if( !readStatus ) return false;
    
    
    
    if( !channelStatus ) return true;
    readStatus = readChannelData();
    if( !readStatus ) return false;
    return true;
}
bool geometryCacheFile::readChannelName( 
MStatus& channelStatus )
 
{
    unsigned int byteCount;
    
    channelStatus = iffFilePtr->beginGet( chnmTag, (unsigned *)&byteCount );
        char *tmpName = (char*)alloca(sizeof(char)* (byteCount+1));
        
        
        if( tmpName && chnmTag == 
MIffTag(
'C', 
'H', 
'N', 
'M') ) {
 
            iffFilePtr->get( tmpName, byteCount, &lStatus);
            if( !lStatus ) return false;
            
            
            storeCacheBlock( "CHNM", channelName );
        } else return false;
    } 
    iffFilePtr->endGet();
    return true;
}
bool geometryCacheFile::readChannelData()
{
    unsigned int byteCount;
    uint size;
    
    
    const int *tmpNum = (int*) iffFilePtr->getChunk(tmpTag, (unsigned *)&byteCount);
    if( tmpNum && 
        tmpTag == 
MIffTag(
'S', 
'I', 
'Z', 
'E') && 
        byteCount == sizeof(int)) 
    {
#if (!defined BYTE_ORDER) || (BYTE_ORDER == BIG_ENDIAN)
            size = *tmpNum;
#else
            size = FLswapword(*tmpNum);
#endif
        
        
        storeCacheBlock( "SIZE", size );
    } else return false;
    
    
    if( size ) {
        const void *tmpVec = (double*) iffFilePtr->getChunk(tmpTag, (unsigned *)&byteCount);
        if( tmpVec && 
            tmpTag == 
MIffTag(
'D', 
'V', 
'C', 
'A') &&
            size * sizeof(double)*3 == byteCount )
        {
            double *tmpVecDbl = (double*) tmpVec;
            double *dataArray = new double[size*3];
#if (!defined BYTE_ORDER) || (BYTE_ORDER == BIG_ENDIAN)
            memcpy(dataArray, tmpVec, byteCount);
#else
            for(unsigned int i = 0; i < size*3; i++) {
                FLswapdouble(tmpVecDbl[i], &(dataArray[i]));
            }
#endif      
            
            
            storeCacheBlock( "DVCA", dataArray, size );
            delete [] dataArray;
        } else if( tmpVec && 
                   tmpTag == 
MIffTag(
'F', 
'V', 
'C', 
'A') &&
                   size * sizeof(float)*3 == byteCount ) {
            float *tmpVecFlt = (float*) tmpVec;
            float *dataArray = new float[size*3];
#if (!defined BYTE_ORDER) || (BYTE_ORDER == BIG_ENDIAN)
            memcpy(dataArray, tmpVec, byteCount);
#else
            for(unsigned int i = 0; i < size*3; i++) {
                FLswapfloat(tmpVecFlt[i], &(dataArray[i]));
            }
#endif      
            
            
            storeCacheBlock( "FVCA", dataArray, size );
            delete [] dataArray;
        } else {
            return false;
        }
    }
    return true;
}
void geometryCacheFile::storeCacheBlock( 
const MString& tag )
 
{
    blockList.push_back( new geometryCacheBlockBase( tag ) );
}
void geometryCacheFile::storeCacheBlock( 
const MString& tag, 
const int& value )
 
{
    blockList.push_back( new geometryCacheBlockIntData( tag, value ) );
}
void geometryCacheFile::storeCacheBlock( 
const MString& tag, 
const MString& value  )
 
{
    blockList.push_back( new geometryCacheBlockStringData( tag, value ) );
}
void geometryCacheFile::storeCacheBlock( 
const MString& tag, 
const double* value, 
const uint& size )
 
{
    blockList.push_back( new geometryCacheBlockDVAData( tag, value, size ) );
}
void geometryCacheFile::storeCacheBlock( 
const MString& tag, 
const float* value, 
const uint& size )
 
{
    blockList.push_back( new geometryCacheBlockFVAData( tag, value, size ) );
}