/* statusicon.c:
 *
 * Copyright (C) 2010 Youness Alaoui
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */


#include "statusicon.h"

#define QUARTZ_POOL_ALLOC NSAutoreleasePool *pool = \
    [[NSAutoreleasePool alloc] init]
#define QUARTZ_POOL_RELEASE [pool release]

static int icon_counter = 0;
static Tcl_HashTable *icons = NULL;
static Tcl_HashTable *callbacks = NULL;

typedef struct {
  Tcl_Interp *interp;
  Tcl_Obj *cb;
} callback_s;

int Statusicon_Create(ClientData clientData, Tcl_Interp *interp,
		      int objc, Tcl_Obj *CONST objv[])
{
  char name[15];
  Tcl_HashEntry *hPtr = NULL;
  int newHash;
  callback_s *callback = NULL;
  int ret = TCL_OK;
  QuartzStatusIcon *status_item = NULL;

  if(objc != 2) {
    Tcl_WrongNumArgs(interp, 1, objv, "callback");
    return TCL_ERROR;
  }

  QUARTZ_POOL_ALLOC;

  callback = (callback_s *) ckalloc(sizeof(callback_s));
  callback->interp = interp;
  callback->cb = objv[1];
  Tcl_IncrRefCount(callback->cb);

  status_item = [[QuartzStatusIcon alloc] initWithCallback:Statusicon_Callback
					       andUserData:callback];
  if (status_item == NULL) {
    Tcl_DecrRefCount(callback->cb);
    ckfree(callback);
    ret = TCL_ERROR;
  } else {
    sprintf(name, "statusicon%d", ++icon_counter);

    hPtr = Tcl_CreateHashEntry(icons, name, &newHash);
    Tcl_SetHashValue(hPtr, (ClientData) status_item);

    hPtr = Tcl_CreateHashEntry(callbacks, name, &newHash);
    Tcl_SetHashValue(hPtr, (ClientData) callback);

    Tcl_ResetResult(interp);
    Tcl_AppendResult(interp, name, NULL);
  }

  QUARTZ_POOL_RELEASE;
	
  return ret;
}

void Statusicon_Callback(QuartzStatusIcon *status_item, void *user_data,
			 int doubleAction)
{
  callback_s * callback = (callback_s *) user_data;
  Tcl_Obj *action = Tcl_NewStringObj(doubleAction ?
				     "DOUBLE_ACTION" :
				     "ACTION", -1);
  Tcl_Obj *eval = Tcl_NewStringObj("eval", -1);
  Tcl_Obj *command[] = {eval, callback->cb, action};

  Tcl_Obj *cb = callback->cb;
  Tcl_IncrRefCount (eval); 
  Tcl_IncrRefCount (action); 
  Tcl_IncrRefCount (cb); 

  if (Tcl_EvalObjv(callback->interp, 3, command, TCL_EVAL_GLOBAL) == TCL_ERROR) {
    Tcl_BackgroundError(callback->interp);
  }

  Tcl_DecrRefCount (eval); 
  Tcl_DecrRefCount (action); 
  Tcl_DecrRefCount (cb); 
  
}

int Statusicon_SetImage(ClientData clientData, Tcl_Interp *interp,
			int objc, Tcl_Obj *CONST objv[])
{
  QuartzStatusIcon *status_item;
  Tcl_HashEntry *hPtr = NULL;
  char * name = NULL;
  char * path = NULL;

  // We verify the arguments
  if( objc != 3) {
    Tcl_WrongNumArgs(interp, 1, objv, "icon pathToImage");
    return TCL_ERROR;
  } 

  name = Tcl_GetStringFromObj(objv[1], NULL);
  path = Tcl_GetStringFromObj(objv[2], NULL);

  hPtr = Tcl_FindHashEntry(icons, name);
  if (hPtr != NULL) {
    status_item = (QuartzStatusIcon *) Tcl_GetHashValue(hPtr);
  }

  if (!status_item) {
    Tcl_AppendResult (interp, "Invalid StatusIcon : " , name, (char *) NULL);
    return TCL_ERROR;
  }

  QUARTZ_POOL_ALLOC;
  [status_item setImagePath:path];
  QUARTZ_POOL_RELEASE;

  return TCL_OK;
}

