/* -----------------------------------------------------------------------
   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
   ----------------------------------------------------------------------- */
#include "plmbase_config.h"
#include <vector>
#include <fstream>
#include <iterator>

#include "mc_dose.h"
#include "plm_image.h"
#include "plm_int.h"
#include "print_and_exit.h"
#include "volume.h"
#include "xio_ct.h"
#include "xio_ct_transform.h"

/* Monte Carlo 3ddose files are generated by DOSXYZ, DPM and probably others.
   
   Block 1 - number of voxels in x,y,z directions (nx,ny,nz)
   Block 2 - voxel boundaries (cm) in x direction (nx+1 values)
   Block 3 - voxel boundaries (cm) in y direction (ny+1 values)
   Block 4 - voxel boundaries (cm) in z direction (nz+1 values)
   Block 5 - dose values 3D array (nx*ny*nz values)
   Block 6 - error values 3D array (nx*ny*nz values)
*/

typedef struct mc_dose_header MC_dose_header;
struct mc_dose_header {
    plm_long dim[3];
    float offset[3];
    float spacing[3];
    int header_size;
};

static void
mc_dose_load_header (MC_dose_header *mcdh, const char *filename)
{    
    std::ifstream input(filename);
    std::vector<float> header;
    int i;
    float value;

    if (!input) {
	print_and_exit ("Error opening file %s for read\n", filename);
    }

    /* Get dimensions */
    for (i = 0; i < 3; i++) {
	if (input >> value) {
	    header.push_back(value);
	} else {
	    print_and_exit ("Dose file %s header is invalid\n", filename);
	}
    }

    mcdh->dim[0] = header[0];
    mcdh->dim[1] = header[1];
    mcdh->dim[2] = header[2];

    mcdh->header_size = 3 + 3 + mcdh->dim[0] + mcdh->dim[1] + mcdh->dim[2];

    /* Read rest of header */
    for (i = 0; i < mcdh->header_size - 3; i++) {
	if (input >> value) {
	    header.push_back(value);
	} else {
	    print_and_exit ("Dose file %s is invalid\n", filename);
	}
    }

    /* Get offset */
    mcdh->offset[0] = header[3] * 10;
    mcdh->offset[1] = header[3 + 1 + mcdh->dim[0]] * 10;
    mcdh->offset[2] = header[3 + 2 + mcdh->dim[0] + mcdh->dim[1]] * 10;

    /* Get element spacing */
    /* NOTE: assuming uniform spacing! */
    mcdh->spacing[0] = (header[4] - header[3]) * 10;
    mcdh->spacing[1] = (header[4 + 1 + mcdh->dim[0]] -
	header[3 + 1 + mcdh->dim[0]]) * 10;
    mcdh->spacing[2] = (header[4 + 2 + mcdh->dim[0] + mcdh->dim[1]] - 
	header[3 + 2 + mcdh->dim[0] + mcdh->dim[1]]) * 10;

    input.close();
}

static void
mc_dose_load_cube (
    Plm_image *pli,
    MC_dose_header *mcdh,
    const char *filename
)
{
    std::ifstream input(filename);
    float value;

    Volume *v;
    float *cube_img_read;

    v = pli->get_vol ();
    cube_img_read = (float*) v->img;

    if (!input) {
	print_and_exit ("Error opening file %s for read\n", filename);
    }

    /* Skip header */
    for (int i=0; i < mcdh->header_size; i++) {
	if (!(input >> value)) {
	    print_and_exit ("Dose file %s is invalid\n", filename);
	}
    }

    /* Read dose cube */
    for (plm_long z=0; z < mcdh->dim[2]; z++) {
	for (plm_long y=0; y < mcdh->dim[1]; y++) {
	    for (plm_long x=0; x < mcdh->dim[0]; x++) {
		if (input >> value) {
		    cube_img_read[x + y*mcdh->dim[0]
			+ z*mcdh->dim[0]*mcdh->dim[1]] = value;
		} else {
		    print_and_exit ("Dose file %s is invalid\n", filename);
		}
	    }
	}
    }

    input.close();
}

static void
mc_dose_create_volume (
    Plm_image *pli, 
    MC_dose_header *mcdh
)
{
    Volume *v;

    v = new Volume (mcdh->dim, mcdh->offset, mcdh->spacing, 0, 
	PT_FLOAT, 1);
    pli->set_volume (v);

    printf ("img: %p\n", v->img);
    printf ("Image dim: %u %u %u\n", (unsigned int) v->dim[0], 
	(unsigned int) v->dim[1], (unsigned int) v->dim[2]);
}

void
mc_dose_load (Plm_image *pli, const char *filename)
{
    MC_dose_header mcdh;
    
    mc_dose_load_header(&mcdh, filename);
    mc_dose_create_volume(pli, &mcdh);
    mc_dose_load_cube(pli, &mcdh, filename);
}

void
mc_dose_apply_transform (Plm_image *pli, Xio_ct_transform *transform)
{
    /* Transform coordinates of MC dose cube to DICOM coordinates */

    Volume *v = pli->get_vol ();

    /* Set offsets */
    v->offset[0] = (v->offset[0] * transform->direction_cosines[0]) + transform->x_offset;
    v->offset[1] = (v->offset[1] * transform->direction_cosines[4]) + transform->y_offset;

    /* Set direction cosines */
    v->set_direction_cosines (transform->direction_cosines);
}
