/* \brief 	Object to handle Gatan's Digital Micrograph files
 *
 * Adapted from Bernard Heynman's Bsoft
 *
 */
#include "core_headers.h"
#include <iostream>
#include <iomanip>

DMFile::DMFile( ) {
    version    = 0;
    show       = 0;
    level      = 0;
    sb         = 0;
    endianness = 1;
    ss         = 1;
    keep       = 0;
}

DMFile::DMFile(std::string wanted_filename, bool overwrite) {
    version    = 0;
    show       = 0;
    level      = 0;
    sb         = 0;
    endianness = 1;
    keep       = 0;
    OpenFile(wanted_filename);
}

DMFile::DMFile(wxString wanted_filename) {
    version    = 0;
    show       = 0;
    level      = 0;
    sb         = 0;
    endianness = 1;
    keep       = 0;
    OpenFile(wanted_filename.ToStdString( ));
}

DMFile::~DMFile( ) {
    CloseFile( );
}

bool DMFile::OpenFile(std::string wanted_filename, bool overwrite, bool wait_for_file_to_exist, bool only_check_the_first_image, int eer_super_res_factor, int eer_frames_per_image) {
    MyDebugAssertFalse(overwrite, "Overwriting is not supported for DM files");
    MyDebugAssertFalse(wait_for_file_to_exist, "Waiting for file to exist not supported for DM files");
    unsigned char* fake_pointer;
    readDM(wxString(wanted_filename), fake_pointer, false);
    filename   = wxString(wanted_filename);
    pixel_size = 1.0; //TODO: figure out where the pixel size is (if anywhere)

    // TODO: return false if something is fishy about this file
    return true;
}

void DMFile::CloseFile( ) {
    filename = "";
}

bool DMFile::IsOpen( ) {
    return filename != "";
}

void DMFile::ReadSlicesFromDisk(int start_slice, int end_slice, float* output_array) {
    MyDebugAssertTrue(start_slice == end_slice, "Can only read single slice at a time from DM files. Sorry");
    ReadSliceFromDisk(start_slice, output_array);
}

// Wanted_slice should be numbered from 0. It's assumed output_array is already allocated to correct dimensions
void DMFile::ReadSliceFromDisk(int wanted_slice, float* output_array) {
    MyDebugAssertTrue(wanted_slice == 0, "Bad start_slice: %i.\n", wanted_slice);
    MyDebugAssertTrue(wanted_slice < n, "Can't read slice %i. There are only %i.\n", wanted_slice, n);

    unsigned char* p;

    // Allocate a temporary array to hold data from disk
    p = new unsigned char[alloc_size( )];

    // Read data from disk
    readDM(filename, p, true, wanted_slice);

    // Apply mirror operation to achieve the same data layout as IMOD's dm2mrc
    long counter        = 0;
    long mirror_address = 0;
    int  i, j;
    for ( j = 0; j < int(sizeY( )); j++ ) {
        mirror_address = (sizeY( ) - 1 - j) * sizeX( );
        for ( i = 0; i < int(sizeX( )); i++ ) {
            output_array[counter] = reinterpret_cast<float*>(p)[mirror_address];
            counter++;
            mirror_address++;
        }
    }

    delete[] p;
}

/**
@brief	Reading a Digital Micrograph image file format.
@param	*p				the image structure.
@param 	readdata		flag to activate reading of image data.
@param 	img_select		image selection in multi-image file (-1 = all images).
@return	int				error code (<0 means failure).
A 2D/3D image format used with CCD cameras in electron microscopy.
	File format extensions:  	.dm, .DM
	Two types: Fixed format (new) and the Macintosh format (old)
	Fixed format:
		Header size:				24 bytes (fixed).
		Byte order determination:	An endian flag: Must be 65535 or swap everything
		Data types: 				many.
	Macintosh format: 			Hermitian
		Header size:				8 bytes (fixed).
		Byte order determination:	Big-endian
		Data types: 				many.
**/
int DMFile::readDM(wxString wanted_filename, unsigned char* p, bool readdata, int img_select) {
    std::ifstream* fimg = new std::ifstream(wanted_filename);
    if ( fimg->fail( ) )
        return -1;

    // Read a small block at the beginning to see if it is the fixed or tagged format
    char buf[1024];
    fimg->read(buf, 4);
    if ( fimg->fail( ) )
        return -2;

    version = *((int*)buf);

    if ( version > 100 )
        version = buf[3];

    if ( show == 1 )
        MyDebugPrint("Magic number = %i\n", version);

    fimg->seekg(0, std::ios::beg);

    switch ( version ) {
        case 0: readFixedDMHeader(fimg, p, readdata); break;
        case 3:
        case 4: readTagGroupWithVersion(fimg, p, readdata, img_select); break;
        default:
            MyDebugAssertFalse(true, "Digital Micrograph format version %i not supported!\n", version);
            wxPrintf("Digital Micrograph format version %i not supported!\n", version);
            DEBUG_ABORT;
    }

    fimg->close( );
    delete fimg;

    return 0;
}

