/*
 * app.c --
 *
 * This file implements the toplevel container gnomeApp
 *
 * Copyright (c) 2001 - 2002 P.G. Baum
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 */

/*
   History:
        07: cleanups, addToolbar
   2001-03: Begin of developement
 */

#include "gnocl.h"
#include <string.h>
#include <assert.h>

/*
static void appDelFunc( ClientData data )
{
   GnoclAppInfo *gbc = (GnoclAppInfo *)data;
   if( gbc->widget ) destroy?
   g_free( gbc );
}
*/

typedef struct
{
   Gnocl_StringBool title;
   Gnocl_StringBool contents;
   Gnocl_StringBool menubar;
   Gnocl_StringBool statusbar;
   Gnocl_StringBool toolbar;
   Gnocl_BoolBool   resizable;
   Gnocl_IntBool    width;
   Gnocl_IntBool    height;
} AppOptions;

typedef struct
{
   char *name;
   char *appName;
   GnomeApp *app;
} AppParams;

static const GnoclWidgetOptions appOptions[] = 
{
   { "-title", GNOCL_STRING, GNOCL_OFFSET( AppOptions, title ) },
   { "-contents", GNOCL_STRING, GNOCL_OFFSET( AppOptions, contents ) },
   { "-menubar", GNOCL_STRING, GNOCL_OFFSET( AppOptions, menubar ) },
   { "-statusbar", GNOCL_STRING, GNOCL_OFFSET( AppOptions, statusbar ) },
   { "-toolbar", GNOCL_STRING, GNOCL_OFFSET( AppOptions, toolbar ) },
   { "-resizable", GNOCL_BOOL, GNOCL_OFFSET( AppOptions, resizable ) },
   { "-width", GNOCL_INT, GNOCL_OFFSET( AppOptions, width ) },
   { "-height", GNOCL_INT, GNOCL_OFFSET( AppOptions, height ) },
   { NULL, 0, 0 }
};

typedef struct
{
   Gnocl_StringBool name;
   Gnocl_ObjBool    behavior;
   Gnocl_ObjBool    placement;
   Gnocl_IntBool    bandNum;
   Gnocl_IntBool    bandPos;
   Gnocl_IntBool    offset;
} AddToolbarOpts;

static GnoclWidgetOptions addToolBarOptions[] = 
{
   { "-name", GNOCL_STRING, GNOCL_OFFSET( AddToolbarOpts, name ) },
   { "-behavior", GNOCL_OBJ, GNOCL_OFFSET( AddToolbarOpts, behavior ) },
   { "-placement", GNOCL_OBJ, GNOCL_OFFSET( AddToolbarOpts, placement ) },
   { "-bandNum", GNOCL_INT, GNOCL_OFFSET( AddToolbarOpts, bandNum ) },
   { "-bandPos", GNOCL_INT, GNOCL_OFFSET( AddToolbarOpts, bandPos ) },
   { "-offset", GNOCL_INT, GNOCL_OFFSET( AddToolbarOpts, offset ) },
   { NULL, 0, 0 }
};

static void freeToolBarOpts( AddToolbarOpts *p )
{
   GNOCL_FREE_MEM_OPT( p->name );
   GNOCL_FREE_TCL_OPT( p->behavior );
   GNOCL_FREE_TCL_OPT( p->placement );
}


