#include <stdlib.h>
#include <grass/gis.h>
#include <grass/dbmi.h>
#include <grass/Vect.h>
#include "local_proto.h"

void cpvalue (struct RASTER_MAP_PTR *from, int fcol, 
        struct RASTER_MAP_PTR *to, int tcol);
double cell_as_dbl(struct RASTER_MAP_PTR *buf, int col);
void set_to_null (struct RASTER_MAP_PTR *buf, int col);

int execute_random (struct rr_state *theState)
{
    long nt;
    long nc;
    struct Cell_head window;
    int nrows, ncols, row, col;
    int infd, outfd;
    char msg[256];
    struct Map_info Out;
    struct field_info *fi;
    dbTable *table;
    dbColumn *column;
    dbString sql;
    dbDriver *driver;
    struct line_pnts *Points;
    struct line_cats *Cats;
    int cat;

    G_get_window (&window);

    nrows = G_window_rows();
    ncols = G_window_cols();

    /* open the data files, input raster should be set-up already */
    infd = theState->fd_old;
    if (infd < 0)
    {
        sprintf (msg, "%s: unable to open raster file [%s]", G_program_name(),
                theState->inraster);
        G_fatal_error (msg);
        exit(1);
    }
    if (theState->outraster != NULL)
    {
        outfd = G_open_raster_new (theState->outraster, theState->buf.type);
        if (outfd < 0)
        {
            sprintf (msg, "%s: unable to create raster file [%s]", 
                G_program_name(), theState->outraster);
            G_fatal_error (msg);
            exit(1);
        }
        theState->fd_new = outfd;
    }
    if (theState->outsites)
    {
        if (theState->z_geometry)
	  Vect_open_new (&Out, theState->outsites, 1);
	else
	  Vect_open_new (&Out, theState->outsites, 0);
        Vect_hist_command ( &Out );

        fi = Vect_default_field_info ( &Out, 1, NULL, GV_1TABLE );

        driver = db_start_driver_open_database ( fi->driver, Vect_subst_var(fi->database,&Out) );
        if ( !driver )
            G_fatal_error ( "Cannot open database %s with driver %s", 
                             Vect_subst_var(fi->database,&Out), fi->driver );
        
        Vect_map_add_dblink ( &Out, 1, NULL, fi->table, "cat", fi->database, fi->driver);

        table = db_alloc_table ( 2 );
        db_set_table_name ( table, fi->table );
        
        column = db_get_table_column (table, 0);
        db_set_column_name ( column,  "cat" );
        db_set_column_sqltype ( column, DB_SQL_TYPE_INTEGER ); 

        column = db_get_table_column (table, 1);
        db_set_column_name ( column,  "value" );
        db_set_column_sqltype ( column, DB_SQL_TYPE_DOUBLE_PRECISION ); 

        if ( db_create_table ( driver, table ) != DB_OK )
            G_warning ( "Cannot create new table" );

        db_begin_transaction ( driver );
        
        Points = Vect_new_line_struct ();
        Cats = Vect_new_cats_struct ();
        db_init_string (&sql);
    }

    if (theState->verbose)
    {
        fprintf (stderr, "Writing ");
        if (theState->outraster)
                fprintf (stderr, "raster file [%s] ", theState->outraster);
        if (theState->outsites && theState->outraster)
                fprintf (stderr, "and ");
        if (theState->outsites)
                fprintf (stderr, "vector file [%s] ", theState->outsites);
        fprintf (stderr, "... ");
        G_percent (0, theState->nRand, 2);
    }

    init_rand();
    nc = (theState->use_nulls) ? theState->nCells : 
            theState->nCells - theState->nNulls ;
    nt = theState->nRand;
    cat = 1;
    for (row = 0; row < nrows && nt ; row++)
    {
        if (G_get_raster_row (infd, theState->buf.data.v, row, theState->buf.type) < 0)
        {
            sprintf (msg, "%s: can't read raster file [%s]",
                G_program_name(), theState->inraster);
            G_fatal_error (msg);
            exit(1);
        }

        for (col = 0; col < ncols && nt ; col++)
        {
            if (!theState->use_nulls && is_null_value(theState->buf, col))
                continue;

            if (make_rand() % nc < nt)
            {
                nt--;
                if (is_null_value(theState->buf, col))
                        cpvalue(&theState->nulls, 0, &theState->buf, col);

                if (theState->outsites)
                {
                    double x, y, val;
                    char buf[500];

                    Vect_reset_line ( Points );
                    Vect_reset_cats ( Cats );
                    
                    x = window.west  + (col + .5) * window.ew_res;
                    y = window.north - (row + .5) * window.ns_res;
                    
                    val = cell_as_dbl(&theState->buf, col);

                    if (theState->z_geometry)
		      Vect_append_point ( Points, x, y, val );
		    else
		      Vect_append_point ( Points, x, y, 0.0 );
                    Vect_cat_set (Cats, 1, cat);

                    Vect_write_line ( &Out, GV_POINT, Points, Cats );

                    sprintf (buf, "insert into %s values ( %d, %f )", fi->table, cat, val );
                    db_set_string ( &sql, buf );
                    
                    if (db_execute_immediate (driver, &sql) != DB_OK )
                        G_fatal_error ( "Cannot insert new row: %s", db_get_string ( &sql )  );

                    cat++;
                }
                G_percent ((theState->nRand - nt), theState->nRand, 2);
            }
            else
                set_to_null(&theState->buf, col);

            nc-- ;
        }

        while (col < ncols)
                set_to_null(&theState->buf, col++);

        if (theState->outraster)
            G_put_raster_row(outfd, theState->buf.data.v, theState->buf.type);
    }

    /* Catch any remaining rows in the window*/
    if (theState->outraster && row < nrows)
    {
        for ( col = 0 ; col < ncols; col++)
            set_to_null(&theState->buf, col);
        
        for ( ; row < nrows ; row++)
            G_put_raster_row(outfd, theState->buf.data.v, theState->buf.type);
    }
            
    if (nt > 0)
        G_warning("%s: Only created %ld random sites",
                G_program_name(), theState->nRand - nt);

    /* close files */
    G_close_cell(infd);
    if (theState->outsites) {
        db_commit_transaction ( driver );
        db_close_database_shutdown_driver ( driver );
        Vect_build (&Out, stderr);
        Vect_close (&Out);
    }
    if (theState->outraster)
        G_close_cell(outfd);

    return 0;
} /* execute_random() */