int DMFile::readFixedDMHeader(std::ifstream* fimg, unsigned char* p, bool readdata) {
    DMhead*    header    = new DMhead;
    DMMachead* macheader = (DMMachead*)header;

    //	if ( fread( header, sizeof(DMhead), 1, fimg ) < 1 ) return -2;
    fimg->read((char*)header, sizeof(DMhead));
    if ( fimg->fail( ) )
        return -2;

    if ( show )
        MyDebugPrint("DEBUG readFixedDMHeader: macheader= %i width = %i height = %i\n", macheader, macheader->width, macheader->height);

    data_offset(8);
    if ( (macheader->width < 1) || (macheader->height < 1) ) {
        macheader = (DMMachead*)((char*)header + 6);
        data_offset(data_offset( ) + 6);
    }

    if ( show )
        MyDebugPrint("DEBUG readFixedDMHeader: macheader= %i width = %i height = %i\n", macheader, macheader->width, macheader->height);

    // Determine header type
    int i, fixed(1), sb(0);
    /*	if ( macheader->width == 0 && macheader->height == -1 ) {
		fixed = 1;
		sb = 0;
	} else if ( macheader->width == -1 && macheader->height == 0 ) {
		fixed = 1;
		sb = 1;
	} else if ( systype(0) >= LittleIEEE ) sb = 1;
*/
    // Swap bytes if necessary
    unsigned char* b = (unsigned char*)header;
    if ( sb ) {
        if ( fixed ) {
            if ( show )
                MyDebugPrint("Warning: Swapping header byte order for 4-byte types\n");
            for ( i = 0; i < 24; i += 4 )
                swapbytes(b + i, 4);
        }
        else {
            b = (unsigned char*)macheader;
            if ( show )
                MyDebugPrint("Warning: Swapping header byte order for 2-byte types\n");
            for ( i = 0; i < 8; i += 2 )
                swapbytes(b + i, 2);
        }
    }

    // Map the parameters
    images(1);
    channels(1);
    compound_type(TSimple);
    if ( fixed ) {
        size(header->xSize, header->ySize, header->zSize);
        data_offset(24);
        datatype_from_dm3_type(header->type);
    }
    else {
        size(macheader->width, macheader->height, 1);
        switch ( macheader->type ) {
            case 6: data_type(UCharacter); break;
            case 14: data_type(UCharacter); break;
            case 9: data_type(SCharacter); break;
            case 10: data_type(UShort); break;
            case 1: data_type(Short); break;
            case 7: data_type(Integer); break;
            case 2: data_type(Float); break;
            case 3:
                data_type(Float);
                compound_type(TComplex);
                channels(2);
                break;
            case 13:
                data_type(Double);
                compound_type(TComplex);
                channels(2);
                break;
            default: data_type(UCharacter);
        }
    }

    //p->image = new Bsub_image[p->images()];

    delete header;

    if ( readdata ) {
        MyDebugPrintWithDetails("About to read %li bytes\n", long(alloc_size( )));
        fimg->read((char*)p, alloc_size( ));
        if ( fimg->fail( ) )
            return -3;
        if ( sb )
            swapbytes(alloc_size( ), p, data_type_size( ));
    }

    fimg->close( );

    return sb;
}

