/******************************************************************************
 * Project:  rasdaman Driver
 * Purpose:  Implement Rasdaman GDAL driver
 * Author:   Constantin Jucovschi, jucovschi@yahoo.com
 *
 ******************************************************************************
 * Copyright (c) 2010, Constantin Jucovschi
 * Copyright (c) 2010, Even Rouault <even dot rouault at spatialys.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 ******************************************************************************/

#include "rasdamandataset.h"

#include "cpl_string.h"
#include "gdal_pam.h"
#include "gdal_frmts.h"
#include "regex.h"
#include <string>
#include <memory>
#include <map>

#include "gdal.h"

void CPL_DLL CPL_STDCALL GDALRegister_RASDAMAN();

// cppcheck-suppress copyCtorAndEqOperator
class Subset
{
  public:
    Subset(int x_loIn, int x_hiIn, int y_loIn, int y_hiIn)
        : m_x_lo(x_loIn), m_x_hi(x_hiIn), m_y_lo(y_loIn), m_y_hi(y_hiIn)
    {
    }

    bool operator<(const Subset &rhs) const
    {
        if (m_x_lo < rhs.m_x_lo || m_x_hi < rhs.m_x_hi || m_y_lo < rhs.m_y_lo ||
            m_y_hi < rhs.m_y_hi)
        {

            return true;
        }
        return false;
    }

    bool contains(const Subset &other) const
    {
        return m_x_lo <= other.m_x_lo && m_x_hi >= other.m_x_hi &&
               m_y_lo <= other.m_y_lo && m_y_hi >= other.m_y_hi;
    }

    bool within(const Subset &other) const
    {
        return other.contains(*this);
    }

    Subset &operator=(const Subset &rhs)
    {
        if (&rhs != this)
        {
            m_x_lo = rhs.m_x_lo;
            m_x_hi = rhs.m_x_hi;
            m_y_lo = rhs.m_y_lo;
            m_y_hi = rhs.m_y_hi;
        }
        return *this;
    }

    int x_lo() const
    {
        return m_x_lo;
    }
    int x_hi() const
    {
        return m_x_hi;
    }
    int y_lo() const
    {
        return m_y_lo;
    }
    int y_hi() const
    {
        return m_y_hi;
    }

  private:
    int m_x_lo;
    int m_x_hi;
    int m_y_lo;
    int m_y_hi;
};

/************************************************************************/
/* ==================================================================== */
/*        RasdamanDataset                                               */
/* ==================================================================== */
/************************************************************************/

typedef std::map<Subset, r_Ref<r_GMarray>> ArrayCache;

class RasdamanRasterBand;
static CPLString getQuery(const char *templateString, const char *x_lo,
                          const char *x_hi, const char *y_lo, const char *y_hi);

class RasdamanDataset final : public GDALPamDataset
{
    friend class RasdamanRasterBand;

  public:
    RasdamanDataset(const char *, int, const char *, const char *,
                    const char *);
    ~RasdamanDataset();

    static GDALDataset *Open(GDALOpenInfo *);

  protected:
    virtual CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
                             GDALDataType, int, int *, GSpacing nPixelSpace,
                             GSpacing nLineSpace, GSpacing nBandSpace,
                             GDALRasterIOExtraArg *psExtraArg) override;

  private:
    ArrayCache m_array_cache;

    r_Ref<r_GMarray> &request_array(int x_lo, int x_hi, int y_lo, int y_hi,
                                    int &offsetX, int &offsetY);
    r_Ref<r_GMarray> &request_array(const Subset &, int &offsetX, int &offsetY);

    void clear_array_cache();

    static r_Set<r_Ref_Any> execute(const char *string);

    void getTypes(const r_Base_Type *baseType, int &counter, int pos);
    void createBands(const char *queryString);

    r_Database database;
    r_Transaction transaction;

    CPLString queryParam;
    CPLString host;
    int port;
    CPLString username;
    CPLString userpassword;
    CPLString databasename;
    int xPos;
    int yPos;
    int tileXSize;
    int tileYSize;
};

/************************************************************************/
/*                            RasdamanDataset()                         */
/************************************************************************/