static void freeOpts( AppOptions *p )
{
   /* nothing to do */
}
/*
   test, if params are valid
   set params and show app with new settings
*/
static int appConfigure( Tcl_Interp *interp, AppParams *para, 
      AppOptions *popt )
{
   if( popt->width.changed ) 
      gtk_window_set_default_size( GTK_WINDOW( para->app ), 
            popt->width.val, -1 );

   if( popt->height.changed ) 
      gtk_window_set_default_size( GTK_WINDOW( para->app ), 
            -1, popt->height.val );
 
   if( popt->resizable.changed )
   {
      if( popt->resizable.val )
         gtk_window_set_policy( GTK_WINDOW( para->app ), 0, 1, 0 );
      else
         gtk_window_set_policy( GTK_WINDOW( para->app ), 0, 0, 1 );
   }

   if( popt->contents.changed )
   {
      GtkWidget *childWidget = gnoclGetWidgetFromName( popt->contents.val, 
            interp );
      if( childWidget == NULL )
         return TCL_ERROR;

      gnome_app_set_contents( para->app, childWidget );

   }
   if( popt->menubar.changed )
   {
      GtkWidget *childWidget = gnoclGetWidgetFromName( popt->menubar.val, 
            interp );
      if( childWidget == NULL )
         return TCL_ERROR;

      if( !GTK_CHECK_TYPE( childWidget, GTK_TYPE_MENU_BAR ) )
      {
         Tcl_AppendResult( interp, "window \"", 
               popt->menubar.val, "\" is not a menubar.", (char *)NULL );
         return TCL_ERROR;
      }
      gnome_app_set_menus( para->app, GTK_MENU_BAR( childWidget ) );

/*
printf( "menubar: %p %p\n", childWidget, gtk_widget_get_toplevel( childWidget ) );
*/
   }
   if( popt->statusbar.changed )
   {
      GtkWidget *childWidget = gnoclGetWidgetFromName( popt->statusbar.val,
            interp );
      if( childWidget == NULL )
         return TCL_ERROR;

      /* if( !GTK_IS_STATUSBAR( childWidget ) ) */
      if( !GNOME_IS_APPBAR( childWidget ) )
      {
         Tcl_AppendResult( interp, "window \"", 
               popt->statusbar.val, "\" is not a appbar.", (char *)NULL );
         return TCL_ERROR;
      }
      gnome_app_set_statusbar( para->app, childWidget );
      gnoclRegisterHintAppbar( NULL, GNOME_APPBAR( childWidget ) );

/*
printf( "statusbar: %p %p\n", childWidget, gtk_widget_get_toplevel( childWidget ) );
*/
   }
   if( popt->toolbar.changed )
   {
      GtkWidget *childWidget = gnoclGetWidgetFromName( popt->toolbar.val,
            interp );
      if( childWidget == NULL )
         return TCL_ERROR;

      if( !GTK_IS_TOOLBAR( childWidget ) )
      {
         Tcl_AppendResult( interp, "window \"", 
               popt->toolbar.val, "\" is not a toolbar.", (char *)NULL );
         return TCL_ERROR;
      }
      gnome_app_set_toolbar( para->app, GTK_TOOLBAR( childWidget ) );
   }
   /* FIXME: how to set title after creation? */

   return TCL_OK;
}

static int gnoclGetDockItemBehavior( Tcl_Interp *interp, Tcl_Obj *obj, 
      GnomeDockItemBehavior *pb )
{
   char *txt[] = { "normal", "exclusive", "neverFloating", 
         "neverVertical", "neverHorizontal", "locked", NULL };
   GnomeDockItemBehavior behavs[] = { 
         GNOME_DOCK_ITEM_BEH_NORMAL,
         GNOME_DOCK_ITEM_BEH_EXCLUSIVE,
         GNOME_DOCK_ITEM_BEH_NEVER_FLOATING,
         GNOME_DOCK_ITEM_BEH_NEVER_VERTICAL,
         GNOME_DOCK_ITEM_BEH_NEVER_HORIZONTAL,
         GNOME_DOCK_ITEM_BEH_LOCKED };
   int k, no;

   *pb = GNOME_DOCK_ITEM_BEH_NORMAL;

   if( Tcl_ListObjLength( interp, obj, &no ) != TCL_OK )
   {
      Tcl_SetResult( interp, "behavior must be a list of flags",
            TCL_STATIC );
      return TCL_ERROR;
   }

   for( k = 0; k < no; ++k )
   {
      int idx;
      Tcl_Obj *tp;

      if( Tcl_ListObjIndex( interp, obj, k, &tp ) != TCL_OK )
         return TCL_ERROR;

      if( Tcl_GetIndexFromObj( interp, tp, txt, "dock item behavior",
            TCL_EXACT, &idx ) != TCL_OK )
         return TCL_ERROR;
      *pb |= behavs[idx];
   }

   return TCL_OK;
}

