/* ----------------------------------------------------------------------------
@COPYRIGHT  :
              Copyright 1993,1994,1995 David MacDonald,
              McConnell Brain Imaging Centre,
              Montreal Neurological Institute, McGill University.
              Permission to use, copy, modify, and distribute this
              software and its documentation for any purpose and without
              fee is hereby granted, provided that the above copyright
              notice appear in all copies.  The author and McGill University
              make no representations about the suitability of this
              software for any purpose.  It is provided "as is" without
              express or implied warranty.
---------------------------------------------------------------------------- */

#include  <internal_volume_io.h>
#include  <minc.h>

#ifndef lint
static char rcsid[] = "$Header: /software/source/minc/cvsroot/minc/volume_io/Volumes/input_free.c,v 1.29 2004/10/04 20:23:52 bert Exp $";
#endif

#define  DEFAULT_SUFFIX  "fre"

#define  NUM_BYTE_VALUES      256

static  Status  input_slice(
    volume_input_struct   *volume_input );

/* ----------------------------- MNI Header -----------------------------------
@NAME       : initialize_free_format_input
@INPUT      : 
@OUTPUT     : volume
              volume_input
@RETURNS    : OK if successful
@DESCRIPTION: Initializes loading a free format file by reading the header.
              If the file contains short, but the convert_to_byte flag is set
              in volume_input, then the file is converted to bytes on input.
              This function assumes that volume->filename has been assigned.
@CREATED    :                      David MacDonald
@MODIFIED   : May  22, 1997   D. MacDonald  - now uses volume starts
---------------------------------------------------------------------------- */