RasdamanDataset::RasdamanDataset(const char *_host, int _port,
                                 const char *_username,
                                 const char *_userpassword,
                                 const char *_databasename)
    : host(_host), port(_port), username(_username),
      userpassword(_userpassword), databasename(_databasename)
{
    database.set_servername(host, port);
    database.set_useridentification(username, userpassword);
    database.open(databasename);
}

/************************************************************************/
/*                            ~RasdamanDataset()                        */
/************************************************************************/

RasdamanDataset::~RasdamanDataset()
{
    if (transaction.get_status() == r_Transaction::active)
    {
        transaction.commit();
    }
    database.close();
    FlushCache(true);
}

CPLErr RasdamanDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
                                  int nXSize, int nYSize, void *pData,
                                  int nBufXSize, int nBufYSize,
                                  GDALDataType eBufType, int nBandCount,
                                  int *panBandMap, GSpacing nPixelSpace,
                                  GSpacing nLineSpace, GSpacing nBandSpace,
                                  GDALRasterIOExtraArg *psExtraArg)
{
    if (eRWFlag != GF_Read)
    {
        CPLError(CE_Failure, CPLE_NoWriteAccess,
                 "Write support is not implemented.");
        return CE_Failure;
    }

    transaction.begin(r_Transaction::read_only);

    /* TODO: Setup database access/transaction */
    int dummyX, dummyY;
    /* Cache the whole image region */
    CPLDebug("rasdaman", "Pre-caching region (%d, %d, %d, %d).", nXOff,
             nXOff + nXSize, nYOff, nYOff + nYSize);
    request_array(nXOff, nXOff + nXSize, nYOff, nYOff + nYSize, dummyX, dummyY);

    CPLErr ret = GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
                                        pData, nBufXSize, nBufYSize, eBufType,
                                        nBandCount, panBandMap, nPixelSpace,
                                        nLineSpace, nBandSpace, psExtraArg);

    transaction.commit();

    /* Clear the cache */
    clear_array_cache();

    return ret;
}

r_Ref<r_GMarray> &RasdamanDataset::request_array(int x_lo, int x_hi, int y_lo,
                                                 int y_hi, int &offsetX,
                                                 int &offsetY)
{
    return request_array(Subset(x_lo, x_hi, y_lo, y_hi), offsetX, offsetY);
};

r_Ref<r_GMarray> &RasdamanDataset::request_array(const Subset &subset,
                                                 int &offsetX, int &offsetY)
{
    offsetX = 0;
    offsetY = 0;

    // check whether or not the subset was already requested
    ArrayCache::iterator it = m_array_cache.find(subset);
    if (it != m_array_cache.end())
    {
        CPLDebug("rasdaman", "Fetching tile (%d, %d, %d, %d) from cache.",
                 subset.x_lo(), subset.x_hi(), subset.y_lo(), subset.y_hi());

        return it->second;
    }

    // check if any tile contains the requested one
    for (it = m_array_cache.begin(); it != m_array_cache.end(); ++it)
    {
        if (it->first.contains(subset))
        {
            const Subset &existing = it->first;

            // TODO: check if offsets are correct
            offsetX = subset.x_lo() - existing.x_lo();
            offsetY = subset.y_lo() - existing.y_lo();

            CPLDebug("rasdaman",
                     "Found matching tile (%d, %d, %d, %d) for requested tile "
                     "(%d, %d, %d, %d). Offsets are (%d, %d).",
                     existing.x_lo(), existing.x_hi(), existing.y_lo(),
                     existing.y_hi(), subset.x_lo(), subset.x_hi(),
                     subset.y_lo(), subset.y_hi(), offsetX, offsetY);

            return it->second;
        }
    }

    if (transaction.get_status() != r_Transaction::active)
    {
        transaction.begin(r_Transaction::read_only);
    }

    CPLDebug("rasdaman",
             "Tile (%d, %d, %d, %d) not found in cache, requesting it.",
             subset.x_lo(), subset.x_hi(), subset.y_lo(), subset.y_hi());

    char x_lo[11], x_hi[11], y_lo[11], y_hi[11];

    snprintf(x_lo, sizeof(x_lo), "%d", subset.x_lo());
    snprintf(x_hi, sizeof(x_hi), "%d", subset.x_hi());
    snprintf(y_lo, sizeof(y_lo), "%d", subset.y_lo());
    snprintf(y_hi, sizeof(y_hi), "%d", subset.y_hi());

    CPLString queryString = getQuery(queryParam, x_lo, x_hi, y_lo, y_hi);

    r_Set<r_Ref_Any> result_set = execute(queryString);
    if (result_set.get_element_type_schema()->type_id() == r_Type::MARRAYTYPE)
    {
        // TODO: throw exception
    }

    if (result_set.cardinality() != 1)
    {
        // TODO: throw exception
    }

    r_Ref<r_GMarray> result_array =
        r_Ref<r_GMarray>(*result_set.create_iterator());
    // std::auto_ptr<r_GMarray> ptr(new r_GMarray);
    // r_GMarray* ptr_ = ptr.get();
    //(*ptr) = *result_array;
    // std::pair<ArrayCache::iterator, bool> inserted =
    // m_array_cache.insert(ArrayCache::value_type(subset, ptr));

    std::pair<ArrayCache::iterator, bool> inserted =
        m_array_cache.insert(ArrayCache::value_type(subset, result_array));

    return inserted.first->second;  //*(ptr);
};