static int gnoclGetDockPlacement( Tcl_Interp *interp, Tcl_Obj *obj, 
      GnomeDockPlacement *place )
{
   char *txt[] = { "top", "right", "bottom", "left", "floating", NULL };
   GnomeDockPlacement placements[] = { 
         GNOME_DOCK_TOP, GNOME_DOCK_RIGHT, GNOME_DOCK_BOTTOM,
         GNOME_DOCK_LEFT, GNOME_DOCK_FLOATING };

   int idx;
   if( Tcl_GetIndexFromObj( interp, obj, txt, "scrollbar policy",
         TCL_EXACT, &idx ) != TCL_OK )
      return TCL_ERROR;
   *place = placements[idx];

   return TCL_OK;
}


static int addToolbar( AppParams *para, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[] )
{
   AddToolbarOpts params;
   GtkWidget *toolbar;
   const gchar *name = NULL;
   GnomeDockItemBehavior behavior = GNOME_DOCK_ITEM_BEH_NORMAL;
   GnomeDockPlacement placement = GNOME_DOCK_TOP;

   if( objc < 3 )
   {
      /* app addToolbar toolbar */
      Tcl_WrongNumArgs( interp, 2, objv, "toolbar-ID ?option val ...?" );
            return TCL_ERROR;
   }

   toolbar = gnoclGetWidgetFromName( Tcl_GetString( objv[2] ), interp );
   if( toolbar == NULL )
      return TCL_ERROR;

   if( !GTK_IS_TOOLBAR( toolbar ) )
   {
      Tcl_AppendResult( interp, "window \"",  Tcl_GetString( objv[2] ), 
            "\" is not a toolbar.", (char *)NULL );
      return TCL_ERROR;
   }

   
   memset( &params, 0, sizeof( params ) );
   params.bandNum.val = 1;
   params.bandPos.val = 1;
   params.offset.val = 0;

   if( gnoclParseAllWidgetOpts( interp, objc, objv,  2,
         addToolBarOptions, (char *)&params ) != TCL_OK )
   {
      freeToolBarOpts( &params );
      return TCL_ERROR;
   }
   
   if( params.name.changed )
      name = params.name.val;
   else
      name = "";

   if( params.behavior.changed )
   {
      if( gnoclGetDockItemBehavior( interp, params.behavior.val, 
            &behavior ) != TCL_OK )
      {
         freeToolBarOpts( &params );
         return TCL_ERROR;
      }
   }

   if( params.placement.changed )
   {
      if( gnoclGetDockPlacement( interp, params.placement.val, 
            &placement ) != TCL_OK )
      {
         freeToolBarOpts( &params );
         return TCL_ERROR;
      }
   }

   gnome_app_add_toolbar( para->app, GTK_TOOLBAR( toolbar ), 
         name, behavior, placement, 
         params.bandNum.val, params.bandPos.val, params.offset.val );


   freeToolBarOpts( &params );

   return TCL_OK;
}