VIOAPI  Status  initialize_free_format_input(
    STRING               filename,
    Volume               volume,
    volume_input_struct  *volume_input )
{
    Status         status, file_status;
    STRING         volume_filename, abs_volume_filename, slice_filename;
    int            sizes[N_DIMENSIONS];
    int            c, volume_byte_offset, int_size;
    int            n_bytes_per_voxel, min_value, max_value, i;
    int            n_slices, n_voxels_in_slice;
    long           slice;
    nc_type        desired_data_type;
    int            value;
    char           ch;
    Real           file_separations[MAX_DIMENSIONS];
    Real           volume_separations[MAX_DIMENSIONS];
    Real           trans[N_DIMENSIONS];
    FILE           *file;
    BOOLEAN        axis_valid;
    int            axis;

    status = OK;

    file_status = open_file_with_default_suffix(
                         filename, DEFAULT_SUFFIX,
                         READ_FILE, ASCII_FORMAT, &file );

    status = file_status;

    abs_volume_filename = NULL;

    /* read the line containing: n_bytes_per_voxel
    */

    if( status == OK )
        status = input_int( file, &n_bytes_per_voxel );

    /* input the 3 translation values used for the voxel_to_world_transform */

    if( status == OK &&
        (input_real( file, &trans[X] ) != OK ||
         input_real( file, &trans[Y] ) != OK ||
         input_real( file, &trans[Z] ) != OK) )
    {
        print_error( "Error reading x,y,z translations from %s.\n", filename );
        status = ERROR;
    }

    if( status == OK )
    {
        /* decide what type of data is in image file */

        if( n_bytes_per_voxel == 1 )
            volume_input->file_data_type = UNSIGNED_BYTE;
        else if( n_bytes_per_voxel == 2 )
            volume_input->file_data_type = UNSIGNED_SHORT;
        else
        {
            print_error( "Must be either 1 or 2 bytes per voxel.\n" );
            status = ERROR;
        }

        /* decide how to store data in memory */

        if( get_volume_data_type(volume) == NO_DATA_TYPE )
            desired_data_type = NC_BYTE;
        else
            desired_data_type = volume->nc_data_type;
    }

    /* read 3 lines, 1 for each axis:

            number_voxels   +/-voxel_separation  x|y|z

       where the x, y, or z is used to indicate the ordering of the axes
             within the file, with the 3rd one being the fastest varying index.
             negative voxel separation means flip on display 
    */

    if( volume->spatial_axes[X] < 0 ||
        volume->spatial_axes[Y] < 0 ||
        volume->spatial_axes[Z] < 0 )
    {
        print_error(
         "warning initialize_free_format_input: setting spatial axes to XYZ.\n");
        volume->spatial_axes[X] = 0;
        volume->spatial_axes[Y] = 1;
        volume->spatial_axes[Z] = 2;
    }

    if( status == OK )
    for_less( axis, 0, N_DIMENSIONS )
    {
        status = ERROR;

        if( input_int( file, &int_size ) != OK )
            break;

        volume_input->sizes_in_file[axis] = (long) int_size;

        if( input_real( file, &file_separations[axis] ) != OK )
            break;

        if( input_nonwhite_character( file, &ch ) != OK )
            break;

        axis_valid = TRUE;

        switch( ch )
        {
        case 'x':
        case 'X':  volume_input->axis_index_from_file[axis] =
                                           volume->spatial_axes[X];
                   break;

        case 'y':
        case 'Y':  volume_input->axis_index_from_file[axis] =
                                           volume->spatial_axes[Y];
                   break;

        case 'z':
        case 'Z':  volume_input->axis_index_from_file[axis] =
                                           volume->spatial_axes[Z];
                   break;

        default:   axis_valid = FALSE;    break;
        }

        if( !axis_valid )
        {
            print_error( "Invalid axis.\n" );
            break;
        }

        status = OK;
    }

    for_less( c, 0, N_DIMENSIONS )
    {
        volume->spatial_axes[c] = c;
        volume->direction_cosines[c][X] = 0.0;
        volume->direction_cosines[c][Y] = 0.0;
        volume->direction_cosines[c][Z] = 0.0;
        volume->direction_cosines[c][c] = 1.0;
    }

    if( status == OK &&
        (volume_input->axis_index_from_file[X] ==
         volume_input->axis_index_from_file[Y] ||
         volume_input->axis_index_from_file[X] ==
         volume_input->axis_index_from_file[Z] ||
         volume_input->axis_index_from_file[Y] ==
         volume_input->axis_index_from_file[Z]) )
    {
        print_error( "Two axis indices are equal.\n" );
        status = ERROR;
    }

    volume_input->directory = extract_directory( filename );

    if( status == OK )
    if( volume_input->sizes_in_file[0] <= 0 )
    {
        n_slices = 0;

        while( input_string( file, &slice_filename, (char) ' ' )==OK)
        {
            SET_ARRAY_SIZE( volume_input->slice_filenames, n_slices, n_slices+1,
                            DEFAULT_CHUNK_SIZE );
            volume_input->slice_filenames[n_slices] = slice_filename;
            SET_ARRAY_SIZE( volume_input->slice_byte_offsets,
                            n_slices, n_slices+1, DEFAULT_CHUNK_SIZE );

            if( input_int( file, &volume_input->slice_byte_offsets[n_slices] )
                != OK )
            {
                volume_input->slice_byte_offsets[n_slices] = 0;
            }

            ++n_slices;
        }

        volume_input->sizes_in_file[0] = (long) n_slices;
        volume_input->one_file_per_slice = TRUE;
    }
    else
    {
        volume_input->one_file_per_slice = FALSE;
        status = input_string( file, &volume_filename, (char) ' ' );
        abs_volume_filename = get_absolute_filename( volume_filename,
                                                     volume_input->directory );
        delete_string( volume_filename );

        if( input_int( file, &volume_byte_offset ) != OK )
            volume_byte_offset = 0;

    }

    if( status == OK )
        status = close_file( file );

    /* record the information in the volume struct */

    if( status == OK )
    {
        for_less( axis, 0, N_DIMENSIONS )
        {
            sizes[volume_input->axis_index_from_file[axis]] =
                                 (int) volume_input->sizes_in_file[axis];
            volume_separations[volume_input->axis_index_from_file[axis]] =
                                                     file_separations[axis];

            if( volume_separations[axis] < 0.0 )
            {
                trans[axis] += -volume_separations[axis] *
                               (Real) (sizes[axis]-1);
            }
        }

        set_volume_separations( volume, volume_separations );
        set_volume_starts( volume, trans );
    }

    set_volume_type( volume, desired_data_type, FALSE, 0.0, 0.0 );
    set_volume_sizes( volume, sizes );

    /* allocate the slice buffer */

    n_voxels_in_slice = (int) volume_input->sizes_in_file[1] *
                        (int) volume_input->sizes_in_file[2];

    if( status == OK )
    switch( volume_input->file_data_type )
    {
    case  UNSIGNED_BYTE:
        ALLOC( volume_input->byte_slice_buffer, n_voxels_in_slice );
        break;

    case  UNSIGNED_SHORT:
        ALLOC( volume_input->short_slice_buffer, n_voxels_in_slice );
        break;

    default:
        handle_internal_error( "initialize_free_format_input" );
        break;
    }

    /* if the data must be converted to byte, read the entire image file simply
       to find the max and min values, and set the value_scale and
       value_translation */

    if( status == OK &&
        get_volume_data_type(volume) != volume_input->file_data_type )
    {
        if( status == OK && !volume_input->one_file_per_slice )
        {
            status = open_file( abs_volume_filename, READ_FILE, BINARY_FORMAT,
                                &volume_input->volume_file );

            if( status == OK )
                status = set_file_position( file, (long) volume_byte_offset );
        }

        min_value = 0;
        max_value = 0;

        volume_input->slice_index = 0;

        for_less( slice, 0, volume_input->sizes_in_file[0] )
        {
            (void) input_slice( volume_input );

            for_less( i, 0, n_voxels_in_slice )
            {
                value = (int) volume_input->short_slice_buffer[i];
                if( slice == 0 && i == 0 || value < min_value )
                    min_value = value;
                if( slice == 0 && i == 0 || value > max_value )
                    max_value = value;
            }
        }

        set_volume_voxel_range( volume, (Real) min_value, (Real) max_value );

        if( status == OK && !volume_input->one_file_per_slice )
            status = close_file( volume_input->volume_file );
    }

    if( status == OK && !volume_input->one_file_per_slice )
    {
        status = open_file( abs_volume_filename, READ_FILE, BINARY_FORMAT,
                            &volume_input->volume_file );

        delete_string( abs_volume_filename );

        if( status == OK )
            status = set_file_position( file, (long) volume_byte_offset );
    }

    volume_input->slice_index = 0;

    delete_string( abs_volume_filename );

    return( status );
}

