#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