void RasdamanDataset::clear_array_cache()
{
    m_array_cache.clear();
};

/************************************************************************/
/* ==================================================================== */
/*                            RasdamanRasterBand                        */
/* ==================================================================== */
/************************************************************************/

class RasdamanRasterBand final : public GDALPamRasterBand
{
    friend class RasdamanDataset;

    int nRecordSize;
    int typeOffset;
    int typeSize;

  public:
    RasdamanRasterBand(RasdamanDataset *, int, GDALDataType type, int offset,
                       int size, int nBlockXSize, int nBlockYSize);
    ~RasdamanRasterBand();

    virtual CPLErr IReadBlock(int, int, void *) override;
};

/************************************************************************/
/*  RasdamanParams                                                      */
/************************************************************************/

/*struct RasdamanParams
{
  RasdamanParams(const char* dataset_info);

  void connect(const r_Database&);

  const char *query;
  const char *host;
  const int port;
  const char *username;
  const char *password;
};*/

/************************************************************************/
/*                           RasdamanRasterBand()                       */
/************************************************************************/

RasdamanRasterBand::RasdamanRasterBand(RasdamanDataset *poDSIn, int nBandIn,
                                       GDALDataType type, int offset, int size,
                                       int nBlockXSizeIn, int nBlockYSizeIn)
{
    this->poDS = poDSIn;
    this->nBand = nBandIn;

    eDataType = type;
    typeSize = size;
    typeOffset = offset;

    this->nBlockXSize = nBlockXSizeIn;
    this->nBlockYSize = nBlockYSizeIn;

    nRecordSize = nBlockXSize * nBlockYSize * typeSize;
}

/************************************************************************/
/*                          ~RasdamanRasterBand()                       */
/************************************************************************/

RasdamanRasterBand::~RasdamanRasterBand()
{
}

/************************************************************************/
/*                             IReadBlock()                             */
/************************************************************************/

CPLErr RasdamanRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
                                      void *pImage)
{
    RasdamanDataset *poGDS = (RasdamanDataset *)poDS;

    memset(pImage, 0, nRecordSize);

    try
    {
        int x_lo = nBlockXOff * nBlockXSize;
        int x_hi = MIN(poGDS->nRasterXSize, (nBlockXOff + 1) * nBlockXSize);
        int y_lo = nBlockYOff * nBlockYSize;
        int y_hi = MIN(poGDS->nRasterYSize, (nBlockYOff + 1) * nBlockYSize);
        int offsetX = 0;
        int offsetY = 0;

        r_Ref<r_GMarray> &gmdd =
            poGDS->request_array(x_lo, x_hi, y_lo, y_hi, offsetX, offsetY);

        int xPos = poGDS->xPos;
        int yPos = poGDS->yPos;

        r_Minterval sp = gmdd->spatial_domain();
        r_Point extent = sp.get_extent();
        r_Point base = sp.get_origin();

        int extentX = extent[xPos];
        int extentY = extent[yPos];

        CPLDebug("rasdaman", "Extents (%d, %d).", extentX, extentY);

        r_Point access = base;

        for (int y = y_lo; y < y_hi; ++y)
        {
            for (int x = x_lo; x < x_hi; ++x)
            {
                char *resultPtr =
                    (char *)pImage +
                    ((y - y_lo) * nBlockXSize + x - x_lo) * typeSize;
                // resultPtr = (char*) pImage
                access[xPos] =
                    x;  // base[xPos] + offsetX; TODO: check if required
                access[yPos] = y;  // base[yPos] + offsetY;
                const char *data = (*gmdd)[access] + typeOffset;
                memcpy(resultPtr, data, typeSize);
            }
        }
    }
    catch (const r_Error &error)
    {
        CPLError(CE_Failure, CPLE_AppDefined, "%s", error.what());
        return CPLGetLastErrorType();
    }

    return CE_None;
}

