/* Pau Freixes, pfreixes@milnou.net
 *
 * Example of Consume client, read and write
 */


#include <db.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>

#ifndef ROOTPATH
    #define ROOTPATH "./env"
#endif

#ifndef DATABASE
    #define DATABASE "test"
#endif

int main(int argc, char **argv)
{
    DB_ENV *dbenv;
    DB *dbp;
    DBT key, data;
    DBC * cursorp;
    
    int raw_value, raw_key; // for get and cursor

    pid_t mypid = getpid();

    int ret, i, op;
    char * progname = argv[0];

    if ((ret = db_env_create(&dbenv, 0)) != 0) {
        printf("%s: %s\n", progname, db_strerror(ret));
        goto err;
    }

    dbenv->set_errpfx(dbenv, progname);

    if ( (ret = dbenv->open(dbenv, ROOTPATH, DB_INIT_CDB | DB_INIT_MPOOL , 0)) != 0) 
    { 
        dbenv->err(dbenv, ret, "environment open: %s", ROOTPATH); 
        goto err; 
    }


    if ((ret = db_create(&dbp, dbenv, 0)) != 0) {
        dbenv->err(dbenv, ret, "database create");
        goto err;
    }
   
    if ((ret = dbp->open(dbp, NULL, DATABASE, NULL, DB_BTREE, 0, 0664)) != 0) {
        dbenv->err(dbenv, ret, "DB->open: %s", DATABASE);
        goto err;
    } 

    if ( argv[1] != NULL )
        i = atoi(argv[1] );
    else
        i = 0;

    op = 0;
    while ( 1 )
    {
        // random get or put or cursor
        if ( op == 0 )
        {
            memset(&key, 0, sizeof(DBT));
            key.data = &i;
            key.size = sizeof(int);

            memset(&data, 0, sizeof(DBT));
            data.data = &i;
            data.size = sizeof(int);

            printf("(%d) Try to put\n", mypid);
            ret = dbp->put(dbp, NULL, &key, &data, DB_NOOVERWRITE);
            if ( ret != 0 )
            {
                dbp->err(dbp, ret, "DB->put key = %d value %d", i, i );
            }
            else
                printf("(%d) put finish key = %d, value = %d\n", mypid, i, i);

            op++;
        }
        else if ( op == 1 )
        {

            memset(&key, 0, sizeof(DBT));
            key.data = &i;
            key.size = sizeof(int);

            memset(&data, 0, sizeof(DBT));
            data.data = &raw_value; // in stack
            data.ulen = sizeof(int);
            data.flags = DB_DBT_USERMEM;


            printf("(%d) Try to get\n", mypid);
            ret = dbp->get(dbp, NULL, &key, &data, 0);
            if ( ret != 0 )
            {
                dbp->err(dbp, ret, "DB->get key = %d ", i);
            }
            else
                printf("(%d) get finish key = %d, value = %d\n", mypid, i , raw_value);

            op++;
        }
        else
        {
            printf("(%d) Try to cursor read all database\n", mypid);

            // special cursor demand a false write cursor, it's only for test concurrency between process

            dbp->cursor(dbp, 0, &cursorp, DB_WRITECURSOR);

            memset(&key, 0, sizeof(DBT));
            memset(&data, 0, sizeof(DBT));

            key.data = & raw_key;
            key.ulen = sizeof(int);

            data.data = & raw_value;
            data.ulen = sizeof(int);

            data.flags = key.flags = DB_DBT_USERMEM;
            while ( (ret= cursorp->c_get(cursorp, &key, &data, DB_NEXT)) == 0 )
            {
                printf("(%d) cursor get, key = %d (%d bytes), value = %d (%d bytes)\n", \
                        mypid,  raw_key , key.size, raw_value, data.size);

                // for view concurrence between isolated process
                sleep(1);
            }

            if ( ret != 0 )
            {
                if ( ret != DB_NOTFOUND ) // reached end of data base
                    dbp->err(dbp, ret, "cursor failed");
            }

            if ( cursorp != NULL)
                cursorp->c_close(cursorp);
            printf("(%d) cursor finish\n", mypid);

            op = 0;
        }
        sleep(1);
        i++;
    }

    dbp->close(dbp, 0);
    dbenv->close(dbenv, 0);
 
    exit(0);
err:
    exit(1);
}