int Statusicon_SetAlternateImage(ClientData clientData, Tcl_Interp *interp,
				 int objc, Tcl_Obj *CONST objv[])
{
  QuartzStatusIcon *status_item;
  Tcl_HashEntry *hPtr = NULL;
  char * name = NULL;
  char * path = NULL;

  // We verify the arguments
  if( objc != 3) {
    Tcl_WrongNumArgs(interp, 1, objv, "icon pathToImage");
    return TCL_ERROR;
  } 

  name = Tcl_GetStringFromObj(objv[1], NULL);
  path = Tcl_GetStringFromObj(objv[2], NULL);

  hPtr = Tcl_FindHashEntry(icons, name);
  if (hPtr != NULL) {
    status_item = (QuartzStatusIcon *) Tcl_GetHashValue(hPtr);
  }

  if (!status_item) {
    Tcl_AppendResult (interp, "Invalid StatusIcon : " , name, (char *) NULL);
    return TCL_ERROR;
  }

  QUARTZ_POOL_ALLOC;
  [status_item setAlternateImagePath:path];
  QUARTZ_POOL_RELEASE;

  return TCL_OK;
}

int Statusicon_SetTooltip(ClientData clientData, Tcl_Interp *interp,
			  int objc, Tcl_Obj *CONST objv[])
{
  QuartzStatusIcon *status_item;
  Tcl_HashEntry *hPtr = NULL;
  char * name = NULL;
  char * tooltip = NULL;

  // We verify the arguments
  if( objc != 3) {
    Tcl_WrongNumArgs(interp, 1, objv, "icon tooltip");
    return TCL_ERROR;
  } 

  name = Tcl_GetStringFromObj(objv[1], NULL);
  tooltip = Tcl_GetStringFromObj(objv[2], NULL);

  hPtr = Tcl_FindHashEntry(icons, name);
  if (hPtr != NULL) {
    status_item = (QuartzStatusIcon *) Tcl_GetHashValue(hPtr);
  }

  if (!status_item) {
    Tcl_AppendResult (interp, "Invalid StatusIcon : " , name, (char *) NULL);
    return TCL_ERROR;
  }

  QUARTZ_POOL_ALLOC;
  [status_item setToolTip:tooltip];
  QUARTZ_POOL_RELEASE;

  return TCL_OK;
}

int Statusicon_SetTitle(ClientData clientData, Tcl_Interp *interp,
			int objc, Tcl_Obj *CONST objv[])
{
  QuartzStatusIcon *status_item;
  Tcl_HashEntry *hPtr = NULL;
  char * name = NULL;
  char * title = NULL;

  // We verify the arguments
  if( objc != 3) {
    Tcl_WrongNumArgs(interp, 1, objv, "icon title");
    return TCL_ERROR;
  } 

  name = Tcl_GetStringFromObj(objv[1], NULL);
  title = Tcl_GetStringFromObj(objv[2], NULL);

  hPtr = Tcl_FindHashEntry(icons, name);
  if (hPtr != NULL) {
    status_item = (QuartzStatusIcon *) Tcl_GetHashValue(hPtr);
  }

  if (!status_item) {
    Tcl_AppendResult (interp, "Invalid StatusIcon : " , name, (char *) NULL);
    return TCL_ERROR;
  }

  QUARTZ_POOL_ALLOC;
  [status_item setTitle:title];
  QUARTZ_POOL_RELEASE;

  return TCL_OK;
}