/************************************************************************/
/* ==================================================================== */
/*                            RasdamanDataset                           */
/* ==================================================================== */
/************************************************************************/

static CPLString getOption(const char *string, regmatch_t cMatch,
                           const char *defaultValue)
{
    if (cMatch.rm_eo == -1 || cMatch.rm_so == -1)
        return defaultValue;
    char *result = new char[cMatch.rm_eo - cMatch.rm_so + 1];
    strncpy(result, string + cMatch.rm_so, cMatch.rm_eo - cMatch.rm_so);
    result[cMatch.rm_eo - cMatch.rm_so] = 0;
    CPLString osResult = result;
    delete[] result;
    return osResult;
}

static int getOption(const char *string, regmatch_t cMatch, int defaultValue)
{
    if (cMatch.rm_eo == -1 || cMatch.rm_so == -1)
        return defaultValue;
    char *result = new char[cMatch.rm_eo - cMatch.rm_so + 1];
    strncpy(result, string + cMatch.rm_so, cMatch.rm_eo - cMatch.rm_so);
    result[cMatch.rm_eo - cMatch.rm_so] = 0;
    int nRet = atoi(result);
    delete[] result;
    return nRet;
}

static void replace(CPLString &str, const char *from, const char *to)
{
    if (strlen(from) == 0)
        return;
    size_t start_pos = 0;
    while ((start_pos = str.find(from, start_pos)) != std::string::npos)
    {
        str.replace(start_pos, strlen(from), to);
        start_pos += strlen(
            to);  // In case 'to' contains 'from', like replacing 'x' with 'yx'
    }
}

static CPLString getQuery(const char *templateString, const char *x_lo,
                          const char *x_hi, const char *y_lo, const char *y_hi)
{
    CPLString result(templateString);

    replace(result, "$x_lo", x_lo);
    replace(result, "$x_hi", x_hi);
    replace(result, "$y_lo", y_lo);
    replace(result, "$y_hi", y_hi);

    return result;
}

static GDALDataType mapRasdamanTypesToGDAL(r_Type::r_Type_Id typeId)
{
    switch (typeId)
    {
        case r_Type::ULONG:
            return GDT_UInt32;
        case r_Type::LONG:
            return GDT_Int32;
        case r_Type::SHORT:
            return GDT_Int16;
        case r_Type::USHORT:
            return GDT_UInt16;
        case r_Type::BOOL:
        case r_Type::CHAR:
            return GDT_Byte;
        case r_Type::DOUBLE:
            return GDT_Float64;
        case r_Type::FLOAT:
            return GDT_Float32;
        case r_Type::COMPLEXTYPE1:
            return GDT_CFloat32;
        case r_Type::COMPLEXTYPE2:
            return GDT_CFloat64;
        default:
            return GDT_Unknown;
    }
}

void RasdamanDataset::getTypes(const r_Base_Type *baseType, int &counter,
                               int pos)
{
    if (baseType->isStructType())
    {
        r_Structure_Type *tp = (r_Structure_Type *)baseType;
        int elem = tp->count_elements();
        for (int i = 0; i < elem; ++i)
        {
            r_Attribute attr = (*tp)[i];
            getTypes(&attr.type_of(), counter, attr.global_offset());
        }
    }
    if (baseType->isPrimitiveType())
    {
        r_Primitive_Type *primType = (r_Primitive_Type *)baseType;
        r_Type::r_Type_Id typeId = primType->type_id();
        SetBand(counter,
                new RasdamanRasterBand(
                    this, counter, mapRasdamanTypesToGDAL(typeId), pos,
                    primType->size(), this->tileXSize, this->tileYSize));
        counter++;
    }
}

