


/*
 * GtkScare is a runner for Adrift text adventure games.
 * This program is released under GNU GPL. See the file COPYING.TXT for
 * details.
 * Copyright (c) 2004 Pallav Nawani
 *
 */


/*! 
*
* \file gtkscare.c
* 	No description yet
* 	
* 	
* \author Pallav Nawani
* 
*/

#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <stdlib.h>

#include "support.h"
#include "gtkscare.h"
#include "interface.h"

extern Gtk_sdata *idata;
extern char fstring[256];

void sdata_init(Gtk_sdata *sdata)
{
   if(NULL == sdata)
      return;

   sdata->userin[0]	= '\0';
   sdata->gname[0]	= '\0';
   strcpy(sdata->name, "./");

   sdata->num_cmds	= 0;
   sdata->ipoint	= 0;
   sdata->spoint	= 0;

   sdata->savefile	= NULL;
   sdata->log		= NULL;
   sdata->debuglog	= NULL;
  
   sdata->tsize		= 0;
   sdata->ftop		= 0;

   sdata->ftag		= NULL;
   
   sdata->got_input	= 0;
   sdata->got_key	= 0;
   sdata->got_yesno	= 0;
   sdata->yesno		=-1;
   sdata->menu		= 0;
   sdata->command	=-1;

   sdata->textw		= NULL;
   sdata->curw		= NULL;
   sdata->mwin		= NULL;
   sdata->entry		= NULL;
   sdata->sbar		= NULL;
   sdata->mverb		= NULL;
   sdata->debugger	= NULL;
   sdata->sgame		= NULL;

   sdata->gp		= NULL;
}


/*
 *
 * This is the string output function for scare
 */

void os_print_string(const sc_char *string)
{

   GtkTextBuffer *tbuf;
   GtkTextIter giter;
   GtkTextIter siter;
   GtkTextView *tview;
   GtkTextMark *tmark;
   const char *cptr;
   char warn[] = "GtkScare: NULL STRING!\n";
   int i;
  
   cptr = string;
   if(NULL == string)
      cptr = warn;

   tview = GTK_TEXT_VIEW(idata->textw);
   tbuf = gtk_text_view_get_buffer(tview);

   gtk_text_buffer_get_end_iter(tbuf, &giter);
   tmark = gtk_text_buffer_create_mark(tbuf,
	 "datamark",
	 &giter,
	 TRUE);
   
   gtk_text_buffer_place_cursor(tbuf, &giter);
   gtk_text_buffer_insert(tbuf, &giter, cptr, -1);
   gtk_text_view_scroll_to_mark(tview,
	 gtk_text_buffer_get_insert(tbuf),
	 0.2, 1, 0, 0.5);
   
   gtk_text_buffer_get_end_iter(tbuf, &giter);
   gtk_text_buffer_get_iter_at_mark(tbuf, &siter, tmark);
   gtk_text_buffer_delete_mark(tbuf, tmark);

   for(i = 1; i < idata->tsize; i++)
      gtk_text_buffer_apply_tag_by_name(tbuf,
	    idata->tags[i], &siter, &giter);

   if(idata->ftag != NULL)
      gtk_text_buffer_apply_tag(tbuf,
	    idata->ftag,
	    &siter, &giter);

   /* kill all tags - but don't delete them */
   idata->ftag = NULL;
   idata->ftop = 0;
   idata->tsize= 1;


}


/*
 *
 * This is the string output function for scare, debug version
 */
