/*
 * The contents of this file are subject to the AOLserver Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://aolserver.com/.
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is AOLserver Code and related documentation
 * distributed by AOL.
 * 
 * The Initial Developer of the Original Code is America Online,
 * Inc. Portions created by AOL are Copyright (C) 1999 America Online,
 * Inc. All Rights Reserved.
 *
 * Alternatively, the contents of this file may be used under the terms
 * of the GNU General Public License (the "GPL"), in which case the
 * provisions of GPL are applicable instead of those above.  If you wish
 * to allow use of your version of this file only under the terms of the
 * GPL and not to allow others to use your version of this file under the
 * License, indicate your decision by deleting the provisions above and
 * replace them with the notice and other provisions required by the GPL.
 * If you do not delete the provisions above, a recipient may use your
 * version of this file under either the License or the GPL.
 */


/*
 * tcltime.c --
 *
 *	Implement Tcl_Obj type for AOLserver Ns_Time.
 */

static const char *RCSID = "@(#) $Header: /cvsroot/aolserver/aolserver/nsd/tclobj.c,v 1.7 2006/04/13 19:06:54 jgdavidson Exp $, compiled: " __DATE__ " " __TIME__;

#include "nsd.h"

/*
 * Prototypes for procedures defined later in this file:
 */

static void		SetTimeInternalRep(Tcl_Obj *objPtr, Ns_Time *timePtr);
static int		SetTimeFromAny (Tcl_Interp *interp, Tcl_Obj *objPtr);
static void		UpdateStringOfTime(Tcl_Obj *objPtr);

/*
 * The following type defines the Ns_Time type.
 */

static Tcl_ObjType timeType = {
    "ns:time",
    (Tcl_FreeInternalRepProc *) NULL,
    (Tcl_DupInternalRepProc *) NULL,
    UpdateStringOfTime,
    SetTimeFromAny
};

static Tcl_ObjType *intTypePtr;


/*
 *----------------------------------------------------------------------
 *
 * NsTclInitTimeType --
 *
 *	Initialize Ns_Time Tcl_Obj type.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void
NsTclInitTimeType()
{
    Tcl_Obj obj;

    if (sizeof(obj.internalRep) < sizeof(Ns_Time)) {
    	Tcl_Panic("NsTclInitObjs: sizeof(obj.internalRep) < sizeof(Ns_Time)");
    }
    intTypePtr = Tcl_GetObjType("int");
    if (intTypePtr == NULL) {
    	Tcl_Panic("NsTclInitObjs: no int type");
    }
    Tcl_RegisterObjType(&timeType);
}


/*
 *----------------------------------------------------------------------
 *
 * Ns_TclSetTimeObj --
 *
 *	Set a Tcl_Obj to an Ns_Time object.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	String rep is invalidated and internal rep is set.
 *
 *----------------------------------------------------------------------
 */

void
Ns_TclSetTimeObj(Tcl_Obj *objPtr, Ns_Time *timePtr)
{
    Tcl_InvalidateStringRep(objPtr);
    SetTimeInternalRep(objPtr, timePtr);
}


/*
 *----------------------------------------------------------------------
 *
 * Ns_TclGetTimeFromObj --
 *
 *	Return the internal value of an Ns_Time Tcl_Obj.
 *
 * Results:
 *	TCL_OK or TCL_ERROR if not a valid Ns_Time.
 *
 * Side effects:
 *	Object is set to Ns_Time type if necessary.
 *
 *----------------------------------------------------------------------
 */

int
Ns_TclGetTimeFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, Ns_Time *timePtr)
{
    if (objPtr->typePtr == intTypePtr) {
	if (Tcl_GetLongFromObj(interp, objPtr, &timePtr->sec) != TCL_OK) {
	    return TCL_ERROR;
	}
	timePtr->usec = 0;
    } else {
	if (Tcl_ConvertToType(interp, objPtr, &timeType) != TCL_OK) {
	    return TCL_ERROR;
        }
    	*timePtr = *((Ns_Time *) &objPtr->internalRep);
    }
    return TCL_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * UpdateStringOfTime --
 *
 *	Update the string representation for an Ns_Time object.
 *	Note: This procedure does not free an existing old string rep
 *	so storage will be lost if this has not already been done. 
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The object's string is set to a valid string that results from
 *	the Ns_Time-to-string conversion.
 *
 *----------------------------------------------------------------------
 */

static void
UpdateStringOfTime(objPtr)
    register Tcl_Obj *objPtr;	/* Int object whose string rep to update. */
{
    Ns_Time *timePtr = (Ns_Time *) &objPtr->internalRep;
    size_t len;
    char buf[100];

    Ns_AdjTime(timePtr);
    if (timePtr->usec == 0) {
    	len = sprintf(buf, "%ld", timePtr->sec);
    } else {
    	len = sprintf(buf, "%ld:%ld", timePtr->sec, timePtr->usec);
    }
    objPtr->length = len;
    objPtr->bytes = ckalloc(len + 1);
    memcpy(objPtr->bytes, buf, len + 1);
}


/*
 *----------------------------------------------------------------------
 *
 * SetTimeFromAny --
 *
 *	Attempt to generate an Ns_Time internal form for the Tcl object.
 *
 * Results:
 *	The return value is a standard object Tcl result. If an error occurs
 *	during conversion, an error message is left in the interpreter's
 *	result unless "interp" is NULL.
 *
 * Side effects:
 *	If no error occurs, an int is stored as "objPtr"s internal
 *	representation. 
 *
 *----------------------------------------------------------------------
 */

static int
SetTimeFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr)
{
    char *str;
    Ns_Time time;

    str = Tcl_GetString(objPtr);
    if (objPtr->typePtr == intTypePtr || strchr(str, ':') == NULL) {
	if (Tcl_GetLongFromObj(interp, objPtr, &time.sec) != TCL_OK) {
	    return TCL_ERROR;
	}
	time.usec = 0;
    } else if (sscanf(str, "%ld:%ld", &time.sec, &time.usec) != 2) {
	Tcl_AppendResult(interp, "invalid time spec \"", str,
	    "\": expected sec:usec", NULL);
	return TCL_ERROR;
    }
    Ns_AdjTime(&time);
    SetTimeInternalRep(objPtr, &time);
    return TCL_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * SetTimeInternalRep --
 *
 *	Set the internal Ns_Time, freeing a previous internal rep if
 *	necessary.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Object will be an Ns_Time type.
 *
 *----------------------------------------------------------------------
 */

static void
SetTimeInternalRep(Tcl_Obj *objPtr, Ns_Time *timePtr)
{
    Tcl_ObjType *typePtr = objPtr->typePtr;

    if (typePtr != NULL && typePtr->freeIntRepProc != NULL) {
	(*typePtr->freeIntRepProc)(objPtr);
    }
    objPtr->typePtr = &timeType;
    *((Ns_Time *) &objPtr->internalRep) = *timePtr;
    Tcl_InvalidateStringRep(objPtr);
    objPtr->length = 0;  /* ensure there's no stumbling */
}