void RasdamanDataset::createBands(const char *queryString)
{
    r_Set<r_Ref_Any> result_set;
    r_OQL_Query query(queryString);
    r_oql_execute(query, result_set);
    if (result_set.get_element_type_schema()->type_id() == r_Type::MARRAYTYPE)
    {
        r_Iterator<r_Ref_Any> iter = result_set.create_iterator();
        r_Ref<r_GMarray> gmdd = r_Ref<r_GMarray>(*iter);
        const r_Base_Type *baseType = gmdd->get_base_type_schema();
        int counter = 1;
        getTypes(baseType, counter, 0);
    }
}

r_Set<r_Ref_Any> RasdamanDataset::execute(const char *string)
{
    CPLDebug("rasdaman", "Executing query '%s'.", string);
    r_Set<r_Ref_Any> result_set;
    r_OQL_Query query(string);
    r_oql_execute(query, result_set);
    return result_set;
}

static int getExtent(const char *queryString, int &pos)
{
    r_Set<r_Ref_Any> result_set;
    r_OQL_Query query(queryString);
    r_oql_execute(query, result_set);
    if (result_set.get_element_type_schema()->type_id() ==
        r_Type::MINTERVALTYPE)
    {
        r_Iterator<r_Ref_Any> iter = result_set.create_iterator();
        r_Ref<r_Minterval> interv = r_Ref<r_Minterval>(*iter);
        r_Point extent = interv->get_extent();
        int dim = extent.dimension();
        int result = -1;
        for (int i = 0; i < dim; ++i)
        {
            if (extent[i] == 1)
                continue;
            if (result != -1)
                return -1;
            result = extent[i];
            pos = i;
        }
        if (result == -1)
            return 1;
        else
            return result;
    }
    else
        return -1;
}

/************************************************************************/
/*                                Open()                                */
/************************************************************************/