void os_print_string_debug(const sc_char *string)
{

   GtkTextBuffer *tbuf;
   GtkTextIter giter;
   GtkTextIter siter;
   GtkTextView *tview;
   GtkTextMark *tmark;
   const char *cptr;
   char warn[] = "GtkScare: NULL STRING!\n";
  
   cptr = string;
   if(NULL == string)
      cptr = warn;

   if(idata->debugger != NULL) {
      tview = (GtkTextView *)lookup_widget(idata->debugger, "debugtextview");
   }
   else   
      tview = GTK_TEXT_VIEW(idata->textw);

   tbuf = gtk_text_view_get_buffer(tview);

   gtk_text_buffer_get_end_iter(tbuf, &giter);
   tmark = gtk_text_buffer_create_mark(tbuf,
	 "datamark",
	 &giter,
	 TRUE);
  
   gtk_text_buffer_place_cursor(tbuf, &giter);
   gtk_text_buffer_insert(tbuf, &giter, cptr, -1);
   gtk_text_view_scroll_to_mark(tview,
	 gtk_text_buffer_get_insert(tbuf),
	 0.2, 1, 0, 0.5);
   
   gtk_text_buffer_get_end_iter(tbuf, &giter);
   gtk_text_buffer_get_iter_at_mark(tbuf, &siter, tmark);
   gtk_text_buffer_delete_mark(tbuf, tmark);

   gtk_text_buffer_apply_tag_by_name(tbuf,
	 "debuginfo", &siter, &giter);
   
   /* kill all tags - but don't delete them */
   idata->ftag = NULL;
   idata->ftop = 0;
   idata->tsize= 1;

}



/*
 *
 * This function creates tags for use in scaling the fonts, giving
 * bold, italic, or underlined output.
 *
 */
void make_tags(Gtk_sdata *sdata, GtkTextBuffer *buffer)
{

   /* create tags for text output formatting */
   gtk_text_buffer_create_tag (buffer, "italic",
	 "style", PANGO_STYLE_ITALIC, NULL);

   gtk_text_buffer_create_tag (buffer, "bold",
	 "weight", PANGO_WEIGHT_BOLD, NULL);

   gtk_text_buffer_create_tag (buffer, "underline",
	 "underline", PANGO_UNDERLINE_SINGLE, NULL);

   gtk_text_buffer_create_tag (buffer, "center",
	 "justification", GTK_JUSTIFY_CENTER, NULL);

   gtk_text_buffer_create_tag (buffer, "right_justify",
	 "justification", GTK_JUSTIFY_RIGHT, NULL);

   gtk_text_buffer_create_tag (buffer, "userinput",
	 "foreground", "DarkBlue", NULL);
   
   gtk_text_buffer_create_tag (buffer, "importantinfo",
	 "foreground", "DarkGreen", NULL);

   gtk_text_buffer_create_tag (buffer, "debuginfo",
	 "foreground", "DarkRed", NULL);

   gtk_text_buffer_create_tag (buffer, "secondary_color",
	 "foreground", "Brown", NULL);

   gtk_text_buffer_create_tag (buffer, "question",
	 "foreground", "DarkRed",
	 "weight", PANGO_WEIGHT_BOLD, NULL);

   gtk_text_buffer_create_tag (buffer, "normal",
	 "foreground", "black",
	 "weight", PANGO_WEIGHT_NORMAL,
	 NULL);

   /* initialize tag stack */
   strcpy(sdata->tags[0], "normal");
   sdata->tsize = 1;
   sdata->ftop  = 0;



}



/*
 *
 * This is the output control function for scare. This fucntion
 * is used to control the formating of the interpreter
 * output.
 *
 */
