/***************************************************************
 *
 * MODULE:       v.db.connect
 * 
 * AUTHOR(S):    Markus Neteler
 *               
 * PURPOSE:      sets/prints DB connection for a given vector map
 *               
 * COPYRIGHT:    (C) 2002 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.
 *
 * TODO: - fix -o flag (needs fix in Vect lib)
 *       - add check that key is INTEGER (needs test for column type)
 *       - add check if column exists
 *
 **************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <grass/gis.h>
#include <grass/Vect.h>
#include <grass/dbmi.h>
#include <grass/glocale.h>

int main (int argc, char **argv)
{
    char *input, *mapset;
    struct GModule *module;
    struct Option *inopt, *dbdriver, *dbdatabase, *dbtable, *field_opt, *dbkey;
    struct Flag *overwrite, *print, *columns, *delete, *shell_print;
    dbDriver *driver;
    dbString table_name;
    dbTable *table;
    dbHandle handle;
    struct field_info *fi;
    int field, ret, num_dblinks, i, ncols, col;
    struct Map_info Map;
    char   *drv, *db;
    
    /* set up the options and flags for the command line parser */

    module = G_define_module();
    module->keywords = _("vector, database, attribute table");
    module->description =
	_("Prints/sets DB connection for a vector map to attribute table");

    inopt = G_define_standard_option(G_OPT_V_MAP);

    dbdriver = G_define_option() ;
    dbdriver->key        = "driver" ;
    dbdriver->type       = TYPE_STRING ;
    dbdriver->options    = db_list_drivers();
    dbdriver->required   = NO  ;
    dbdriver->multiple   = NO ;
    dbdriver->description= _("Driver name");
    if ( (drv=G__getenv2("DB_DRIVER",G_VAR_MAPSET)) )
	dbdriver->answer = G_store ( drv );

    dbdatabase = G_define_option() ;
    dbdatabase->key        = "database" ;
    dbdatabase->type       = TYPE_STRING ;
    dbdatabase->required   = NO  ;
    dbdatabase->multiple   = NO ;
    dbdatabase->description= _("Database name");
    if ( (db=G__getenv2("DB_DATABASE",G_VAR_MAPSET)) )
	dbdatabase->answer = G_store ( db );

    dbtable = G_define_option();
    dbtable->key         = "table";
    dbtable->type        = TYPE_STRING;
    dbtable->required    = NO;         
    dbtable->description = _("Table name");

    dbkey = G_define_option() ;
    dbkey->key        = "key" ;
    dbkey->type       = TYPE_STRING ;
    dbkey->required   = NO  ;
    dbkey->multiple   = NO ;
    dbkey->answer    = "cat";
    dbkey->description= _("Key name (must refer to an integer column)");

    field_opt = G_define_standard_option(G_OPT_V_FIELD) ;

    print = G_define_flag();
    print->key               = 'p';
    print->description       = _("Print current map connection parameters and exit");

    shell_print = G_define_flag();
    shell_print->key               = 'g';
    shell_print->description       = _("Print current map connection parameters and exit (shell script style)\n"
				     "\tformat:  layer[/layer name] table key database driver");

    columns = G_define_flag();
    columns->key               = 'c';
    columns->description       = _("Print types/names of table columns for specified layer and exit");

    overwrite = G_define_flag();
    overwrite->key               = 'o';
    overwrite->description       = _("Overwrite connection parameter for certain layer");
    
    delete = G_define_flag();
    delete->key               = 'd';
    delete->description       = _("Delete connection for certain layer (not the table)");

    G_gisinit (argv[0]);

    if (G_parser (argc, argv))
        exit (EXIT_FAILURE);

    /* The check must allow '.' in the name (schema.table) */
    /*
    if (dbtable->answer) {
       if ( db_legal_tablename(dbtable->answer) == DB_FAILED )
            G_fatal_error ( _("Table name '%s' is not valid."), dbtable->answer );
    }
    */

    /* set input vector file name and mapset */
    input = inopt->answer;
    mapset = G_find_vector2 (input, "") ;
    if(field_opt->answer)
       field = atoi (field_opt->answer);
    else
       field = 1;

    if (!mapset)
	G_fatal_error(_("Vector file [%s] not available in search list"), input);
      
    G_debug ( 3, "Mapset = %s", mapset);

    if (print->answer && shell_print->answer)
	G_fatal_error(_("Please choose only one print style"));

    if (print->answer || shell_print->answer || columns->answer)
      Vect_open_old ( &Map, inopt->answer, mapset);
    else
    {
      if ( Vect_open_update_head ( &Map, inopt->answer, G_mapset()) < 1 )
               G_fatal_error(_("Cannot edit vector map stored in other mapset."));
      Vect_hist_command ( &Map );
    }

    if (print->answer || shell_print->answer || columns->answer)
    {
      num_dblinks = Vect_get_num_dblinks(&Map);
      if (num_dblinks <= 0)
         G_fatal_error(_("Database connection for map <%s> is not defined in DB file"), input);
      else /* num_dblinks > 0 */
      {
        if (print->answer || shell_print->answer)
        {
	  if ( !(shell_print->answer) ) {
              fprintf(stderr,"Vector map <%s> is connected by:\n", input);
	  }
          for (i = 0; i < num_dblinks; i++) {
            if ( (fi = Vect_get_dblink( &Map, i)) == NULL)
               G_fatal_error(_("Database connection not defined"));

	    if ( shell_print->answer ) {
		if ( fi->name ) {
                    fprintf(stdout,"%d/%s %s %s %s %s\n", fi->number, fi->name, fi->table, fi->key, 
			                                  fi->database, fi->driver);
		} else {
                    fprintf(stdout,"%d %s %s %s %s\n", fi->number, fi->table, fi->key, 
			                               fi->database, fi->driver);
		}
	    } else { 
		fprintf(stderr,"layer <%d> table <%s> in database <%s> through driver <%s> with key <%s>\n", fi->number, fi->table, fi->database, fi->driver, fi->key);
	    }
          }
        } /* end print */
        else /* columns */
        {
          if ( (fi = Vect_get_field( &Map, field)) == NULL)
               G_fatal_error(_("Database connection not defined for layer <%d>"), field);
          driver = db_start_driver(fi->driver);
          if (driver == NULL)
                G_warning(_("Cannot open driver %s"), fi->driver) ; /* G_fatal_error ? */
          
          db_init_handle (&handle);
          db_set_handle (&handle, fi->database, NULL);
          if (db_open_database(driver, &handle) != DB_OK)
             G_fatal_error(_("Cannot open database <%s>"), fi->database);
          db_init_string(&table_name);
          db_set_string(&table_name, fi->table);
          if(db_describe_table (driver, &table_name, &table) != DB_OK)
             G_fatal_error(_("Cannot open table <%s>"), fi->table);

          ncols = db_get_table_number_of_columns(table);
          for (col = 0; col < ncols; col++) {
	      fprintf (stdout,"%s|%s\n", db_sqltype_name(db_get_column_sqltype(db_get_table_column(table, col))), 
		                         db_get_column_name(db_get_table_column(table, col)));
	  }

          db_close_database(driver);
          db_shutdown_driver(driver);
        }
      } /* end else num_dblinks */
    } /* end print/columns */
    else /* define new dbln settings or delete */
    {
	if ( delete->answer ) {
            Vect_map_del_dblink (  &Map, atoi(field_opt->answer) );
	} else { 
	   if (field_opt->answer && dbtable->answer && dbkey->answer
	       && dbdatabase->answer && dbdriver->answer)
	   {
	     fi = (struct field_info *) G_malloc( sizeof(struct field_info) );
	     fi->name     = NULL;
	     fi->table    = dbtable->answer;
	     fi->key      = dbkey->answer;
	     fi->database = dbdatabase->answer;
	     fi->driver   = dbdriver->answer;
	   
	     ret = Vect_map_check_dblink ( &Map, atoi(field_opt->answer) );
	     G_debug(3, "Vect_map_check_dblink = %d", ret);
	     if ( ret == 1) {
	       /* field already defined */
	       if( !overwrite->answer )
		   G_fatal_error(_("Use -o to overwrite existing link for layer <%d>"),atoi(field_opt->answer));
	       else
	       {
		   if( db_table_exists ( dbdriver->answer, dbdatabase->answer, dbtable->answer) < 1 )
		       G_fatal_error(_("Table <%s> does not exist in database <%s>"),dbtable->answer, dbdatabase->answer);
		   ret = Vect_map_del_dblink (  &Map, atoi(field_opt->answer) );
		   if( Vect_map_add_dblink ( &Map, atoi(field_opt->answer), 
					     fi->name, fi->table, fi->key, fi->database, fi->driver) == 0) 
		   {
		       G_warning (_("The table <%s> is now part of vector map <%s> and may be deleted "
				   "or overwritten by GRASS modules"), dbtable->answer, input);
		   }
	       }
	     }
	     else
	     { /* field not yet defined, add new field */
		if( db_table_exists ( dbdriver->answer, dbdatabase->answer, dbtable->answer) < 1 )
		   G_warning (_("Table <%s> does not exist in database <%s>"),dbtable->answer, dbdatabase->answer);

		if( Vect_map_add_dblink ( &Map, atoi(field_opt->answer), 
					  fi->name, fi->table, fi->key, fi->database, fi->driver) == 0) 
		{
		   G_warning (_("The table <%s> is now part of vector map <%s> and may be deleted "
			       "or overwritten by GRASS modules"), dbtable->answer, input);

		   driver = db_start_driver_open_database (fi->driver, Vect_subst_var (fi->database, &Map) );

		   if ( !driver )
		       G_fatal_error(_("Cannot open database %s by driver %s"), fi->database, fi->driver);

		   if ( db_create_index2(driver, fi->table, fi->key ) != DB_OK )
		       G_warning (_("Cannot create index"));

		   if (db_grant_on_table (driver, fi->table, DB_PRIV_SELECT, DB_GROUP|DB_PUBLIC ) != DB_OK )
		       G_warning (_("Cannot grant privileges on table %s"), fi->table );

		   G_warning (_("Select privileges were granted on the table"));

		   db_close_database_shutdown_driver(driver);
		}
	     }
	   }
	   else /* incomplete parameters given */
	      G_fatal_error(_("For defining a new connection you have to specify these parameters: "
			      "driver, database, table [, key [, layer]]"));
	}
    } /* end define new dbln settings */

    Vect_close ( &Map);
    
    exit(EXIT_SUCCESS);
}