/*
	Main DM3/4 file database:

	Arrangement:
		The file header (TagGroupWithVersion) contains 4 items:
		Version: 1-4 (4 byte)
		Size of contained data (TagGroupData): v1-v3 (4 byte), v4 (8 byte)
		Endianness: 0,1 (4 byte)
		Content (TagGroupData)

	Important image information:
		Main block: "ImageList"
			May contain more than one image, usually a small display version
			followed by the real data
		Important elements:
			Pixel size (nm):		ImageData/Calibrations/Dimension/Scale
			Data element type:		ImageData/DataType
			Image size:				ImageData/Dimensions
			Data element size:		ImageData/PixelDepth
			Camera pixel size (um):	ImageTags/Acquisition/Device/CCD/Pixel Size (um)
			Dose rate:				ImageTags/Calibration/Dose rate/Calibration
			Magnification:			ImageTags/Microscope Info/Actual Magnification
			Acceleration voltage:	ImageTags/Microscope Info/Voltage
*/
int DMFile::readTagGroupWithVersion(std::ifstream* fimg, unsigned char* p, bool readdata, int img_select) {
    //if ( verbose & VERB_DEBUG_DM ) show = 2;

    keep = level = 0;

    size_t file_length(0), val(0);
    char   buf[1024];

    fimg->read(buf, 4);
    if ( fimg->fail( ) )
        return -2;

    version = *((int*)buf);

    if ( version > 100 ) {
        sb      = 1;
        version = buf[3];
    }

    file_length = dm_read_integer(fimg, sizeof(size_t)); // File length
    file_length += 16;

    endianness = dm_read_integer(fimg, sizeof(unsigned int)); // Endianness

    if ( show == 1 ) {
        if ( sb )
            std::cout << "Warning: Swapping header bytes" << std::endl;
        std::cout << "Version: " << version << std::endl;
        std::cout << "File length: " << file_length << std::endl;
        std::cout << "Endianness: " << endianness << std::endl;
    }

    /*
	if ( file_length <= 16 ) {
		std::cout << "Error: file length = " << file_length << endl;
		return error_show("File length specifier incorrect!\n", __FILE__, __LINE__);
	}
	*/
    MyDebugAssertTrue(file_length > 16, "Error: file length specifier incorrect. File length = %i\n", file_length);

    if ( show == 2 ) {
        std::cout << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
        std::cout << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">" << std::endl;
        std::cout << "<plist version=\"1.0\">" << std::endl;
    }

    // Set up default parameters
    size(1, 1, 1);
    images(1);
    channels(1);

    readTagGroupData(fimg, 0, p, readdata);

    val = dm_read_integer(fimg, sizeof(unsigned int)); // 500
    val = dm_read_integer(fimg, sizeof(unsigned int)); // Label size
    if ( val ) {
        fimg->read(buf, val);
        if ( show == 2 )
            std::cout << "<" << buf << "/>" << std::endl;
    }

    if ( show == 1 )
        std::cout << std::endl;
    if ( show == 2 )
        std::cout << "</plist>" << std::endl;

    if ( img_select >= 0 ) {
        data_offset(data_offset( ) + img_select * sizeX( ) * sizeY( ) * sizeZ( ) * channels( ) * data_type_size( ));
        images(1);
    }

    /*
	if ( !p->image ) {
		if ( images() < 1 ) images(1);
		p->image = new Bsub_image[p->images()];
	}
	*/

    if ( show_scale( ) != 1 ) {
        sampling(samplingX( ) / show_scale( ), samplingY( ) / show_scale( ), 1);
        show_scale(1);
    }

    if ( readdata ) {
        //MyDebugPrintWithDetails("About to read %li bytes\n",long(alloc_size()));
        fimg->seekg(data_offset( ), std::ios_base::beg);
        //p->data_alloc();
        //fimg->read((char *)p->data_pointer(), p->alloc_size());
        fimg->read((char*)p, alloc_size( ));
    }

    return version;
}