void os_print_tag (sc_uint32 tag, const sc_char *arg)
{
   sc_char  *sizearg;
   sc_int32 size;
   float fwait;
   int i;

   GtkTextBuffer *tbuf;
   GtkTextTagTable *ttable;

   
   gchar *copy;

   switch(tag) {

      case SC_TAG_CLS:
	 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(idata->textw));
	 gtk_text_buffer_set_text(tbuf, " ", -1);
	 break;

      case SC_TAG_WAIT:
	 sscanf (arg, "%f", &fwait);
	 size = (int)fwait;
	 sleep(size);
	 break;
	 

      case SC_TAG_FONT:

	 if(idata->ftop >= CMDBUF)
	    break;

	 copy = g_strdup(arg);
	 for(i=0; i< strlen(arg); i++) {
	    copy[i] = tolower(arg[i]);
	 }
	 
	 //printf("%s\n", copy);
   
	 sizearg = strstr (copy, "size=");
	 if (sizearg != NULL)
	 {
	    
	    tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(idata->textw));
	    if(idata->ftag != NULL)
	    {
	       ttable = gtk_text_buffer_get_tag_table(tbuf);
	       gtk_text_tag_table_remove(ttable, idata->ftag);
	       idata->ftag = NULL;
	    }

	    /* Deal with incremental and absolute sizes. */
	    if (!sc_strncasecmp (sizearg, "size=+", 6)
		  && sscanf (sizearg, "size=+%ld", &size) == 1)
	    {
	       if(idata->ftop > 0)
		  size += idata->fstack[idata->ftop-1];
	       else
		  size += DEFAULT_FONT_SIZE;
	       idata->fstack[idata->ftop++] = size;
	    }
	    else if (!sc_strncasecmp (sizearg, "size=-", 6)
		  && sscanf (sizearg, "size=-%ld", &size) == 1)
	    {
	       if(idata->ftop > 0)
		  size = idata->fstack[idata->ftop-1] - size;
	       else
		  size -= DEFAULT_FONT_SIZE;
	       idata->fstack[idata->ftop++] = size;
	    }
	    else if (sscanf (arg, "size=%ld", &size) == 1)
	    {
	       idata->fstack[idata->ftop++] = size;
	    }
	    else {
	       idata->ftop--;
	       break;
	    }

	    idata->ftag = gtk_text_buffer_create_tag(tbuf,
		  NULL,
		  "size", size*PANGO_SCALE, NULL);


	 }
	 g_free(copy);
	 break;

	 
      case SC_TAG_ENDFONT:
	 if(idata->ftop > 0)
	 {
	    tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(idata->textw));
	    if(idata->ftag != NULL)
	    {
	       ttable = gtk_text_buffer_get_tag_table(tbuf);
	       gtk_text_tag_table_remove(ttable, idata->ftag);
	       idata->ftag = NULL;
	    }

	    idata->ftop--;
	    if(idata->ftop <= 0)
	       break;

	    idata->ftag = gtk_text_buffer_create_tag(tbuf,
		  NULL, "size",
		  idata->fstack[idata->ftop-1]*PANGO_SCALE,
		  NULL);
	 }
	 break;

      case SC_TAG_SCOLOR:
	 if(idata->tsize < CMDBUF) {
	    strcpy(idata->tags[idata->tsize], "secondary_color");
	    idata->tsize++;
	 }
	 break;


      case SC_TAG_BOLD:
	 if(idata->tsize < CMDBUF) {
	    strcpy(idata->tags[idata->tsize], "bold");
	    idata->tsize++;
	 }
	 break;

      case SC_TAG_ITALICS:
	 if(idata->tsize < CMDBUF) {
	    strcpy(idata->tags[idata->tsize], "italic");
	    idata->tsize++;
	 }
	 break;

      case SC_TAG_UNDERLINE:
	 if(idata->tsize < CMDBUF) {
	    strcpy(idata->tags[idata->tsize], "underline");
	    idata->tsize++;
	 }
	 break;

      case SC_TAG_ENDBOLD:
      case SC_TAG_ENDITALICS:
      case SC_TAG_ENDUNDERLINE:
      case SC_TAG_ENDRIGHT:
      case SC_TAG_ENDCENTER:
      case SC_TAG_ENDSCOLOR:

	 if(idata->tsize > 1)
	    idata->tsize--;
	 break;

      case SC_TAG_CENTER:
	 if(idata->tsize < CMDBUF) {
	    strcpy(idata->tags[idata->tsize], "center");
	    idata->tsize++;
	 }
	 break;

      case SC_TAG_RIGHT:
	 if(idata->tsize < CMDBUF) {
	    strcpy(idata->tags[idata->tsize], "right_justify");
	    idata->tsize++;
	 }

	 break;

      case SC_TAG_WAITKEY:
	 idata->got_input = 0;
	 idata->command   = WAIT_FOR_KEY;
	 while(!idata->got_input) {
	    gtk_main_iteration_do(1);
	 }
	 break;
	
      default:
	 break;
   }

}