GDALDataset *RasdamanDataset::Open(GDALOpenInfo *poOpenInfo)
{
    // buffer to communicate errors
    char errbuffer[4096];

    // fast checks if current module should handle the request
    // check 1: the request is not on a existing file in the file system
    if (poOpenInfo->fpL != NULL)
    {
        return NULL;
    }
    // check 2: the request contains --collection
    char *connString = poOpenInfo->pszFilename;
    if (!STARTS_WITH_CI(connString, "rasdaman"))
    {
        return NULL;
    }

    // regex for parsing options
    regex_t optionRegEx;
    // regex for parsing query
    regex_t queryRegEx;
    // array to store matching subexpressions
    regmatch_t matches[10];

#define QUERY_POSITION 2
#define SERVER_POSITION 3
#define PORT_POSITION 4
#define USERNAME_POSITION 5
#define USERPASSWORD_POSITION 6
#define DATABASE_POSITION 7
#define TILEXSIZE_POSITION 8
#define TILEYSIZE_POSITION 9

    int result =
        regcomp(&optionRegEx,
                "^rasdaman:(query='([[:alnum:][:punct:] "
                "]+)'|host='([[:alnum:][:punct:]]+)'|port=([0-9]+)|user='([[:"
                "alnum:]]+)'|password='([[:alnum:]]+)'|database='([[:alnum:]]+)"
                "'|tileXSize=([0-9]+)|tileYSize=([0-9]+)| )*",
                REG_EXTENDED);

    // should never happen
    if (result != 0)
    {
        regerror(result, &optionRegEx, errbuffer, 4096);
        CPLError(CE_Failure, CPLE_AppDefined,
                 "Internal error at compiling option parsing regex: %s",
                 errbuffer);
        return NULL;
    }

    result = regcomp(
        &queryRegEx,
        "^select ([[:alnum:][:punct:] ]*) from ([[:alnum:][:punct:] ]*)$",
        REG_EXTENDED);
    // should never happen
    if (result != 0)
    {
        regerror(result, &queryRegEx, errbuffer, 4096);
        CPLError(CE_Failure, CPLE_AppDefined,
                 "Internal error at compiling option parsing regex: %s",
                 errbuffer);
        return NULL;
    }

    // executing option parsing regex on the connection string and checking if
    // it succeeds
    result = regexec(&optionRegEx, connString, 10, matches, 0);
    if (result != 0)
    {
        regerror(result, &optionRegEx, errbuffer, 4096);
        CPLError(CE_Failure, CPLE_AppDefined,
                 "Parsing opening parameters failed with error: %s", errbuffer);
        regfree(&optionRegEx);
        regfree(&queryRegEx);
        return NULL;
    }

    regfree(&optionRegEx);

    // checking if the whole expressions was matches, if not give an error where
    // the matching stopped and exit
    if (size_t(matches[0].rm_eo) < strlen(connString))
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "Parsing opening parameters failed with error: %s",
                 connString + matches[0].rm_eo);
        regfree(&queryRegEx);
        return NULL;
    }

    CPLString queryParam =
        getOption(connString, matches[QUERY_POSITION], (const char *)NULL);
    CPLString host =
        getOption(connString, matches[SERVER_POSITION], "localhost");
    int port = getOption(connString, matches[PORT_POSITION], 7001);
    CPLString username =
        getOption(connString, matches[USERNAME_POSITION], "rasguest");
    CPLString userpassword =
        getOption(connString, matches[USERPASSWORD_POSITION], "rasguest");
    CPLString databasename =
        getOption(connString, matches[DATABASE_POSITION], "RASBASE");
    int tileXSize = getOption(connString, matches[TILEXSIZE_POSITION], 1024);
    int tileYSize = getOption(connString, matches[TILEYSIZE_POSITION], 1024);

    result = regexec(&queryRegEx, queryParam, 10, matches, 0);
    if (result != 0)
    {
        regerror(result, &queryRegEx, errbuffer, 4096);
        CPLError(CE_Failure, CPLE_AppDefined,
                 "Parsing query parameter failed with error: %s", errbuffer);
        regfree(&queryRegEx);
        return NULL;
    }

    regfree(&queryRegEx);

    CPLString osQueryString = "select sdom(";
    osQueryString += getOption(queryParam, matches[1], "");
    osQueryString += ") from ";
    osQueryString += getOption(queryParam, matches[2], "");

    CPLDebug("rasdaman", "osQueryString: %s", osQueryString.c_str());

    CPLString queryX = getQuery(osQueryString, "*", "*", "0", "0");
    CPLString queryY = getQuery(osQueryString, "0", "0", "*", "*");
    CPLString queryUnit = getQuery(queryParam, "0", "0", "0", "0");

    CPLDebug("rasdaman", "queryX: %s", queryX.c_str());
    CPLDebug("rasdaman", "queryY: %s", queryY.c_str());
    CPLDebug("rasdaman", "queryUnit: %s", queryUnit.c_str());

    RasdamanDataset *rasDataset = NULL;
    try
    {
        rasDataset = new RasdamanDataset(host, port, username, userpassword,
                                         databasename);
        // getMyExtent(osQueryString, posX, sizeX, posY, sizeY);

        r_Transaction transaction;
        transaction.begin(r_Transaction::read_only);

        int dimX = getExtent(queryX, rasDataset->xPos);
        int dimY = getExtent(queryY, rasDataset->yPos);
        rasDataset->nRasterXSize = dimX;
        rasDataset->nRasterYSize = dimY;
        rasDataset->tileXSize = tileXSize;
        rasDataset->tileYSize = tileYSize;
        rasDataset->createBands(queryUnit);

        transaction.commit();

        rasDataset->queryParam = queryParam;
        rasDataset->host = host;
        rasDataset->port = port;
        rasDataset->username = username;
        rasDataset->userpassword = userpassword;
        rasDataset->databasename = databasename;

        return rasDataset;
    }
    catch (const r_Error &error)
    {
        CPLError(CE_Failure, CPLE_AppDefined, "%s", error.what());
        delete rasDataset;
        return NULL;
    }

    return rasDataset;
}

/************************************************************************/
/*                          GDALRegister_RASDAMAN()                     */
/************************************************************************/

void GDALRegister_RASDAMAN()

{
    if (GDALGetDriverByName("RASDAMAN") != NULL)
        return;

    GDALDriver *poDriver = new GDALDriver();

    poDriver->SetDescription("RASDAMAN");
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "RASDAMAN");
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
                              "drivers/raster/rasdaman.html");

    poDriver->pfnOpen = RasdamanDataset::Open;

    GetGDALDriverManager()->RegisterDriver(poDriver);
}
