/*
  $Source: /local/data/cvs/yellowbank/postgres/src/uuid/y_uuid.c,v $
  $Revision: 1.7 $
  $State: Exp $
  $Date: 2007/10/20 01:19:25 $
  $Author: yrp001 $
  $Locker:  $

  Copyright (c) 2006
  Ronald Peterson
  (Y) Yellowbank
  All rights reserved.  Applicable BSD license terms can be found in
  the associated LICENSE file.

  Links to the Universally Unique ID library created by Theodore
  Y. Ts'o to provide PostgreSQL C functions for generating UUID's as
  bytea values.  Also provides functions for converting bytea uuid
  values to and from their standard canonical hexadecimal
  representation, and another function to extract the time portion of
  time-based UUID's.
*/

/* PostgreSQL includes */
#include "postgres.h"
#include "fmgr.h"
#include "utils/datetime.h"

/* external requirements */
#include <uuid/uuid.h>
#include <sys/time.h>
#include <time.h>
#include <string.h>

#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif

// 8.2 -> 8.3 macro change
#ifndef SET_VARSIZE
#define SET_VARSIZE(v,l) (VARATT_SIZEP(v) = (l))
#endif

Datum y_uuid_generate( PG_FUNCTION_ARGS );
Datum y_uuid_generate_random( PG_FUNCTION_ARGS );
Datum y_uuid_generate_time( PG_FUNCTION_ARGS );
Datum y_uuid_unparse( PG_FUNCTION_ARGS );
Datum y_uuid_parse( PG_FUNCTION_ARGS );
Datum y_uuid_time( PG_FUNCTION_ARGS );

////////////////////////////////////////////////////////////////////////
PG_FUNCTION_INFO_V1( y_uuid_generate );
Datum
y_uuid_generate( PG_FUNCTION_ARGS )
{
   void* ret;
   bytea* result;
   uuid_t out;
   uuid_generate( out );

   result = (bytea*)palloc(VARHDRSZ + 16);
   SET_VARSIZE( result, VARHDRSZ + 16 );
   ret = memcpy( VARDATA( result ), out, 16 );
   if( ret == NULL ) {
      exit(-1);
   }
   PG_RETURN_BYTEA_P( result );
}


////////////////////////////////////////////////////////////////////////
PG_FUNCTION_INFO_V1( y_uuid_generate_random );
Datum
y_uuid_generate_random( PG_FUNCTION_ARGS )
{
   void* ret;
   bytea* result;
   uuid_t out;
   uuid_generate_random( out );

   result = (bytea*)palloc(VARHDRSZ + 16);
   SET_VARSIZE( result, VARHDRSZ + 16 );
   ret = memcpy( VARDATA( result ), out, 16 );
   if( ret == NULL ) {
      exit(-1);
   }
   PG_RETURN_BYTEA_P( result );
}


////////////////////////////////////////////////////////////////////////
PG_FUNCTION_INFO_V1( y_uuid_generate_time );
Datum
y_uuid_generate_time( PG_FUNCTION_ARGS )
{
   void* ret;
   bytea* result;
   uuid_t out;
   uuid_generate_time( out );

   result = (bytea*)palloc(VARHDRSZ + 16);
   SET_VARSIZE( result, VARHDRSZ + 16 );
   ret = memcpy( VARDATA( result ), out, 16 );
   if( ret == NULL ) {
      exit(-1);
   }
   PG_RETURN_BYTEA_P( result );
}


////////////////////////////////////////////////////////////////////////
PG_FUNCTION_INFO_V1( y_uuid_unparse );
Datum
y_uuid_unparse( PG_FUNCTION_ARGS )
{
   bytea *uuid;
   uuid_t uu;
   char* s;
   text* result;

   if( PG_ARGISNULL(0) ) {
      PG_RETURN_NULL();
   }
   uuid = PG_GETARG_BYTEA_P(0);

   memcpy( uu, VARDATA( uuid ), 16 );

   s = (char*)palloc(37);

   uuid_unparse( uu, s );

   result = (text*)palloc( VARHDRSZ + 36 );
   SET_VARSIZE( result, VARHDRSZ + 36 );

   memcpy( VARDATA( result ), s, 36 );

   PG_RETURN_TEXT_P( result );
}



////////////////////////////////////////////////////////////////////////
PG_FUNCTION_INFO_V1( y_uuid_parse );
Datum
y_uuid_parse( PG_FUNCTION_ARGS )
{
   text *uuid;
   char* s;
   bytea* result;
   uuid_t uu;

   if( PG_ARGISNULL(0) ) {
      PG_RETURN_NULL();
   }
   uuid = PG_GETARG_TEXT_P(0);

   s = (char*)palloc(37);
   memset( s, '\0', 37 );
   memcpy( s, VARDATA( uuid ), 36 );

   uuid_parse( s, uu );

   result = (bytea*)palloc( VARHDRSZ + 16 );
   SET_VARSIZE( result, VARHDRSZ + 16 );

   memcpy( VARDATA( result ), uu, 16 );

   PG_RETURN_BYTEA_P( result );
}



////////////////////////////////////////////////////////////////////////
PG_FUNCTION_INFO_V1( y_uuid_time );
Datum
y_uuid_time( PG_FUNCTION_ARGS )
{
   typedef unsigned int uint;

   bytea *uuid;
   uuid_t uu;
   struct timeval tv;
   time_t t;
   Timestamp ts;
   uint epoch_offset;

   if( PG_ARGISNULL(0) ) {
      PG_RETURN_NULL();
   }
   uuid = PG_GETARG_BYTEA_P(0);

   epoch_offset = (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY;

   memcpy( uu, VARDATA( uuid ), 16 );

   t = uuid_time( uu, &tv );

#ifdef HAVE_INT64_TIMESTAMP
   ts = (tv.tv_sec - epoch_offset) * 1000000 + tv.tv_usec;
#else
   ts = (double)((tv.tv_sec - epoch_offset) + (tv.tv_usec / 1000000.0));
#endif

   PG_RETURN_TIMESTAMP( ts );
}



////////////////////////////////////////////////////////////////////////
// superfluous, but leaving here in commented form for now.
// 
// PG_FUNCTION_INFO_V1( y_uuid_time_text );
// Datum
// y_uuid_time_text( PG_FUNCTION_ARGS )
// {
// 	if( PG_ARGISNULL(0) ) {
// 		PG_RETURN_NULL();
// 	}
// 	bytea* uuid = PG_GETARG_BYTEA_P(0);
// 
// 	typedef unsigned int uint;
// 
// 	uuid_t uu;
// 	char* ts;
// 	text* result;
// 	struct timeval tvfine;
// 	time_t t;
// 	struct tm tst;
// 
// 	memcpy( uu, VARDATA( uuid ), 16 );
// 
// 	ts = (char*)palloc(27);
// 
// 	t = uuid_time( uu, &tvfine );
// 	gmtime_r( &t, &tst );
// 
// //	yyyy-mm-dd hh:mm:ss.uuuuuu
// 	strftime( ts, 20, "%F %T", &tst );
// 	snprintf( ts + 19, 8, ".%06d", (uint)tvfine.tv_usec );
// 
// 	result = (text*)palloc( VARHDRSZ + strlen( ts ) );
// 	VARATT_SIZEP( result ) = VARHDRSZ + strlen( ts );
// 
// 	memcpy( VARDATA( result ), ts, strlen( ts ) );
// 
// 	PG_RETURN_TEXT_P( result );
// }