int Statusicon_SetHighlightMode(ClientData clientData, Tcl_Interp *interp,
				int objc, Tcl_Obj *CONST objv[])
{
  QuartzStatusIcon *status_item;
  Tcl_HashEntry *hPtr = NULL;
  char * name = NULL;
  int highlighted = 0;

  // We verify the arguments
  if( objc != 3) {
    Tcl_WrongNumArgs(interp, 1, objv, "icon highlightMode");
    return TCL_ERROR;
  } 

  name = Tcl_GetStringFromObj(objv[1], NULL);
  if (Tcl_GetBooleanFromObj(interp, objv[2], &highlighted) != TCL_OK) 
    return TCL_ERROR;

  hPtr = Tcl_FindHashEntry(icons, name);
  if (hPtr != NULL) {
    status_item = (QuartzStatusIcon *) Tcl_GetHashValue(hPtr);
  }

  if (!status_item) {
    Tcl_AppendResult (interp, "Invalid StatusIcon : " , name, (char *) NULL);
    return TCL_ERROR;
  }

  QUARTZ_POOL_ALLOC;
  [status_item setHighlightMode:highlighted];
  QUARTZ_POOL_RELEASE;


  return TCL_OK;
}
int Statusicon_SetVisible(ClientData clientData, Tcl_Interp *interp,
			  int objc, Tcl_Obj *CONST objv[])
{
  QuartzStatusIcon *status_item;
  Tcl_HashEntry *hPtr = NULL;
  char * name = NULL;
  int visible = 0;

  // We verify the arguments
  if( objc != 3) {
    Tcl_WrongNumArgs(interp, 1, objv, "icon visible");
    return TCL_ERROR;
  } 

  name = Tcl_GetStringFromObj(objv[1], NULL);
  if (Tcl_GetBooleanFromObj(interp, objv[2], &visible) != TCL_OK) 
    return TCL_ERROR;

  hPtr = Tcl_FindHashEntry(icons, name);
  if (hPtr != NULL) {
    status_item = (QuartzStatusIcon *) Tcl_GetHashValue(hPtr);
  }

  if (!status_item) {
    Tcl_AppendResult (interp, "Invalid StatusIcon : " , name, (char *) NULL);
    return TCL_ERROR;
  }

  QUARTZ_POOL_ALLOC;
  [status_item setVisible:visible];
  QUARTZ_POOL_RELEASE;


  return TCL_OK;
}

int Statusicon_Destroy(ClientData clientData, Tcl_Interp *interp,
		       int objc, Tcl_Obj *CONST objv[])
{
  QuartzStatusIcon *status_item;
  callback_s * callback = NULL;
  Tcl_HashEntry *hPtr = NULL;
  char * name = NULL;

  // We verify the arguments
  if( objc != 2) {
    Tcl_WrongNumArgs(interp, 1, objv, "icon");
    return TCL_ERROR;
  } 

  name = Tcl_GetStringFromObj(objv[1], NULL);

  hPtr = Tcl_FindHashEntry(icons, name);
  if (hPtr != NULL) {
    status_item = (QuartzStatusIcon *) Tcl_GetHashValue(hPtr);
  }

  if (!status_item) {
    Tcl_AppendResult (interp, "Invalid StatusIcon : " , name, (char *) NULL);
    return TCL_ERROR;
  }

  QUARTZ_POOL_ALLOC;
  [status_item release];
  QUARTZ_POOL_RELEASE;

  Tcl_DeleteHashEntry(hPtr);

  hPtr = Tcl_FindHashEntry(callbacks, name);
  if (hPtr != NULL) {
    callback = (callback_s *) Tcl_GetHashValue(hPtr);
  }

  if (callback) {
    Tcl_DecrRefCount(callback->cb);
    ckfree(callback);
    Tcl_DeleteHashEntry(hPtr);
  }

  return TCL_OK;
}

int Statusicon_Init(Tcl_Interp *interp)
{
  if (Tcl_InitStubs(interp, "8.4", 0) == NULL) {
    return TCL_ERROR;
  }

  NSApplicationLoad();
  
  icons = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
  Tcl_InitHashTable(icons, TCL_STRING_KEYS);

  callbacks = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
  Tcl_InitHashTable(callbacks, TCL_STRING_KEYS);

  Tcl_CreateObjCommand(interp, "::statusicon::create",
		       Statusicon_Create, NULL, NULL);
  Tcl_CreateObjCommand(interp, "::statusicon::setImage",
		       Statusicon_SetImage, NULL, NULL);
  Tcl_CreateObjCommand(interp, "::statusicon::setAlternateImage",
		       Statusicon_SetAlternateImage, NULL, NULL);
  Tcl_CreateObjCommand(interp, "::statusicon::setTooltip",
		       Statusicon_SetTooltip, NULL, NULL);
  Tcl_CreateObjCommand(interp, "::statusicon::setTitle",
		       Statusicon_SetTitle, NULL, NULL);
  Tcl_CreateObjCommand(interp, "::statusicon::setHighlightMode",
		       Statusicon_SetHighlightMode, NULL, NULL);
  Tcl_CreateObjCommand(interp, "::statusicon::setVisible",
		       Statusicon_SetVisible, NULL, NULL);
  Tcl_CreateObjCommand(interp, "::statusicon::destroy",
		       Statusicon_Destroy, NULL, NULL);
  

  return Tcl_PkgProvide(interp, "statusicon", "0.1");
}

int Statusicon_SafeInit(Tcl_Interp *interp)
{
  return Statusicon_Init(interp);
}
