/************************************************************************************
TerraLib - a library for developing GIS applications.
Copyright © 2001-2007 INPE and Tecgraf/PUC-Rio.

This code is part of the TerraLib library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

You should have received a copy of the GNU Lesser General Public
License along with this library.

The authors reassure the license terms regarding the warranties.
They specifically disclaim any warranties, including, but not limited to,
the implied warranties of merchantability and fitness for a particular purpose.
The library provided hereunder is on an "as is" basis, and the authors have no
obligation to provide maintenance, support, updates, enhancements, or modifications.
In no event shall INPE and Tecgraf / PUC-Rio be held liable to any party for direct,
indirect, special, incidental, or consequential damages arising out of the use
of this library and its documentation.
*************************************************************************************/
/*! \file TeOCICursor.h
	\brief This file contains the class OCICursor that represents a concept of record set to ORACLE DBMS using OCI (Oracle Call Interface) library. 
*/

#ifndef OCICursor_H
#define OCICursor_H

#include <TeOCIConnect.h>
#include <vector>
#include <TeCoord2D.h>

#include "TeOracleDefines.h"

using namespace std;

#define TYPE_OWNER				"MDSYS"
#define SDO_ORDINATE_ARRAY      TYPE_OWNER".SDO_ORDINATE_ARRAY"
#define SDO_ELEM_INFO_ARRAY     TYPE_OWNER".SDO_ELEM_INFO_ARRAY"
#define SDO_GEOMETRY			TYPE_OWNER".SDO_GEOMETRY"

//! Number of rows to be fetched into memory from database server
#define MAX_ROWS				500

//! Max size to be allocated in the client side to store blob data (in bytes)
#define MAX_BLOB_LENGTH			5000000 //5 MegaBytes (the max blob length is 4 Gigabytes)

//! Structure to represent a point as the SDO_POINT type of the Oracle Spatial 
struct TLORACLE_DLL sdo_point_type
{
   OCINumber x;
   OCINumber y;
   OCINumber z;
};

typedef struct sdo_point_type sdo_point_type;

//! Structure to represent a geometry as the SDO_GEOMETRY type of the Oracle Spatial 
struct TLORACLE_DLL sdo_geometry
{
   OCINumber       sdo_gtype;
   OCINumber       sdo_srid;
   sdo_point_type  sdo_point;
   OCIArray        *sdo_elem_info;
   OCIArray        *sdo_ordinates;
};

typedef struct sdo_geometry SDO_GEOMETRY_TYPE;

//! Structure to represent a point indicator as the SDO_POINT type of the Oracle Spatial 
struct TLORACLE_DLL sdo_point_type_ind
{
   OCIInd _atomic;
   OCIInd x;
   OCIInd y;
   OCIInd z;
};

typedef struct sdo_point_type_ind sdo_point_type_ind;

//! Structure to represent a geometry indicator as the SDO_GEOMETRY type of the Oracle Spatial 
struct TLORACLE_DLL SDO_GEOMETRY_ind
{
   OCIInd                    _atomic;
   OCIInd                    sdo_gtype;
   OCIInd                    sdo_srid;
   struct sdo_point_type_ind sdo_point;
   OCIInd                    sdo_elem_info;
   OCIInd                    sdo_ordinates;
};

typedef struct SDO_GEOMETRY_ind SDO_GEOMETRY_ind;

//! Structure to handle OCI data types
struct TLORACLE_DLL sb2ind
{
	sb2		sbind[MAX_ROWS];
};
typedef struct sb2ind	indarray;

/*! \class TeOCICursor
	\brief A class that implements a concept of record set to a ORACLE DBMS.

	This class contains attributes and methods to handle a record set 
	to ORACLE DBMS using OCI (Oracle Call Interface) library.

	A record set is generated by a query on the data in the database server.
	The class OCICursor is used by the drivers TeOCIOracle and TeOCIOracleSpatial that
	are responsible for the link between ORACLE DBMS and TerraLib.
	\sa 
	TeOCIConnect
*/
class TLORACLE_DLL TeOCICursor
{
protected:
	TeOCIConnection*	connection_;		//!< A pointer to a opened connection
	OCIStmt*			stmthpToQuery_;		//!< OCI statement handle to query
	OCIDescribe*		dschp_;				//!< OCI environment describe handle - describes objects
	bool				isOpen_;			//!< indicates if the cursor is opened or not (if the stmthpToQuery was located)
	string				errorMessage_;		//!< error message returned by the ORACLE DBMS  
				
	/** @name Buffers to store data in memory - client side
	*/
	//@{ 	
	vector<void *>			buffers_;	//!< buffer to store simple data type (varchar, number, date, etc)
	vector <indarray>		ind_;		//!< buffer to store null indicator
				
	SDO_GEOMETRY_TYPE*		global_geom_obj_[MAX_ROWS];	//!< buffer to store spatial object  
	SDO_GEOMETRY_ind*		global_geom_ind_[MAX_ROWS];	//!< buffer to store spatial object indicator  

	vector<unsigned char*>	lobBuffer_;			//! vector of buffers to store blob
	bool					hasBlob_;			//! Flag that indicates if the cursor has blob data type
	unsigned long			maxBuflen_;			//! Max memory the will be allocated to store blob data
    int						maxRows_;			//! Number of rows to be fetched into memory from database server

	vector<OCIDefine *>		defines_;		//!< OCIDefine objects to link buffers in client side and the statement handle
	OCIArray*				ordinates_;		//!< store the ordinates to be used in a query (bind)
	//@}

	string					fieldValue_;	//!< temporary variable 

