#ifndef DICOMINFO_H_INCLUDED
#define DICOMINFO_H_INCLUDED
// 	$Id: dicomInfo.h,v 1.23 2007-11-15 16:20:44 valerio Exp $

//============================================================================
// CTN software is curtesy of
//
//		Mallinckrodt Institute of Radiology
//		Washington University in St. Louis MO
//		http://wuerlim.wustl.edu/
//
//============================================================================

//****************************************************************************
//
// Modification History (most recent first)
// mm/dd/yy  Who  What
//
// 10/17/11  VPL  Added verbose parameter
// 10/17/11  VPL  Add optional haveNumSlices to constructor
// 08/15/06  VPL  GPL'ed it
// 03/22/05  VPL  Allow processing of multiple series
//                Add NYU License agreement
// 01/17/03  VPL  
//
//****************************************************************************

#include <list>
#include <map>
#include <math.h>
#include <string>
#include <vector>
#include <sstream>
#include "dicom.h"
#include "lst.h"
#include "dicom_objects.h"
#include "utility.h"
#include "condition.h"

typedef std::string::size_type size_type;

//
// On older distros 'to_string' not defined
//

template <typename T> std::string to_string(const T& t)
{ 
	std::ostringstream os; 
	os << t; 
	return os.str(); 
} 

std::string &DeSpace(std::string &strng);

class DICOMImage
{
public:
	DICOMImage(const char *path, bool verbose = false, bool haveNumSlices = true);
	
	void AdjustSet(int numSlices, int imgDiff)
    {
		numSlices_ = numSlices;
		imageNum_ -= numSlices * (acqNum_ - 1) + imgDiff;
	}

	std::string &ImagePath()        { return imgPath_; }
	std::string &SubjectID()        { return subjID_; }
	std::string &SubjectName()      { return subjName_; }
	int ImageNumber() const			{ return imageNum_; }
	bool Mosaic() const				{ return mosaic_; }
	bool D3() const                 { return d3_; }
	int NumSlices() const			{ return numSlices_; }
    int NumGroups() const           { return numGroups_; }
	int GroupSize(int a = 0)        { return groupSize_[a]; }
	int GroupStartImage(int a = 0)  { return groupStartImage_[a]; }
	int GroupNumber(int imgNum = 1)
	{
		int gn = 0;
		while ( gn < numGroups_ )
		{
			if ( imgNum < (groupStartImage_[gn] + groupSize_[gn]) ) break;
			++gn;
		}

		// The gn index should never get to numGroups_, but you never know.
		
		return ( gn == numGroups_ ? 0 : gn );
	}
	
	float SliceThickness() const   	{ return sliceThickness_; }
	float SliceSpacing() const     	{ return sliceSpacing_; }
	std::string &Comments()		 	{ return comments_; }
	int Columns() const				{ return columns_; }
	int Rows() const			 	{ return rows_; }
	float XFOV() const		 	    { return xFOV_; }
	float YFOV() const				{ return yFOV_; }
	int RepetitionTime() const		{ return repTime_; }
	std::string &SeriesID()    		{ return seriesID_; }
	int FrequencyDim() const        { return (phaseEncoding_ == 2 ? 1 : 2); }
	int PhaseDim() const            { return phaseEncoding_; }
	float ImgRescaleSlope() const   { return slope_; }
	float ImgRescaleIntercept() const { return intercept_; }
	
	std::string &SeriesDescription(){ return seriesDescription_; }
	std::string &SeriesNumber()     { return seriesNumber_; }
	std::string &SeriesDate()       { return seriesDate_; }
	std::string &SeriesTime()       { return seriesTime_; }
	std::string &ACQDescription()   { return acqDescription_; }
	std::string &Accession()        { return accession_; }
	int ACQNumber() const	        { return acqNum_; }
	int ACQEchoNumber() const	    { return acqEchoNum_; }
	float ACQEchoTime() const	    { return acqEchoTime_; }
	unsigned short ACQRows() const  { return acqRows_; }
	unsigned short ACQColumns() const { return acqColumns_; }
	float RowSagCos() const    	 	{ return rowCosines_[0]; }
	float RowCorCos() const        	{ return rowCosines_[1]; }
	float RowTraCos() const		 	{ return rowCosines_[2]; }
	float ColSagCos() const    	 	{ return colCosines_[0]; }
	float ColCorCos() const    	 	{ return colCosines_[1]; }
	float ColTraCos() const    	 	{ return colCosines_[2]; }
  
	float SagNorm() const 		 	{ return sagNorm_; }
	float CorNorm() const 		 	{ return corNorm_; }
	float TraNorm() const 		 	{ return traNorm_; }