static int appFunc( ClientData data, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[] )
{
   static char *cmds[] = { GNOCL_STD_CMD_NAMES,
         "configure", "addToolbar",
         "message", "flash", "error", "warning", "question",
         "okCancel", "request", "progress",
         NULL };
   enum cmdIdx { GNOCL_STD_CMD_ENUMS,
         ConfigureIdx, AddToolbarIdx,
         MessageIdx, FlashIdx, ErrorIdx, WarningIdx, QuestionIdx,
         OkCancelIdx, RequestIdx, ProgressIdx };
   AppParams *para = (AppParams *)data;
   int idx;

   if( Tcl_GetIndexFromObj( interp, objv[1], cmds, "command", 
         TCL_EXACT, &idx ) != TCL_OK )
      return TCL_ERROR;

   switch( idx )
   {
      GNCOL_STD_CMD_CASE( interp, objc, objv, GTK_WIDGET( para->app ) );
            break;

      case ConfigureIdx:
            {
               AppOptions opt;
               int ret = TCL_ERROR;

               memset( &opt, 0, sizeof( opt ) );
               if( gnoclParseAllWidgetOpts( interp, objc, objv, 1,
                     appOptions, (char *)&opt ) == TCL_OK )
                  ret = appConfigure( interp, para, &opt );

               freeOpts( &opt );
               if( ret != TCL_OK )
                  return TCL_ERROR;
            }
            break;
      case AddToolbarIdx:
               return addToolbar( para, interp, objc, objv );

      case MessageIdx:
      case FlashIdx:
      case ErrorIdx:
      case WarningIdx:
            {
               const char *txt;
               if( objc != 3 )
               {
                  Tcl_WrongNumArgs( interp, 2, objv, "text" );
                  return TCL_ERROR;
               }
               txt = Tcl_GetString( objv[2] );
               switch( idx )
               {
                  case MessageIdx:  gnome_app_message( para->app, txt ); 
                                    break;
                  case FlashIdx:    gnome_app_flash( para->app, txt );
                                    break;
                  case ErrorIdx:    gnome_app_error( para->app, txt );
                                    break;
                  case WarningIdx:  gnome_app_warning( para->app, txt );
                                    break;
                  default:          assert( 0 );
               }
            }
            break;
      case QuestionIdx:
      case OkCancelIdx:
      case RequestIdx:
            /* TODO:  */
            break;
      case ProgressIdx:
            {
               GtkProgress *progress;
               double perc;

               /* FIXME: is there a documented way? */
               if( para->app->statusbar == NULL )
               {
                  Tcl_SetResult( interp, "app has no status bar", 
                        TCL_STATIC );
                  return TCL_ERROR; 
               }
               progress = gnome_appbar_get_progress( 
                     GNOME_APPBAR( para->app->statusbar ) );
               if( progress == NULL )
               {
                  Tcl_SetResult( interp, "status bar has no progress bar", 
                        TCL_STATIC );
                  return TCL_ERROR; 
               }
               if( objc != 3 )
               {
                  Tcl_WrongNumArgs( interp, 2, objv, "percent" );
                  return TCL_ERROR;
               }
               if( Tcl_GetDoubleFromObj( interp, objv[2], &perc ) != TCL_OK )
                  return TCL_ERROR;
               perc /= 100.;
               if( perc > 1. )
                  perc = 1.;
               else if( perc < 0 )
                  perc = 0;
               gtk_progress_set_percentage( progress, perc );
            }
   }

   return TCL_OK;
}


int gnoclAppCmd( ClientData data, Tcl_Interp *interp,
      int objc, Tcl_Obj * const objv[] )
{
   AppOptions opt;
   AppParams *para;

   memset( &opt, 0, sizeof( opt ) );
   if( objc < 2 )
   {
      Tcl_WrongNumArgs( interp, 1, objv, "appName ?option val ...?" );
      return TCL_ERROR;
   }

   if( gnoclParseAllWidgetOpts( interp, objc, objv, 1,
         appOptions, (char *)&opt ) != TCL_OK )
      return TCL_ERROR;

   para = g_new( AppParams, 1 );
   para->appName = Tcl_GetString( objv[1] );
   para->name = gnoclGetAutoWidgetId(); /* must be uniq! */

   para->app = GNOME_APP( gnome_app_new( para->appName, 
         opt.title.val ) );
   if( appConfigure( interp, para, &opt ) != TCL_OK )
   {
      /* FIXME: g_free para->name ... */
      gtk_widget_destroy( GTK_WIDGET( para->app ) );
      return TCL_ERROR;
   }

   gnoclMemNameAndWidget( para->name, GTK_WIDGET( para->app ) );
   gtk_widget_show( GTK_WIDGET( para->app ) );

   Tcl_CreateObjCommand( interp, para->name, appFunc, para, 
         NULL /* FIXME: delFunc */ );
   Tcl_SetObjResult( interp, Tcl_NewStringObj( para->name, -1 ) );

/*
gtk_window_add_accel_group( GTK_WINDOW( para->app ), gnoclGetAccelGroup() );
printf( "added accel\n" );
*/

   return TCL_OK;
}

/*
   eigenes Toplevel
      menu
      menuBar
      toolBar

   Toplevel wie app
      statusBar
*/