	/** @name Information to handle the records or rows stored in memory (buffers) 
	*/
	//@{ 	
	short				row_Index_;		//!< relative current row index in the memory buffer
	int					row_Cur_;		//!< absolute current row index in the memory buffer
	int					rows_Fetched_;	//!< number of rows fetched from database
	int					rows_Mem_;		//!< number of rows on memory buffer - client side
	bool				last_Row_;		//!< if the current row is the last
	//@}
	
	/** @name Information about columns of the record set in memory
	*/
	//@{ 	
	int					numColls_;  //!< number of columns
	vector<string>		colName_;	//!< name of the columns
	vector<int>			colType_;	//!< type of the columns
	vector<int>			colSize_;	//!< size of the columns
	vector<int>			colScale_;  //!< scale of the columns
	vector<string>		colBlobName_; //!< name of the BLOB columns only
	//@}

public:
	
	//! Constructor 
	TeOCICursor(TeOCIConnection* conn, const unsigned long& maxBlobSize=0, const unsigned long& maxRowNum=0 );

	//! Destructor
	~TeOCICursor() { close(); }

	//! Opens the cursor locating memory and initializing the enviroment 
	bool open();
		
	//! Returns the error message 
	string	getErrorMessage() { return errorMessage_;}

	//! Checks if the cursor is opened
	bool isOpen()	{ return isOpen_; }

	//! Returns the connection to the database server  
	TeOCIConnection*	conn()		{ return connection_; }

	//! Returns the current row index 
	int	currentRow() { return row_Cur_; }

	//! Closes the cursor
	void close();
	
	//! Defines the OCI handles by position
	void defineByPos(int pos, void* value, int size, void* indicator, int type);

	//! Executes the SQL statement set previously 
	bool		execute();

	//! Fetchs a number of rows 
	bool		fetch(int rows);

	//! Executes a query defined by a SQL statement that returns a record set as result
	bool		query(const string& query);

	//! Executes a query defined by a SQL statement that uses SDO_GEOMETRY type and returns a record set as result
	bool		querySDO(const string& query);

	//! Moves to the first row in the record set 
	bool		moveFirst();

	//! Moves to the next row in the record set
	bool		moveNext();

	//! Moves to a specific position in the record set
	bool		moveTo(int pos);

	//! Moves to the last row in the record set
	bool		moveLast();
	
	/** @name Methods to return information about the columns and rows in the record set
	*/
	//@{ 
	//! Returns the column type (SQLT) of a specific column
	int			colType (int colnumber);
	//! Returns the column name of a specific column
	string		colName (int colnumber);
	//! Returns the column size of a specific column
	int			colSize (int colnumber);
	//! Returns the column scale (number of digits after decimal point) of a specific column
	int			colScale (int colnumber);
	//! Returns the columns number in the client side
	int			numCol(void);
	//! Loads all column descriptions (name, type, size and scale)
	void		loadCollDescription (); 
	//! Returns the rows number in the client side
	int			numRows(void);
	//@}

	//! Appends the new value to the internal ordinate vector
	bool		appendOrdinates(const double& val);
	
	//! Binds the internal ordinate array with the SQL statement
	bool		bindOrdinates();
	
	//! Sets and prepares the SQL statement to be executed
	bool		prepare(const string& stmt);
	
	//! Returns the query type based on the SQL statement set previously
	int			queryType(); 

	//! Locates memory to store the record set in the client side
	bool		allocateCursor(void);

	//! Gets the i-th field value from record set
	char*		getFieldValue(int i);

	/** @name Methods to deal with SDO_GEOMETRY, SDO_ELEM_INFO and SDO_ORDINATES types of the Oracle Spatial
	*/
	//@{ 	
	//! Gets the size of the SDO_ELEM_INFO array of the record set current row  
	int			getDimArraySize();
	//! Gets the i-th element of the SDO_ELEM_INFO array of the record set current row 
	bool		getDimElement(int i,int &elem);
	//! Gets the number of ordinates in the SDO_ORDINATES array of the record set current row 
	int			getNumberOrdinates(void);
	//! Gets the i-th coordinate of the SDO_ORDINATES array of the record set current row 
	bool		getCoordinates(int i,TeCoord2D& coord);
	//! Gets all coordinates of the SDO_ORDINATES array of the record set current row 
	bool		getCoordinates(vector<TeCoord2D>& result);
	//! Gets the geometry type of the SDO_GEOMETRY type of the record set current row 
	int			getGeometryType();
	//! Gets the spatial reference of the SDO_GEOMETRY type of the record set current row 
	int			getSpatialReferenceId();
	//! Gets a coordinate (x and y) of the SDO_POINT type of the record set current row 
	bool		getXYZcoord (double& x, double& y);
	//@}

	//! Clears the memory and structures located by the record set
	void		freeResult(void);

	/** @name Methods to deal with long binary (blob) type
	*/
	//@{ 	
	//! Reads a long binary (blob) column (blobCol) from database server and returns it as a unsigned char vector
	bool		readBlob(unsigned char* buffer, unsigned int& bufsize, unsigned int& blobCol);
	//! Reads a long binary (blob) column (blobCol) from database server and returns it as a unsigned char vector
	bool		readBlob(unsigned char* buffer, unsigned int& bufsize, const string& blobCol);
	//! Reads a long binary (blob) from database server and returns it as a double vector
	bool		readBlob(double *buffer, unsigned int& bufsize);
	//! Gets a pointer to buffer of blob
	bool		readBlob(double **buffer);
	//! Returns the size (in unsigend int) of a specific blob column
	unsigned int	getBlobSize(const string& blobCol);
	//@}

protected:
	//! Checks the error associated to error OCI handle
	bool		checkError(sword status);
};

#endif 