	float SagPos(int a = 0) const  	{ return sagPos_.size() > 0 ? sagPos_[a] : columns_ / 2; }
	float CorPos(int a = 0) const  	{ return corPos_.size() > 0 ? corPos_[a] : rows_ / 2; }
	float TraPos(int a = 0) const  	{ return traPos_.size() > 0 ? traPos_[a] : 1; }

	bool operator!() const		 	{ return error_; }
	operator void*() const		 	{ return error_ ? (void *)0 : (void *)this; }

	bool ReadSlice(U16 *data, int slice);
	bool ReadVolume();
	void FreeVolume()	 	     	{ if ( volumeData_ != NULL) delete [] volumeData_; volumeData_ = NULL; }

private:

	bool error_;
	bool verbose_;
	
	bool OpenFile(const char *path);
	
	bool GetImageNumber();
	bool GetACQNumber();
	bool GetACQEchoNumber();
	bool GetACQEchoTime();
	bool GetACQMatrix();
	bool GetMosaic();
	bool GetD3();
	bool GetNumSlices();
	bool GetGroupsInfo();
	bool GetSliceThickness();
	bool GetSliceSpacing();
	bool GetComments();
	bool GetPosNorm();
	bool GetPosNormSiemens(char *storage);
	bool GetColumns();
	bool GetRows();
	bool GetFOV();
	bool GetTime();
	bool GetRepetitionTime();
	bool GetImgOrientation();
	bool GetSeriesID();
	bool GetPhaseEncoding();
	bool GetImgRescaleIntercept();
	bool GetImgRescaleSlope();
	bool GetManufacturer();
	bool GetSeriesDescription();
	bool GetACQDescription();
	bool MosaicAdjustments();
	bool GetSubjectID();
	bool GetSubjectName();
	bool GetSeriesDate();
	bool GetSeriesTime();
	bool GetAccession();
	bool GetSeriesNumber();
	
	U32 GetData(DCM_TAG tag, char **storage);
	bool GetData(DCM_TAG tag, U32 *storage);
	bool GetData(DCM_TAG tag, U16 *storage);
	char *GetShadowSet();
  
	DCM_OBJECT *handle_;
	std::string imgPath_;
	std::string subjID_;
	std::string subjName_;
	int imageNum_;
	int numGroups_;
	std::vector<int> groupSize_;
	std::vector<int> groupStartImage_;
	bool mosaic_;
	bool d3_;
	float sliceThickness_;
	float sliceSpacing_;
	std::string comments_;
	int acqNum_;
	int acqEchoNum_;
	float acqEchoTime_;
	unsigned short acqRows_;
	unsigned short acqColumns_;
	int columns_;
	int rows_;
	float xFOV_;
	float yFOV_;
	int repTime_;
	int phaseEncoding_;
	float intercept_;
	float slope_;
	unsigned short *volumeData_;
	std::string seriesID_;
	std::string seriesDescription_;
	std::string seriesNumber_;
	std::string acqDescription_;
	std::string seriesDate_;
	std::string seriesTime_;

	std::string accession_;
	
	enum MANUFACTURER {SIEMENS, GE, UNDEFINED};
	MANUFACTURER manufacturer_;
	
	char *shadowSet_;
  
	std::vector<float> rowCosines_;
	std::vector<float> colCosines_;
	float sagNorm_;
	float corNorm_;
	float traNorm_;
	std::vector<float> sagPos_;
	std::vector<float> corPos_;
	std::vector<float> traPos_;
  
	// Next are used only with Mosaics
  
	int numSlices_;
	int mosaicGridSize_;
};

// Define a few usefull things:
//
//	IMAGELIST: a list of DICOMImage objects, each describing and image
//  IMAGEMAP: a map of IMAGELISTs to timepoints, one list per time point
//  SERIESMAP: a map of IMAGEMAPs to series, one map per series

typedef std::list<DICOMImage> IMAGELIST;
typedef std::map<int, IMAGELIST *> IMAGEMAP;

struct SeriesInfo
{
	SeriesInfo() : mosaic(false), multiEcho(false),
				   maxImgNum(0), minImgNum(0),
				   maxAcqNum(0), minAcqNum(0), numSlices(0) {}

	std::string outName;
	bool mosaic;
	bool multiEcho;
	int maxImgNum;
	int minImgNum;
	int maxAcqNum;
	int minAcqNum;
	int numSlices;
	IMAGEMAP imageMap;
};

typedef std::map<std::string, SeriesInfo> SERIESMAP;

#endif