// TagGroupData contains 3 items
// Sorted flag: 0,1; v3-v4 (1 byte)
// Open flag: 0,1; v3-v4 (1 byte)
// Number of records (TagEntry/TagSubGroup): v3 (4 byte), v4 (8 byte)
// Content (TagGroupData)
int DMFile::readTagGroupData(std::ifstream* fimg, int dim_flag, unsigned char* p, bool readdata) {
    unsigned char sorted, open;
    size_t        i, ntag(0);
    int           notag(0);

    *fimg >> sorted;
    *fimg >> open;

    ntag = dm_read_integer(fimg, sizeof(size_t)); // Number of tags

    if ( show == 1 )
        std::cout << "\tntag=" << ntag;
    if ( show == 2 ) {
        for ( i = 0; i < level; i++ )
            std::cout << "\t";
        std::cout << "<dict>" << std::endl;
    }

    for ( i = 0; i < ntag; i++ ) {
        readTag(fimg, dim_flag, p, readdata, notag);
        if ( dim_flag )
            dim_flag++;
    }

    if ( show == 2 ) {
        for ( i = 0; i < level; i++ )
            std::cout << "\t";
        std::cout << "</dict>" << std::endl;
    }

    return 0;
}

/*
	Image data is assigned to the data pointer based on the keep flag
	Associated properties are also kept
*/
int DMFile::readTag(std::ifstream* fimg, int dim_flag, unsigned char* p, bool readdata, int& notag) {
    unsigned char  t, tag[128];
    unsigned short len;
    size_t         i, j, k, data_size(0);
    size_t         nnum, size(0), nstr, narr, nel, dt, dtarr[128], dtlen; // Change in v4
    double         val, arr[256];
    char           buf[1024];

    level++;

    // Common part of TagSubGroup and TagEntry records
    // Selector (1 byte): TagSubGroup=20, TagEntry=21
    // Length of label (2 bytes)
    // Label
    *fimg >> t;
    len = dm_read_integer(fimg, sizeof(unsigned short));
    //	cout << "\tlen=" << len << endl;

    if ( len > 128 ) {
        std::cout << "\tlen=" << len << std::endl;
        std::cerr << "Error: tag length too long!" << std::endl;
        DEBUG_ABORT;
    }

    if ( len ) {
        fimg->read((char*)tag, len);
        tag[len] = 0;
        tag_convert(tag);
        notag = 0;
    }
    else {
        snprintf((char*)tag, 128, "%d", notag);
        notag++;
    }

    if ( show == 1 ) {
        std::cout << std::endl;
        for ( i = 0; i < level; i++ )
            std::cout << "\t";
        std::cout << "Tag=" << (int)t << "\tlen=" << len << "\t" << tag;
    }
    if ( show == 2 ) {
        for ( i = 0; i < level; i++ )
            std::cout << "\t";
        std::cout << "<key>" << tag << "</key>" << std::endl;
    }

    if ( version == 4 ) { // Changed in v4
        size = dm_read_integer(fimg, sizeof(size_t)); // Size of data type & data
        if ( show == 1 )
            std::cout << "\tsize=" << size;
    }

    if ( t == 20 ) { // TagSubGroup record
        if ( strcmp((char*)tag, "Dimensions") == 0 )
            dim_flag = 1;
        readTagGroupData(fimg, dim_flag, p, readdata);
    }
    else if ( t == 21 ) { // TagEntry record
        fimg->read((char*)buf, 4); // String = "%%%%"
        buf[4] = 0;
        nnum   = dm_read_integer(fimg, sizeof(size_t)); // Number of integers in array
        if ( show == 1 )
            std::cout << "\t" << std::setw(4) << buf << "\tnnum=" << nnum;

        for ( i = 0; i < nnum; i++ ) {
            dtarr[i] = dm_read_integer(fimg, sizeof(size_t)); // Data type array
            if ( show == 1 )
                std::cout << "\tdt[" << i << "]=" << dtarr[i];
        }
        if ( show == 1 )
            std::cout << std::endl;

        dt = dtarr[0];
        if ( dt <= 12 ) {
            val = dm3_value(fimg, dt);
        }
        else if ( dt == 15 ) { // Struct
            narr = dtarr[2];
            nel  = 1;
            if ( show == 1 )
                std::cout << "\tstruct=" << narr << "x" << nel;
            if ( show == 2 ) {
                for ( i = 0; i < level; i++ )
                    std::cout << "\t";
                std::cout << "<array>" << std::endl;
            }
            level++;
            for ( k = 0; k < narr; k++ )
                arr[k] = dm3_value(fimg, dtarr[4 + 2 * k]);
            level--;
            if ( show == 2 ) {
                for ( i = 0; i < level; i++ )
                    std::cout << "\t";
                std::cout << "</array>" << std::endl;
            }
        }
        else if ( dt == 18 ) { // String
            fimg->read((char*)buf, dtarr[1]);
            if ( show == 1 )
                std::cout << "\tstrlen=" << dtarr[1];
            if ( show == 2 ) {
                for ( i = 0; i < level; i++ )
                    std::cout << "\t";
                std::cout << "<string>" << buf << "</string>" << std::endl;
            }
        }
        else if ( dt == 20 ) { // Data array
            if ( dtarr[1] == 15 )
                narr = dtarr[3];
            else
                narr = 1;
            nel = dtarr[nnum - 1]; // Number of elements
            if ( show == 1 )
                std::cout << "\tarr=" << narr << "x" << nel;
            if ( show == 2 ) {
                for ( i = 0; i < level; i++ )
                    std::cout << "\t";
                std::cout << "<array>" << std::endl;
            }
            level++;
            if ( dtarr[1] == 15 ) { // Struct
                if ( show == 1 )
                    std::cout << "\tstruct=" << narr << "x" << nel;
                if ( show == 2 ) {
                    for ( i = 0; i < level; i++ )
                        std::cout << "\t";
                    std::cout << "<array>" << std::endl;
                }
                level++;
                for ( i = 0; i < nel; i++ )
                    for ( k = 0; k < narr; k++ )
                        arr[k] = dm3_value(fimg, dtarr[5 + 2 * k]);
                level--;
                if ( show == 2 ) {
                    for ( i = 0; i < level; i++ )
                        std::cout << "\t";
                    std::cout << "</array>" << std::endl;
                }
            }
            else {
                narr      = 1;
                dtlen     = dm3_type_length(dtarr[1]);
                data_size = nel * dtlen;
                // The big image data block is not printed for the plist format
                if ( strcmp((char*)tag, "Data") == 0 && nel > 1e6 ) {
                    keep = 1;
                    if ( show == 1 )
                        std::cout << "\tdata_size=" << data_size;
                    //					if ( readdata ) {
                    //						p->data_alloc(data_size);
                    //						fimg->read((char *)p->data_pointer(), data_size);
                    //					} else {
                    //						fimg->seekg(data_size, ios_base::cur);
                    //					}
                    data_offset(fimg->tellg( ));
                    fimg->seekg(data_size, std::ios_base::cur);
                }
                else {
                    for ( i = 0; i < nel; i++ ) {
                        if ( i < 10 )
                            val = dm3_value(fimg, dtarr[1]); // Element value
                        else
                            fimg->read((char*)buf, dtlen); // Element value
                    }
                }
            }
            level--;
            if ( show == 2 ) {
                for ( i = 0; i < level; i++ )
                    std::cout << "\t";
                std::cout << "</array>" << std::endl;
            }
        }
        else {
            std::cerr << "Error: Data type " << dt << " not defined!" << std::endl;
        }

        if ( keep && strcmp((char*)tag, "ImageData") == 0 )
            keep = 0;

        if ( keep ) {
            if ( strcmp((char*)tag, "DataType") == 0 ) {
                datatype_from_dm3_type((DMDataType)val);
                if ( show == 1 )
                    std::cout << "\tdatatype=" << data_type( ) << "\tcompoundtype=" << compound_type( ) << "\tc=" << channels( ) << "\tval=" << val;
            }
            if ( strcmp((char*)tag, "Minimum Value (counts)") == 0 ) {
                minimum(val);
            }
            if ( strcmp((char*)tag, "Maximum Value (counts)") == 0 ) {
                maximum(val);
            }
            if ( dim_flag == 1 ) {
                sizeX((size_t)val);
                dim_flag = 2;
            }
            else if ( dim_flag == 2 ) {
                sizeY((size_t)val);
                dim_flag = 3;
            }
            else if ( dim_flag == 3 ) {
                images((size_t)val);
                dim_flag = 0;
                if ( show == 1 )
                    std::cout << "\tx=" << sizeX( ) << "\ty=" << sizeY( ) << "\tn=" << images( );
            }
            if ( strcmp((char*)tag, "PixelDepth") == 0 ) {
                if ( show == 1 )
                    std::cout << "\t" << tag << "=" << val << std::endl;
            }
            if ( strcmp((char*)tag, "Pixel Size (um)") == 0 ) {
                sampling(arr[0], arr[1], 1);
                if ( show == 1 )
                    std::cout << "\t" << tag << "=" << samplingX( ) << samplingY( ) << samplingZ( ) << std::endl;
                //				cout << tab << tag << "=" << p->sampling() << endl;
            }
            if ( strcmp((char*)tag, "Actual Magnification") == 0 ) {
                show_scale(val / 1e4);
                if ( show == 1 )
                    std::cout << "\t" << tag << "=" << val << std::endl;
                //				cout << tab << tag << "=" << val << endl;
            }
            if ( strcmp((char*)tag, "Pixel Upsampling") == 0 ) {
                if ( show == 1 )
                    std::cout << "\t"
                              << "\t"
                              << "=" << arr[0] << "x" << arr[1] << std::endl;
            }
            if ( strcmp((char*)tag, "SourceSize_Pixels") == 0 ) {
                if ( show == 1 )
                    std::cout << "\t"
                              << "\t"
                              << "=" << arr[0] << "x" << arr[1] << std::endl;
            }
            //			if ( strcmp(tag, "ImageIndex") == 0 ) {
            //				if ( show == 1 ) cout << tab << tag << "=" << val << endl;
            //			}
            if ( strstr((char*)tag, "Emission") ) {
                if ( show == 1 ) {
                    std::cout << "\t" << tag << "=" << val << std::endl;
                    for ( i = 0; i < len; i++ )
                        std::cout << (int)tag[i] << "\t" << tag[i] << std::endl;
                }
            }
        }
    }
    else {
        std::cerr << "Error: Undefined tag type! (" << t << ")" << std::endl;
    }

    level--;

    return 0;
}