/* ----------------------------- MNI Header -----------------------------------
@NAME       : delete_free_format_input
@INPUT      : volume_input
@OUTPUT     : 
@RETURNS    : 
@DESCRIPTION: Frees the slice buffer and closes the image file.
@CREATED    :                      David MacDonald
@MODIFIED   : 
---------------------------------------------------------------------------- */

VIOAPI  void  delete_free_format_input(
    volume_input_struct   *volume_input )
{
    long   slice;

    if( volume_input->file_data_type == UNSIGNED_BYTE )
    {
        FREE( volume_input->byte_slice_buffer );
    }
    else
    {
        FREE( volume_input->short_slice_buffer );
    }

    delete_string( volume_input->directory );

    if( volume_input->one_file_per_slice )
    {
        for_less( slice, 0, volume_input->sizes_in_file[0] )
            delete_string( volume_input->slice_filenames[slice] );

        FREE( volume_input->slice_filenames );
        FREE( volume_input->slice_byte_offsets );
    }
    else
    {
        (void) close_file( volume_input->volume_file );
    }
}

/* ----------------------------- MNI Header -----------------------------------
@NAME       : input_slice
@INPUT      : volume
              volume_input
@OUTPUT     : 
@RETURNS    : OK if success
@DESCRIPTION: Reads the next slice from the volume.
@CREATED    :                      David MacDonald
@MODIFIED   : 
---------------------------------------------------------------------------- */

static  Status  input_slice(
    volume_input_struct   *volume_input )
{
    Status           status;
    FILE             *file;
    STRING           slice_filename;

    status = OK;

    if( (long) volume_input->slice_index < volume_input->sizes_in_file[0] )
    {
        if( volume_input->one_file_per_slice )
        {
            slice_filename = get_absolute_filename(
                   volume_input->slice_filenames[volume_input->slice_index],
                   volume_input->directory );
            status = open_file( slice_filename, READ_FILE, BINARY_FORMAT,
                                &file );

            delete_string( slice_filename );

            if( status == OK )
                status = set_file_position( file,
                           (long) (volume_input->slice_byte_offsets
                                           [volume_input->slice_index]) );
        }
        else
            file = volume_input->volume_file;

        if( status == OK )
        switch( volume_input->file_data_type )
        {
        case  UNSIGNED_BYTE:
            status = io_binary_data( file, READ_FILE,
                                 (void *) volume_input->byte_slice_buffer,
                                 sizeof(volume_input->byte_slice_buffer[0]),
                                 (int) volume_input->sizes_in_file[1] *
                                 (int) volume_input->sizes_in_file[2] );
            break;

        case  UNSIGNED_SHORT:
            status = io_binary_data( file, READ_FILE,
                                 (void *) volume_input->short_slice_buffer,
                                 sizeof(volume_input->short_slice_buffer[0]),
                                 (int) volume_input->sizes_in_file[1] *
                                 (int) volume_input->sizes_in_file[2] );
            break;

        default:
            handle_internal_error( "input_slice" );
            break;
        }

        if( status == OK && volume_input->one_file_per_slice )
        {
            status = close_file( file );
        }

        ++volume_input->slice_index;
    }
    else
        status = ERROR;

    return( status );
}