/*
 *
 * This is used to flush the output if we are buffering it. But we
 * are not buffering the output, so, this is an empty function
 *
 */
void os_flush (void)
{
}






/*
 * This function gets a line of input from the user. It waits
 * until the user has entered something & pressed the Enter key.
 * It does more than just accept user input, though.
 *
 * It also updates the status bar, it updates the verbose status
 * of the pulldown menu
 *
 */
sc_bool os_read_line (sc_char *buffer, sc_uint16 length)
{

   int cid;
   char text[2048];
   char *cptr;
   GtkCheckMenuItem *mit;

   /* Update statusbar */
   cid = gtk_statusbar_get_context_id(GTK_STATUSBAR(idata->sbar),
	 "simple_status");

   if(idata->got_yesno)
      gtk_statusbar_pop(GTK_STATUSBAR(idata->sbar),
	    idata->got_yesno);
   idata->got_yesno = cid;

   if((cptr = sc_get_game_room(idata->sgame)) != NULL)
      sprintf(text, "%s ", cptr);
   
   if((cptr = sc_get_game_status_line(idata->sgame)) != NULL)
      strcat(text, cptr);
   else {
      strcat(text, " |            Score: ");
      cptr = (text + strlen(text));
      sprintf(cptr, "%ld", sc_get_game_score(idata->sgame));
      strcat(text, "/");
      cptr = (text + strlen(text));
      sprintf(cptr, "%ld", sc_get_game_max_score(idata->sgame));
      strcat(text, " Turns: ");
      cptr = (text + strlen(text));
      sprintf(cptr, "%lu", sc_get_game_turns(idata->sgame));
   }

   gtk_statusbar_push(GTK_STATUSBAR(idata->sbar),
	 cid,
	 text);

   /* Now update verbose status in the menu */
   mit = (GtkCheckMenuItem *)idata->mverb;
   gtk_check_menu_item_set_active(mit,
	 sc_get_game_verbose (idata->sgame));

   /* Now wait for input */
   idata->got_input = 0;
   idata->command  = WAIT_FOR_INPUT;
   while(!idata->got_input) {
      gtk_main_iteration_do(1);
   }

   strncpy(buffer, idata->userin, length-1);
   if(strlen(idata->userin) >= length)
      buffer[length] = '\0';

   return TRUE;

}



/*
 *
 * This is the debug version of os_read_line()
 * currently is just calls os_read_line() ;)
 *
 */
sc_bool os_read_line_debug (sc_char *buffer, sc_uint16 length)
{
   return os_read_line(buffer, length);

}


/*
 * 
 * This function poses a query to the user and waits for
 * the answer.
 *
 */
void os_prompt_string (sc_char *question,
      sc_char *buffer,
      sc_int32 length)
{
   GtkTextBuffer *tbuf;
   GtkTextIter giter;
   
   tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(idata->textw));
   gtk_text_buffer_get_end_iter(tbuf, &giter);

   gtk_text_buffer_insert_with_tags_by_name(tbuf,
	 &giter, question, -1,
	 "question", NULL);
   gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(idata->textw),
	 gtk_text_buffer_get_insert(tbuf),
	 0.2, 1, 0, 0.5);

   idata->got_input = 0;
   idata->command  = WAIT_FOR_INPUT;
   while(!idata->got_input) {
      gtk_main_iteration_do(1);
   }

   strncpy(buffer, idata->userin, length-1);
   if((int)strlen(idata->userin) >= length)
      buffer[length] = '\0';

}



/*
 *
 * Ask user for confirmation for whatever action they may have activated
 * The parameter 'type' will decide what action it was.
 *
 */