double DMFile::dm3_value(std::ifstream* fimg, int dm3_type) {
    int dtlen = dm3_type_length(dm3_type);
    if ( dtlen < 1 )
        return 0;

    long   i, ivalue(0);
    double dvalue(0);
    char   buf[1024];

    fimg->read(buf, dtlen);

    if ( sb - endianness )
        swapbytes((unsigned char*)buf, dtlen);

    switch ( dm3_type ) {
        case 2: ivalue = *((short*)buf); break;
        case 3: ivalue = *((int*)buf); break;
        case 4: ivalue = *((unsigned short*)buf); break;
        case 5: ivalue = *((unsigned int*)buf); break;
        case 6: dvalue = *((float*)buf); break;
        case 7: dvalue = *((double*)buf); break;
        case 8: ivalue = *buf; break;
        case 9: ivalue = *buf; break;
        case 10: ivalue = *buf; break;
        case 11: ivalue = *((long*)buf); break;
        case 12: ivalue = *((unsigned long*)buf); break;
        default:
            std::cerr << "Error: Data type " << dm3_type << " not defined!" << std::endl;
    }

    if ( ! dvalue )
        dvalue = ivalue;

    if ( show == 1 )
        std::cout << "\t" << dvalue;

    if ( show == 2 ) {
        for ( i = 0; i < level; i++ )
            std::cout << "\t";
        if ( dm3_type == 6 || dm3_type == 7 )
            std::cout << "<real>" << dvalue << "</real>" << std::endl;
        else
            std::cout << "<integer>" << ivalue << "</integer>" << std::endl;
    }

    return dvalue;
}