void cpvalue (struct RASTER_MAP_PTR *from, int fcol, 
        struct RASTER_MAP_PTR *to, int tcol)
{
    switch (from->type)
    {
        case CELL_TYPE:
            to->data.c[tcol] = from->data.c[fcol]; break;
        case FCELL_TYPE:
            to->data.f[tcol] = from->data.f[fcol]; break;
        case DCELL_TYPE:
            to->data.d[tcol] = from->data.d[fcol]; break;
    }
}

double cell_as_dbl(struct RASTER_MAP_PTR *buf, int col)
{
    switch(buf->type)
    {
        case CELL_TYPE:
            return (double) buf->data.c[col];
        case FCELL_TYPE:
            return (double) buf->data.f[col];
        case DCELL_TYPE:
            return (double) buf->data.d[col];
    }
}


void set_to_null (struct RASTER_MAP_PTR *buf, int col)
{
    switch(buf->type)
    {
        case CELL_TYPE:
            G_set_c_null_value(&(buf->data.c[col]), 1); break;
        case FCELL_TYPE:
            G_set_f_null_value(&(buf->data.f[col]), 1); break;
        case DCELL_TYPE:
            G_set_d_null_value(&(buf->data.d[col]), 1); break;
    }
}

int
is_null_value (struct RASTER_MAP_PTR buf, int col)

{
    switch(buf.type)
    {
        case CELL_TYPE:
            return G_is_c_null_value(&buf.data.c[col]);
            break;
        case FCELL_TYPE:
            return G_is_f_null_value(&buf.data.f[col]);
            break;
        case DCELL_TYPE:
            return G_is_d_null_value(&buf.data.d[col]);
            break;
    }

    return -1;
}


/* vim: set softtabstop=4 shiftwidth=4 expandtab: */