sc_bool os_confirm (sc_uint32 type)
{

   GtkWidget     *confi;
   GtkTextBuffer *tbuf;
   GtkTextIter   giter;
   GtkTextView   *tview;
   
   idata->yesno = DNORESPONSE;
   switch (type)
   {
      case SC_CONF_QUIT:
	  confi = create_confirmation_dlg(idata,
		"Really quit the game?");
	 break;

      case SC_CONF_RESTART:
	  confi = create_confirmation_dlg(idata,
		"Really restart the game?");
	 break;

      case SC_CONF_SAVE:
	 return TRUE;
	 break;

      case SC_CONF_RESTORE:
	  confi = create_confirmation_dlg(idata,
		"Really load a savegame?");
	 break;
      
      case SC_CONF_VIEW_HINTS:
	  confi = create_confirmation_dlg(idata,
		"Really show hints (This could reduce your score)?");
	 break;

      default:
	  confi = create_confirmation_dlg(idata,
		"Really do this?");
	 break;
   }

   gtk_widget_show(confi);
   while(idata->yesno == DNORESPONSE)
   {
      gtk_main_iteration_do(1);
   }

   if(idata->yesno == DYES) {

      tview = GTK_TEXT_VIEW(idata->textw);
      /* provide a little feedback */
      switch(type) {
	 case SC_CONF_QUIT:
	    tbuf = gtk_text_view_get_buffer(tview);
	    gtk_text_buffer_get_end_iter(tbuf, &giter);

	    gtk_text_buffer_insert_with_tags_by_name(tbuf, &giter,
		  "\nGAME ABORTED (YOU COULD NOW LOAD A NEW GAME)\n\n", -1,
		  "center", "bold", "importantinfo", NULL);

	    gtk_text_view_scroll_to_mark(tview,
		  gtk_text_buffer_get_insert(tbuf),
		  0.2, 1, 0, 0.5);
	    break;

	 case SC_CONF_RESTART:
	    tbuf = gtk_text_view_get_buffer(tview);
	    gtk_text_buffer_get_end_iter(tbuf, &giter);
	    
	    gtk_text_buffer_insert_with_tags_by_name(tbuf, &giter,
		  "\nRESTARTING GAME..\n\n", -1,
		  "center", "bold", "importantinfo", NULL);

	    gtk_text_view_scroll_to_mark(tview,
		  gtk_text_buffer_get_insert(tbuf),
		  0.2, 1, 0, 0.5);
	    break;
	 default:
	    break;
      }

      return TRUE;
   }
   return FALSE;

}



/*
 *
 * Opens a file & returns the pointer to the file, cast to
 * a void.
 *
 */
void *os_open_file (sc_bool is_save)
{
 
   GtkWidget *fsel;

   idata->yesno   = DNORESPONSE;
   idata->menu    = 0;
   idata->command = SAVE_GAME;
   
   if(is_save)
      fsel = create_fileselection (idata, 1);
   else
      fsel = create_fileselection (idata, 0);
      
   gtk_file_selection_set_filename(GTK_FILE_SELECTION(fsel),
	 idata->name);

   gtk_widget_show(fsel);
   while(idata->yesno == DNORESPONSE)
   {
      gtk_main_iteration_do(1);
   }

   if(idata->yesno == DYES) {
      if(is_save)
	 idata->savefile = fopen(idata->name, "wb");
      else
	 idata->savefile = fopen(idata->name, "rb");
      return idata->savefile;
   }

   return NULL;

}



/*
 *
 * Writes some data to a file
 *
 *
 */
void os_write_file (void *opaque,
      sc_byte *buffer,
      sc_uint16 length)
{
   FILE	*stream = (FILE *)opaque;

   fwrite (buffer, 1, length, stream);
   if (ferror (stream))
      fprintf (stderr, "Write error: %s\n", strerror (errno));
}



/*
 *
 * Reads some data from a file & stores it into a buffer
 * passed to it
 *
 */
sc_uint16 os_read_file(void *opaque,
      sc_byte *buffer,
      sc_uint16 length)
{
   FILE	*stream = (FILE *)opaque;
   sc_uint16 bytes;

   bytes = fread (buffer, 1, length, stream);
   if (ferror (stream))
      fprintf (stderr, "Read error: %s\n", strerror (errno));

   return bytes;
}



/*
 *
 * Closes a file.
 *
 */
void os_close_file (void *opaque)
{
   FILE	*stream = (FILE *)opaque;
   fclose (stream);
}

/*
 *
 * Just stubs for now
 *
 */