int DMFile::dm3_type_length(int dm3_type) {
    int dtlen = 0;

    switch ( dm3_type ) {
        case 2: dtlen = 2; break;
        case 3: dtlen = 4; break;
        case 4: dtlen = 2; break;
        case 5: dtlen = 4; break;
        case 6: dtlen = 4; break;
        case 7: dtlen = 8; break;
        case 8: dtlen = 1; break;
        case 9: dtlen = 1; break;
        case 10: dtlen = 1; break;
        case 11: dtlen = 8; break;
        case 12: dtlen = 8; break;
        default:
            std::cerr << "Error: Data type " << dm3_type << " length not defined!" << std::endl;
    }

    return dtlen;
}

unsigned long DMFile::dm_read_integer(std::ifstream* fimg, long len) {
    unsigned short sval(0);
    unsigned int   ival(0);
    unsigned long  lval(0);
    char           buf[1024];

    if ( version < 4 && len > 4 )
        len = 4;

    fimg->read(buf, len);

    if ( sb )
        swapbytes((unsigned char*)buf, len);

    switch ( len ) {
        case 2:
            sval = *((unsigned short*)buf);
            lval = sval;
            break;
        case 4:
            ival = *((unsigned int*)buf);
            lval = ival;
            break;
        case 8: lval = *((unsigned long*)buf); break;
        default: lval = *buf;
    }

    return lval;
}