/* ----------------------------- MNI Header -----------------------------------
@NAME       : input_more_free_format_file
@INPUT      : volume
              volume_input
@OUTPUT     : fraction_done
@RETURNS    : TRUE if more to input after this call
@DESCRIPTION: Reads in one more slice from the image file.
@CREATED    :                      David MacDonald
@MODIFIED   : 
---------------------------------------------------------------------------- */

VIOAPI  BOOLEAN  input_more_free_format_file(
    Volume                volume,
    volume_input_struct   *volume_input,
    Real                  *fraction_done )
{
    Real            min_value, max_value, value;
    int             x, y, z, sizes[MAX_DIMENSIONS];
    long            i;
    Status          status;
    BOOLEAN         more_to_do, scaling_flag;
    Real            value_translation, value_scale;
    Real            original_min_voxel, original_max_voxel;
    int             *inner_index, indices[MAX_DIMENSIONS];
    unsigned char   *byte_buffer_ptr;
    unsigned short  *short_buffer_ptr;

    if( (long) volume_input->slice_index < volume_input->sizes_in_file[0] )
    {
        if( !volume_is_alloced( volume ) )
            alloc_volume_data( volume );

        status = input_slice( volume_input );

        scaling_flag = FALSE;
        if( get_volume_data_type(volume) != volume_input->file_data_type )
        {
            scaling_flag = TRUE;
            get_volume_voxel_range( volume, &original_min_voxel,
                                    &original_max_voxel );
            value_translation = original_min_voxel;
            value_scale = (original_max_voxel - original_min_voxel) /
                          (Real) (NUM_BYTE_VALUES - 1);
        }

        inner_index = &indices[volume_input->axis_index_from_file[2]];

        indices[volume_input->axis_index_from_file[0]] =
                                         volume_input->slice_index-1;

        if( status == OK )
        switch( volume_input->file_data_type )
        {
        case  UNSIGNED_BYTE:
            byte_buffer_ptr = volume_input->byte_slice_buffer;
            for_less( i, 0, volume_input->sizes_in_file[1] )
            {
                indices[volume_input->axis_index_from_file[1]] = (int) i;
                for_less( *inner_index, 0, (int) volume_input->sizes_in_file[2] )
                {
                    if( scaling_flag )
                    {
                        value = ((Real) (*byte_buffer_ptr) - value_translation)/
                                   value_scale;

                        if( value < 0.0 )
                            value = 0.0;
                        else if( value > 255.0 )
                            value = 255.0;
                    }
                    else
                        value = (Real) (*byte_buffer_ptr);

                    set_volume_voxel_value( volume,
                               indices[X], indices[Y], indices[Z], 0, 0,
                               value );

                    ++byte_buffer_ptr;
                }
            }
            break;

        case  UNSIGNED_SHORT:
            short_buffer_ptr = volume_input->short_slice_buffer;
            for_less( i, 0, volume_input->sizes_in_file[1] )
            {
                indices[volume_input->axis_index_from_file[1]] = (int) i;
                for_less( *inner_index, 0, (int) volume_input->sizes_in_file[2])
                {
                    if( scaling_flag )
                    {
                        value = 
                         ((Real) (*short_buffer_ptr) - value_translation) /
                                               value_scale;
                    }
                    else
                        value = (Real) (*short_buffer_ptr);

                    set_volume_voxel_value( volume,
                               indices[X], indices[Y], indices[Z], 0, 0,
                               value );

                    ++short_buffer_ptr;
                }
            }
            break;

        default:
            handle_internal_error( "input_more_free_format_file" );
            break;
        }
    }

    get_volume_sizes( volume, sizes );

    *fraction_done = (Real) volume_input->slice_index /
                   (Real) sizes[volume_input->axis_index_from_file[0]];

    more_to_do = TRUE;

    if( volume_input->slice_index ==
        sizes[volume_input->axis_index_from_file[0]] )
    {
        more_to_do = FALSE;

        min_value = get_volume_voxel_value( volume, 0, 0, 0, 0, 0 );
        max_value = min_value;
        for_less( x, 0, sizes[X] )
        {
            for_less( y, 0, sizes[Y] )
            {
                for_less( z, 0, sizes[Z] )
                {
                    value = get_volume_voxel_value( volume, x, y, z, 0,0);
                    if( value < min_value )
                        min_value = value;
                    else if( value > max_value )
                        max_value = value;
                }
            }
        }

        set_volume_voxel_range( volume, min_value, max_value );

        if( get_volume_data_type(volume) != volume_input->file_data_type )
        {
            set_volume_real_range( volume, original_min_voxel,
                                   original_max_voxel );
        }
    }

    return( more_to_do );
}


syntax highlighted by Code2HTML, v. 0.9.1