#ifndef Imagep_H
#define Imagep_H

#ifndef Grib_H
#include <Grib.hpp>
#endif

#ifndef Box_H
#include <Box.hpp>
#endif


#define NLINBUFFER		128		// no. lines in line_buffer
#define NLINBLOCK		64		// no. lines per block
#define NCOLBLOCK		64		// no. columns per block
#define NPELBLOCK		4096		// no. pixels per block

#define NBITLIN	6		// log (base 2) NLINBLOCK 
#define NBITCOL	6		// log (base 2) NCOLBLOCK 
#define MASCLIN	63		// NLINBLOCK - 1 
#define MASCCOL	63		// NCOLBLOCK - 1 

#define NOT_IN_MEMORY	(short)-1 
#define NOT_IN_USE	(short)-1 


class Imagep
{
protected :
	GribFile*	Igf;	// pointer to Grib file
	GribData*	Igd;	// pointer to Grib data
	SProjection*	Iproj;	// pointer to projection
	Box		Ibox;	// image bounding area
	short		Inx,	// no. of columns
			Iny;	// no. of lines
	float		Irx,	// horizontal resolution
			Iry;	// vertical resolution
	short		Index;	// Grib data index inside Grib file
	short		Inbits; // No. of bits/pixel
	
// Internal controls of the image

	char		rw;		// access mode : 0 read only
					//		 1 read/write
	char		bl;		// data structure : 1 block
					//                  0 line
	short		Inbytes;	// No. bytes/pixel
	unsigned long	pixel;		// pixel out of image
	unsigned long   outsidevalue;	// Default value to pixels outside Imagep
	unsigned long	naccess;	// no. of accesses to the image
	unsigned char	*tbuf;		// buffer for transfer data


	
	short 	npieces_disk,		// no. of pieces in disk 
		npieces_col,		// no. of pieces in the horizontal 
		npieces_memory;		// no. of pieces in the memory 

	unsigned long **piece_buffer;	// array of image blocks 
	short 	*piece_disk;		// piece_disk[i] gives the position in
					// data_buffer of the image block i
					// if piece_disk[i] = NOT_IN_MEMORY, the image
					// block i is not in piece_buffer 
		   			// NOT_IN_MEMORY -> piece i is not in memory

	short	*piece_memory;		// piece_memory[i] gives the index of the
					// image block occupying the piece_buffer[i]
					// piece_memory[i] = NOT_IN_USE- means that
					// piece_buffer[i] is empty
	
	unsigned long *piece_buffer_access;// gives the last access to the block_buffer[i] 
	

	int   ReadBlock( int pb, int pd );	// reads a block from disk
	int   WriteBlock( int pb, int pd );	// writes a block to disk
	int   ReadLine( int pb, int pd );	// reads a line from disk
	int   WriteLine( int pb, int pd );	// writes a line to disk
	int   AllocateMemoryBlock();		// Allocates memory for blocks
	int   AllocateMemoryLine();		// Allocates memory for lines
	void  DeallocateMemory();		// Frees memory
	void  FlushBlocks();			// Save block structure
	void  FlushLines();			// Save Line structure
	void  AllocateDisk();			// Allocates disk space
	void  Clear();				// Resets image
	void  SetLowerLefUpperRight();

public:	

	Imagep ();
// Constructor

	~Imagep ();
// Destructor.

	GribData* GetGribData(){ return Igd; }
// Return GribData pointer
	short NumCol () { return Inx; }
// Get the horizontal image size.

	short NumLin () { return Iny; }
// Get the vertical image size.

	short NumBit(){ return Inbits; }
// Get the number of bits/pixel

	short NumByte(){ return Inbytes; }
// Get the number of bites/pixel

	const char* Directory() { return (Igf) ? (Igf->Dir()) : (NULL); }
// Get the name of directory

	const char* Name(){ return (Igf) ? (Igf->Name()) : (NULL); }

	int Open ();
// Open GribFile related to this image

	int Close ();
// Close GribFile related to this image

	Box BoundingBox () { return Ibox;}
// Get image bounding area'

	SProjection* Projection () { return Iproj; }
// Get image projection.

	float ResolutionX () { return Irx; }
// Get the horizontal image resolution.

	float ResolutionY () { return Iry; }
// Get the vertical image resolution.

	unsigned long& operator() ( int lin, int col );
// Access to image pixels.

	short Get (short i, unsigned char* b);
// Get Line i from the image. It can be used only when the the image is set
// to line access mode ( see SetLineMode() ).

	short Put (short i, unsigned char* b);
// Put line i in the image. It can be used only when the the image is set
// to line access mode ( see SetLineMode() ).

	int  SetBlockMode();
// Set Virtual memory to the Block mode

	int  SetLineMode();
// Set Virtual memory to the Line mode

	void  SetReadWrite();
// Set permission access to read/write

	void  SetReadOnly();
// Set permission access to read only

	void  Flush(){ if( bl == 1 )FlushBlocks(); else FlushLines(); }

	void  SetOutsideValue( long out ) { pixel = out; outsidevalue = out; }
// Set default value for pixels lying outside image

	int Init( const char* dir, const char* nam, long index, int is_offset = 0);
// Initializes an image, already created.
//	Input:
//		dir   : directory path
//		nam   : file name
// 		index : Index of GribData in the GribFile
//		is_offset: Flag to indicate if previous value is field position or offset

	short InitAndRemap (const char* dir, const char* nam, Imagep* imi, Box& b, SProjection* p, float resx=0., float resy=0.);
//		Initializer based on a Imagep imi, already created, clipped by a Box b
//		in Projection p, with resolution resx and resy. A new image file 'nam' will
//		be created on directory 'dir'.
//		Input :
//			nam : file name where new
//			nam : file name where new
//			imi : pointer to Imagep that contains original data
//			b   : image bounding rectangle
//			p   : pointer to image projection
//			resx: horizontal resolution
//			resy: vertical resolution

	Point Coord2Index (Point& p);

	void InterpolateIn (Imagep* imi, Point& poll, Point& pour, Point& poul, Point& polr, Point& pill, Point& piur, Point& piul, Point& pilr);

	short PointOnLine (Point& p, Point& q, Point& t);

	short Remap (Imagep* imi); //, short type_interp);

	unsigned long InterpolateAt (Point& p);

	short ProjectionCode() { return Igd->ProjectionCode(); }

//<*Elisa*> new usefull functions

	short OriginX() { return Igd->OriginX(); }
	short OriginY() { return Igd->OriginY(); }
	double SubPointLat() { return Igd->SubPointLat(); }
	double SubPointLng() { return Igd->SubPointLng(); }
	short SubPointCoordX() { return Igd->SubPointCoordX(); }
	short SubPointCoordY() { return Igd->SubPointCoordY(); }
	double Yaw() { return Igd->Yaw(); }
	double AltitudeSat() { return Igd->AltitudeSat(); }
	short ScanningMode() { return Igd->ScaningMode(); }
	double ApparentDiaX() { return Igd->ApparentDiaX(); }
	double ApparentDiaY() { return Igd->ApparentDiaY(); }
	short ScaningMode() { return Igd->ScaningMode(); }
};

#endif