int DMFile::tag_convert(unsigned char* tag) {
    for ( ; *tag; tag++ )
        if ( *tag > 127 )
            *tag -= 64;

    return 0;
}

DataType DMFile::datatype_from_dm3_type(DMDataType dm3_type) {
    if ( show )
        MyDebugPrint("\tDMDataType= %i\n", dm3_type);

    compound_type(TSimple);
    channels(1);

    DataType datatype = Unknown_Type;

    switch ( dm3_type ) {
        case BINARY_DATA: datatype = UCharacter; break;
        case UNSIGNED_INT8_DATA: datatype = UCharacter; break;
        case SIGNED_INT8_DATA: datatype = SCharacter; break;
        case RGB_DATA:
        case RGB_UINT8_DATA:
            datatype = UCharacter;
            channels(3);
            compound_type(TRGB);
            break;
        case OS_RGBA_UINT8_DATA:
            datatype = UCharacter;
            channels(4);
            compound_type(TRGBA);
            break;
        case UNSIGNED_INT16_DATA: datatype = UShort; break;
        case RGB_UINT16_DATA:
            datatype = UShort;
            channels(3);
            compound_type(TRGB);
            break;
        case RGBA_UINT16_DATA:
            datatype = UShort;
            channels(4);
            compound_type(TRGBA);
            break;
        case SIGNED_INT16_DATA: datatype = Short; break;
        case UNSIGNED_INT32_DATA: datatype = UInteger; break;
        case SIGNED_INT32_DATA: datatype = Integer; break;
        case REAL4_DATA: datatype = Float; break;
        case RGBA_FLOAT32_DATA:
            datatype = Float;
            channels(4);
            compound_type(TRGBA);
            break;
        case REAL8_DATA: datatype = Double; break;
        case RGB_FLOAT64_DATA:
            datatype = Double;
            channels(3);
            compound_type(TRGB);
            break;
        case RGBA_FLOAT64_DATA:
            datatype = Double;
            channels(4);
            compound_type(TRGBA);
            break;
        case COMPLEX8_DATA:
            datatype = Float;
            compound_type(TComplex);
            break;
        case COMPLEX16_DATA:
            datatype = Double;
            compound_type(TComplex);
            break;
        default: datatype = UCharacter;
    }

    data_type(datatype);
    if ( compound_type( ) == TComplex )
        channels(2);

    return datatype;
}

/**
@brief 	Returns the size of the datatype in bytes.
@return size_t			data type size.

	The Bit type returns 1.

**/
size_t DMFile::data_type_size( ) {
    size_t typesize(0);

    switch ( datatype ) {
        case Bit:
        case UCharacter:
        case SCharacter: typesize = sizeof(char); break;
        case UShort:
        case Short: typesize = sizeof(short); break;
        case UInteger:
        case Integer: typesize = sizeof(int); break;
        case ULong:
        case Long: typesize = sizeof(long); break;
        case Float: typesize = sizeof(float); break;
        case Double: typesize = sizeof(double); break;
        default: typesize = 0;
    }

    return typesize;
}
