///////////////////////////////////////////////////////////////////////////////
// Name:			TrackIO.h
// Purpose:			I/O interface for track file (.trk) 
// Author:			Ruopeng Wang
// Modified by:
// Last modified:	2005.03.15
// Copyright:		(c) 2004-2005 Ruopeng Wang <rpwang@nmr.mgh.harvard.edu>
// Licence:		
//           
// Usage:
//			Sample lines to read track file -
//
//          include "TrackIO.h"
//          
//			...
//
//			CTrackReader reader;
//          TRACK_HEADER header;
//			if (!reader.Open("foo.trk", &header))
//			{
//				printf(reader.GetLastErrorMessage());
//				return;
//			}
//			...
//             
//			int cnt;
//			if (ignore_scalars)
//			{
//				while (reader.GetNextPointCount(&cnt))
//				{
//					float* pts = new float[cnt*3];
//					reader.GetNextTrackData(cnt, pts);
//					...
//		            process_point_data(...);
//					...
//					delete[] pts;
//				}
//			}
//			else
//			{
//				while (reader.GetNextPointCount(&cnt))
//				{
//					float* pts = new float[cnt*3];
//					float* scalars = new float[cnt*header.n_scalars];
//					reader.GetNextTrackData(cnt, pts, scalars);
//					...
//		            process_point_and_scalar_data(...);
//					...
//					delete[] pts;
//					delete[] scalars;
//				}
//			}
//
//			reader.Close();
//
///////////////////////////////////////////////////////////////////////////////

#ifndef _TrackIO_H_
#define _TrackIO_H_

#include <stdio.h>
#include "ByteSwap.h"

struct TRACK_HEADER 
{
	char			id_string[6];	// first 5 chars must be "TRACK"
	short int		dim[3];			// dimensions
	float			voxel_size[3];	// voxel size
	float			origin[3];		// origin. default is 0,0,0.
	short int		n_scalars;		// number of scalars saved per point besides xyz coordinates. 
	char 			pad1;		
	unsigned char	has_max_min;	// indicate there are max and min values of the scalars stored
									// in the header
	float			max[10];		// max and min values of all the scalars. can store up to 10 pairs
	float			min[10];
	char			reserved[868];
	int				n_count;		// total number of tracks. if 0, number of tracks was not recorded.
									// call GetNumberOfTracks() to get it.
	int				version;		// version number
	int				hdr_size;		// size of the header. used to determine byte swap

	TRACK_HEADER()
	{
		Initialize();
	}

	TRACK_HEADER(int* d, float* vs, float* o, int n)
	{
		Initialize();

		for (int i = 0; i < 3; i++)
		{
			dim[i] = d[i];
			voxel_size[i] = vs[i];
			origin[i] = o[i];
		}
		n_scalars = n;
	}

	TRACK_HEADER(short int* d, float* vs, float* o, int n)
	{
		Initialize();

		for (int i = 0; i < 3; i++)
		{
			dim[i] = d[i];
			voxel_size[i] = vs[i];
			origin[i] = o[i];
		}
		n_scalars = n;
	}

	inline void ByteSwap()
	{
		SWAP_SHORT(dim, 3);
		SWAP_FLOAT(voxel_size, 3);
		SWAP_FLOAT(origin, 3);
		SWAP_SHORT(n_scalars);
		SWAP_FLOAT(max, 10);
		SWAP_FLOAT(min, 10);
		SWAP_INT(version);
		SWAP_INT(n_count);
		SWAP_INT(hdr_size);
	}

	inline void Initialize()
	{
		memset(this, 0, sizeof(struct TRACK_HEADER));
		strcpy(id_string, "TRACK");
		hdr_size = sizeof(struct TRACK_HEADER);
	}
};


enum TRACKIO_ERROR 
{	
	TE_NO_ERROR = 0,
	TE_CAN_NOT_OPEN,
	TE_CAN_NOT_CLOSE,
	TE_CAN_NOT_READ,
	TE_CAN_NOT_WRITE,
	TE_NOT_TRACK_FILE,
	TE_NOT_INITIALIZED
};

class CTrackIO
{
public:
	CTrackIO() { m_nErrorCode = 0; m_pFile = 0; }
	virtual ~CTrackIO() { Close(); }

	BOOL GetHeader(TRACK_HEADER* header);
	virtual BOOL Close();
	const char*	GetLastErrorMessage();
	int	GetLastErrorCode() { return m_nErrorCode; }

protected:
	TRACK_HEADER	m_header;
	FILE*	m_pFile;

	int		m_nErrorCode;
};



class CTrackReader : public CTrackIO
{
public:
	CTrackReader();

	BOOL Open(const char* filename, TRACK_HEADER* header = NULL);
	BOOL GetNextPointCount(int* ncount);
	BOOL GetNextRawData(int ncount, float* data);
	BOOL GetNextTrackData(int nCount, float* pt_data, float* scalars = NULL);
	int GetProgress();
	int  GetNumberOfTracks();
	BOOL GetNumberOfTracks(int* cnt);

	static BOOL GetHeaderOnly(const char* filename, TRACK_HEADER* header);

protected:
	BOOL			m_bByteSwap;
	long			m_nSize;
};



class CTrackWriter : public CTrackIO
{
public:
	BOOL Initialize(const char* filename, short int* dim, float* voxel_size, float* origin,
		short int n_scalars);
	BOOL Initialize(const char* filename, int* dim, float* voxel_size, float* origin,
		short int n_scalars);
	BOOL Initialize(const char* filename, TRACK_HEADER header);
	BOOL WriteNextTrack(int ncount, float* data);
	BOOL UpdateHeader(TRACK_HEADER header);

	virtual BOOL Close();

	static BOOL UpdateHeader(const char* filename, TRACK_HEADER header);
};


// find mininum value from an array
template <class T> inline T fmin(const T* a, int n)
{
	T tmin = a[0];
	for (int i = 1; i < n; i++)
	{
		if (tmin > a[i])
			tmin = a[i];
	}
	return tmin;
}

// find maximum value from an array
template <class T> inline T fmax(const T* a, int n)
{
	T tmax = a[0];
	for (int i = 1; i < n; i++)
	{
		if (tmax < a[i])
			tmax = a[i];
	}
	return tmax;
}


#endif