/*!
  \file db/drivers/describe.c
  
  \brief Low level OGR SQL driver
 
  (C) 2004-2009 by the GRASS Development Team
  This program is free software under the GNU General Public License
  (>=v2). Read the file COPYING that comes with GRASS for details.
  
  \author Radim Blazek
  \author Some updates by Martin Landa <landa.martin gmail.com>
*/

#include <grass/gis.h>
#include <grass/datetime.h>
#include <grass/dbmi.h>
#include <grass/glocale.h>

#include <ogr_api.h>

#include "globals.h"
#include "proto.h"

/*!
  \brief Describe table using driver

  \param table_name table name (as dbString)
  \param table[out] pointer to dbTable

  \return DB_OK on success
  \return DB_FAILED on failure
*/
int db__driver_describe_table(dbString * table_name, dbTable ** table)
{
    int i, nlayers;
    OGRLayerH hLayer = NULL;
    OGRFeatureDefnH hFeatureDefn;

    /* get number of OGR layers in the datasource */
    nlayers = OGR_DS_GetLayerCount(hDs);

    /* find OGR layer */
    for (i = 0; i < nlayers; i++) {
	hLayer = OGR_DS_GetLayer(hDs, i);
	hFeatureDefn = OGR_L_GetLayerDefn(hLayer);
	if (G_strcasecmp
	    ((char *)OGR_FD_GetName(hFeatureDefn),
	     db_get_string(table_name)) == 0) {
	    break;
	}
	hLayer = NULL;
    }

    if (hLayer == NULL) {
	db_d_append_error(_("OGR layer <%s> does not exist\n"),
			  db_get_string(table_name));
	db_d_report_error();
	return DB_FAILED;
    }

    G_debug(3, "->>");
    if (describe_table(hLayer, table, NULL) == DB_FAILED) {
	db_d_append_error(_("Unable to describe table\n"));
	db_d_report_error();
	return DB_FAILED;
    }

    return DB_OK;
}

/*!
  \brief Describe table

  If c is not NULL cur->cols and cur->ncols is also set 
  cursor may be null

  \param hLayer OGR layer
  \param[put] table pointer to dbTable
  \param c pointer to cursor
 */
int describe_table(OGRLayerH hLayer, dbTable **table, cursor *c)
{
    int i, ncols, kcols, col;
    dbColumn *column;
    OGRFeatureDefnH hFeatureDefn;
    OGRFieldDefnH hFieldDefn;
    const char *fieldName, *fidcol;
    int ogrType;
    int *cols;

    G_debug(1, "describe_table()");

    hFeatureDefn = OGR_L_GetLayerDefn(hLayer);
    ncols = OGR_FD_GetFieldCount(hFeatureDefn);
    G_debug(2, "   ncols = %d (without fid column)", ncols);
    
    /* for some formats fid column is not defined, e.g. ESRI Shapefile */
    fidcol = OGR_L_GetFIDColumn(hLayer); 
    G_debug(2, "   fidcol = %s", fidcol);
    
    /* identify known columns */
    cols = (int *) G_malloc(ncols * sizeof(int));
    kcols = 0;
    for(i = 0; i < ncols; i++) {
	hFieldDefn = OGR_FD_GetFieldDefn(hFeatureDefn, i);
	ogrType = OGR_Fld_GetType(hFieldDefn);
	fieldName = OGR_Fld_GetNameRef(hFieldDefn);

	if (ogrType != OFTInteger && ogrType != OFTReal &&
	    ogrType != OFTString  && ogrType != OFTDate &&
	    ogrType != OFTTime    && ogrType != OFTDateTime ) {
	    G_warning(_("OGR driver: column '%s', OGR type %d is not supported"),
		      fieldName, ogrType);
	    cols[i] = 0;
	}
	else {
	    cols[i] = 1;
	    kcols++;
	}
    }
    
    if (*fidcol)
	kcols++;
    G_debug(2, "   kcols = %d (including fid column)", kcols);
    
    /* allocate dbTable */
    if (!(*table = db_alloc_table(kcols))) {
	return DB_FAILED;
    }
    
    /* set the table name */
    /* TODO */
    db_set_table_name(*table, "");
    
    /* set the table description */
    db_set_table_description(*table, "");

    /* TODO */
    /*
       db_set_table_delete_priv_granted (*table);
       db_set_table_insert_priv_granted (*table);
       db_set_table_delete_priv_not_granted (*table);
       db_set_table_insert_priv_not_granted (*table);
    */

    if (*fidcol) {
	column = db_get_table_column(*table, 0);
	db_set_column_host_type(column, OFTInteger);
	db_set_column_sqltype(column, DB_SQL_TYPE_INTEGER);
	db_set_column_name(column, fidcol);
	db_set_column_length(column, 11); /* ??? */
	db_set_column_precision(column, 0);
	
	col = 1;
    }
    else {
	col = 0;
    }

    for (i = 0; i < ncols; i++, col++) {
	int sqlType;
	int size, precision, scale;

	hFieldDefn = OGR_FD_GetFieldDefn(hFeatureDefn, i);
	ogrType = OGR_Fld_GetType(hFieldDefn);
	fieldName = OGR_Fld_GetNameRef(hFieldDefn);
	
	if (!(cols[i])) {
	    continue;		/* unknown type */
	}

	switch (ogrType) {
	case OFTInteger:
	    sqlType = DB_SQL_TYPE_INTEGER;
	    size = OGR_Fld_GetWidth(hFieldDefn);	/* OK ? */
	    precision = 0;
	    break;

	case OFTReal:
	    sqlType = DB_SQL_TYPE_DOUBLE_PRECISION;
	    size = OGR_Fld_GetWidth(hFieldDefn);	/* OK ? */
	    precision = OGR_Fld_GetPrecision(hFieldDefn);
	    break;

	case OFTString:
	case OFTDate:
	case OFTTime:
	case OFTDateTime:
	    sqlType = DB_SQL_TYPE_CHARACTER;
	    size = OGR_Fld_GetWidth(hFieldDefn);
	    if (size == 0) {
		G_warning(_("column '%s', type 'string': unknown width -> stored as varchar(250) "
			   "some data may be lost"), fieldName);
		size = 250;
	    }
	    precision = 0;
	    break;

	default:
	    G_warning(_("Unknown type"));
	    break;
	}
	
	G_debug(3, "   %d: field %d : ogrType = %d, name = %s, size=%d precision=%d",
		i, col, ogrType, fieldName, size, precision);
	
	column = db_get_table_column(*table, col);

	db_set_column_host_type(column, ogrType);
	db_set_column_sqltype(column, sqlType);
	db_set_column_name(column, fieldName);
	db_set_column_length(column, size);
	db_set_column_precision(column, precision);

	/* TODO */
	scale = 0;
	/*
	   db_set_column_scale (column, scale);
	 */

	/* TODO */
	db_set_column_null_allowed(column);
	db_set_column_has_undefined_default_value(column);
	db_unset_column_use_default_value(column);

	/* TODO */
	/*
	   db_set_column_select_priv_granted (column);
	   db_set_column_update_priv_granted (column);
	   db_set_column_update_priv_not_granted (column); 
	 */
    }

    if (c) {
	c->cols = cols;
	c->ncols = ncols;
    }
    else {
	G_free(cols);
    }

    return DB_OK;
}