void os_play_sound (sc_char *filepath,
      sc_int32 offset,
      sc_int32 length,
      sc_bool is_looping)
{


}

/*
 *
 * Just stubs for now
 *
 */
void os_stop_sound (void)
{
}



/*
 *
 * This function loads a graphic file & displays it. The graphics
 * could be in a external file or within the TAF file.
 *
 */
void os_show_graphic(sc_char *filepath,
      sc_int32 offset,
      sc_int32 length)
{

   GtkTextBuffer *tbuf;
   GdkPixbuf *gp;
   GtkTextIter giter;

   char *cptr1;
   char *cptr2;
   char fullp[1024];

   guint8 *data;
   FILE *fptr;

   /* Attempt to load the image into a pixbuf */
   if(offset == 0 && length == 0)
   {
      cptr1 = strrchr(filepath, '\\');
      if(NULL == cptr1)
	 cptr1 = filepath;
      else
	 cptr1++;
      strcpy(fullp, idata->gname);
      cptr2 = strrchr(fullp, '/');
      if(NULL == cptr2)
	 strcpy(fullp, cptr1);
      else {
	 cptr2++;
	 strcpy(cptr2, cptr1);
      }
      

      gp = gdk_pixbuf_new_from_file(fullp, NULL);
      if(NULL == gp)
	 return;
   }
   else {

      fptr = fopen(idata->gname, "rb");
      if(NULL == fptr)
	 return;

      data = (guint8 *)malloc(length);
      if(NULL == data) {
	 fclose(fptr);
	 return;
      }
      fseek(fptr, offset, SEEK_SET);
      fread(data, 1, length, fptr);
      fclose(fptr);

      fptr = fopen("/tmp/gtkscare_temp_file.jpg", "wb");
      if(NULL == fptr) {
	 free(data);
	 return;
      }
      fwrite(data, 1, length, fptr);
      fclose(fptr);

      gp = gdk_pixbuf_new_from_file("/tmp/gtkscare_temp_file.jpg", NULL);
      free(data);
      unlink("/tmp/gtkscare_temp_file.jpg");

      if(NULL == gp) {
	 return;
      }

   }

   /* Could it be true? We have the image? */
   tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(idata->textw));
   gtk_text_buffer_get_end_iter(tbuf, &giter);

   gtk_text_buffer_insert_with_tags_by_name(tbuf, &giter,
	 "\n ", -1, "center", NULL);
   gtk_text_buffer_insert_pixbuf(tbuf, &giter, gp);
   gtk_text_buffer_insert(tbuf, &giter, "\n\n", -1);

   gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(idata->textw),
	 gtk_text_buffer_get_insert(tbuf),
	 0.2, 1, 0, 0.5);

   g_object_unref(G_OBJECT(gp));

}




/*
 *
 * This function displays hints on request.
 *
 */
void os_display_hints (sc_game game)
{
   sc_game_hint *shints;

   GtkWidget *gw;
   GtkListStore *store;
   GtkTreeIter   treeiter;

   int hnum;
   int i;

   shints = idata->shint;
   for(i=0; i< CMDBUF; i++)
      shints[i] = NULL;

   shints[0] = sc_iterate_game_hints(game, shints[0]);
   if(NULL == shints[0]) {
      free(shints);
      return;
   }

   i = 1;
   do {
      shints[i] = sc_iterate_game_hints(game, shints[i-1]);
      i++;
   }
   while(i < CMDBUF && shints[i] != NULL);
   

   hnum  = i;
   gw    = create_hint_window(idata);
   store = (GtkListStore *)lookup_widget(gw, "store");

   for(i=0; i<hnum; i++)
   {
      gtk_list_store_append (store, &treeiter);
      gtk_list_store_set (store, &treeiter, 0, i+1, 1,
	    sc_get_game_hint_question(game, shints[i]), -1);
   }

   gtk_widget_show(gw);
   


}

//==///////////////////////////////////////////////////////////////////
//
///
///
/// \param
///	
/// \return
///	
//==///////////////////////////////////////////////////////////////////


/*  End of file gtkscare.c  */

