/*
 *  Copyright (c) 1992, 1993 John E. Davis  (davis@amy.tch.harvard.edu)
 *  All Rights Reserved.
 */
#include <stdio.h>
#include <string.h>
#include <setjmp.h>

#include "config.h"
#include "buffer.h"
#include "screen.h"
#include "window.h"
#include "display.h"
#include "sysdep.h"
#include "file.h"
#include "keymap.h"
#include "ledit.h"
#include "misc.h"
#include "ins.h"
#include "slang.h"
#include "paste.h"
#include "mem.h"

char Error_Buffer[256];
char Message_Buffer[256];
volatile int KeyBoard_Quit;
int Exit_From_MiniBuffer;


typedef struct
{
   jmp_buf b;
} jmp_buf_struct;

extern jmp_buf_struct Jump_Buffer, *Jump_Buffer_Ptr;

#define MACRO_SIZE 256
char Macro_Buffer[MACRO_SIZE];
char *Macro_Buffer_Ptr = Macro_Buffer;
char *Macro_Ptr_Max = Macro_Buffer;
int Defining_Keyboard_Macro = 0;
int Executing_Keyboard_Macro = 0;

struct
{
   int def;
   int exe;
} Macro_State;

MiniInfo_Type Mini_Info;

void macro_store_key(char ch)
{
   /* I need to put something here to increase the size of the Macro_Buffer
      in case of overflow */

   if (Macro_Buffer_Ptr - Macro_Buffer >= MACRO_SIZE) msg_error("Macro Size exceeded.");
   else *Macro_Buffer_Ptr++ = ch;
}

void restore_macro_state(void)
{
   Defining_Keyboard_Macro = Macro_State.def;
   Executing_Keyboard_Macro = Macro_State.exe;
}

void set_macro_state(void)
{
   Macro_State.def = Defining_Keyboard_Macro;
   Macro_State.exe = Executing_Keyboard_Macro;
}

/* if in the mini buffer and if during keyboard macro, allow user to enter
   different text each time macro is executed */

int macro_query()
{
   unsigned char s[256];

   /* macro state is already set */
   Defining_Keyboard_Macro = 0;
   Executing_Keyboard_Macro = 0;

   if (!IS_MINIBUFFER)
     {
	*s = 0;
	read_from_minibuffer("Enter String:", (char *) NULL, (char *) s);
	ins_chars(s, strlen((char *) s));
     }

   /* exit from mini restores the state */
   return(1);
}

int jed_getkey()
{
   int ch, diff;
   static int mini_flag = 0;
   
   if (Read_This_Character != NULL)
     {
	ch = *Read_This_Character++;
	if (ch == 0) Read_This_Character = NULL;
	else if (ch == '@')
	  {
	     if ((ch = *Read_This_Character++) == '@') ch = 0;
	  }
	return(ch);
     }
   
   if (!Executing_Keyboard_Macro)
     {
	diff = (int) (Key_Bufferp - Key_Buffer);
	if (Key_TimeOut_Flag && 
	    ((mini_flag && (diff > 0))
	     || !input_pending(&Number_Ten)))
	  {
	     message(Key_Buffer);
	     strcat(Message_Buffer, "-");
	     update((Line *)NULL, 0, 0);
	     message(" ");	       /* ensures that previous message will 
					  get erased. */
	     mini_flag = 1;
	  }
	else (mini_flag = 0);
	
	ch = my_getkey();
	if (diff < 13) 
	  {
	     *Key_Bufferp++ = (char) ch;
	     *Key_Bufferp = 0;
	  }
	else msg_error("KeyBuffer overflow!");

	if (Defining_Keyboard_Macro) 
	  {
	     /* starting a new keysequence to save this point */
	     if (diff == 0)
	       {
		  Macro_Ptr_Max = Macro_Buffer_Ptr;
	       }
	     
	     macro_store_key(ch);
	  }
     }
   else
     {
	if (Macro_Buffer_Ptr < Macro_Ptr_Max) ch = *Macro_Buffer_Ptr++;
	else ch = Abort_Char;
     }
   return(ch);
}

int msg_error(char *msg)
{
   flush_input();
   Window->trashed = 1;
   Defining_Keyboard_Macro = Executing_Keyboard_Macro = 0;
   Read_This_Character = NULL;
   set_macro_state();
   if (Batch) fprintf(stderr, "%s\n", msg);

   if (!SLang_Error) SLang_Error = 1;
   if (Error_Buffer[0] != 0) return(0);
   strcpy(Error_Buffer, msg);
   return(1);
}

/* later I will do more with this-- for now, keep last one */
void message(char *msg)
{
   if (Executing_Keyboard_Macro) return;
   if (Batch) fprintf(stderr, "%s\n", msg);
   /* if (*Message_Buffer) do_dialog(Message_Buffer); */
   if (msg == NULL) *Message_Buffer = 0; else strncpy(Message_Buffer, msg, 255);
   Message_Buffer[255] = 0;
}

/* read from minibuffer using prompt providing a default, and stuffing
   the minibuffer with init if necessary */
/* I should make recursive mini buffers */
int read_from_minibuffer(char *prompt, char *deflt, char *what)
{
   char buf[256];
   jmp_buf_struct *mini_jmp_save, mini_jmp_buf;
   unsigned char *ps;
   unsigned char *p, ch;
   
   static Window_Type *current_window;
   /* may get changed if user leaves minibuffer and returns from other
      window which means that the new window becomes target of minibuffer action */
   Window_Type *w;
   int ret, len, save_point;

   if (!IS_MINIBUFFER) current_window = Window;
   if (select_minibuffer()) return(0);   /* we should be on a new line of mini buffer */

   ps = Mini_Info.prompt;
   p = (unsigned char *) prompt;
   len = 0;
   
   if (p != NULL) 
     {
	while (0 != (ch = *p++))
	  {
	     *ps++ = ch;
	     len++;
	  }
	*ps++ =  ' ';
	len++;
     }

   if ((deflt != NULL) && (*deflt != 0))
     {
	sprintf(buf,"(default: %s) ", deflt);
	p = (unsigned char *) buf;
	
	while (0 != (ch = *p++))
	  {
	     *ps++ = ch;
	     len++;
	  }
     }
   *ps = 0;

   /* Use calculate column to find effective length */
   
   Mini_Info.prompt_len = len;
   p = CLine->data; save_point = Point;
   CLine->data = Mini_Info.prompt;  Point = len;
   Mini_Info.effective_prompt_len = 0;
   Mini_Info.effective_prompt_len = calculate_column() - 1;
   CLine->data = p;  Point = save_point;

   touch_window();
   ins_chars((unsigned char *) what, strlen(what));
   update((Line *) NULL, 0, 0);
   
   mini_jmp_save = Jump_Buffer_Ptr;
   Jump_Buffer_Ptr = &mini_jmp_buf;
   
   if (setjmp(mini_jmp_buf.b) != 0)
     {
	SLang_restart(1);   /* just in case */
	update((Line *) NULL, 0, 0);
     }

   while(!Exit_From_MiniBuffer)
     {
	do_jed();
	/* if (setjmp(Jump_Buffer) != 0)
	  {
	     Exit_From_MiniBuffer = 1;
	  }  */

	if (KeyBoard_Quit && IS_MINIBUFFER) break;
	update((Line *) NULL, 0, 0);
     }
   
   if (Exit_From_MiniBuffer && !Executing_Keyboard_Macro 
       && (Repeat_Factor == NULL) && !Window->trashed && !input_pending(&Number_Zero))
     {
	goto_rc(Screen_Height, 1);
     }

   Jump_Buffer_Ptr = mini_jmp_save;

   Exit_From_MiniBuffer = 0;
   MiniBuffer_Active = 0;
   restore_macro_state();
   *what = 0;
   ret = 1;
   if (KeyBoard_Quit) ret = 0;
   else if (CLine->len == 0)
     {
	if (deflt != NULL) strcpy(what, deflt);
     }
   /* problem if user manages to insert a neline in minibuffer */
   else
     {
	if (CLine->len > 255) CLine->len = 255;
	MEMCPY(what, (char *) CLine->data, CLine->len);
	what[CLine->len] = 0;
     }
   
   /* we should be in minibuffer so delete marks and spots */
   while (CBuf->spots) pop_spot();
   while (CBuf->marks) pop_mark(&Number_Zero);
   w = Window;
   while (w->next != Window) w = w->next;
   other_window();
   w->next = Window;
   /*  FREE(mini_w); do not free this, see The_MiniWindow */
   while(Window != current_window) other_window();
   Window->trashed = 1;

   /* delete_buffer(MiniBuffer); */
   MiniBuffer = NULL;
   return(ret);
}

void exit_error(char *str)
{
    reset_display();
    reset_tty(); 
    fprintf(stderr,"\007\rFatal Error: %s\n", str);
    if (CBuf != NULL) auto_save_all();
    exit(1);
}

int begin_keyboard_macro()
{
   Macro_Buffer_Ptr = Macro_Buffer;
   message("Defining Macro.");
   Defining_Keyboard_Macro = 1;
   set_macro_state();
   return(1);
}

int end_keyboard_macro()
{

    if (Defining_Keyboard_Macro) message("Macro Defined.");
    else
      {
	  if (!Executing_Keyboard_Macro) msg_error("Not defining Macro!");
	  Executing_Keyboard_Macro = 0;
	  return(1);
      }

   /* Macro_Ptr_Max = Macro_Buffer_Ptr; */
   Defining_Keyboard_Macro = 0;
   set_macro_state();
   return(1);
}

int execute_keyboard_macro()
{
    int repeat = 0, *repeat_ptr;
    char ch;

    if (Defining_Keyboard_Macro)
      {
	  msg_error("Can't execute a macro while defining one.");
	  return(0);
      }

   Executing_Keyboard_Macro = 1;
   set_macro_state();
   Macro_Buffer_Ptr = Macro_Buffer;

   /* save the repeat context */
   repeat_ptr = Repeat_Factor;
   if (repeat_ptr != NULL) repeat = *repeat_ptr;

   Window->trashed = 1;
   while (Macro_Buffer_Ptr < Macro_Ptr_Max)
     {
	Repeat_Factor = NULL;
	ch = *Macro_Buffer_Ptr++;
	do_key(ch);
	if (KeyBoard_Quit || (*Error_Buffer)) break;
     }

   /* restore context */
   Repeat_Factor = repeat_ptr;
   if (repeat_ptr != NULL) *repeat_ptr = repeat;
   Executing_Keyboard_Macro = 0;
   set_macro_state();
   return(1);
}
/*
   before this can be of any real use, I need strings without the NULL 
   chars! */
/*      
int get_macro()
{
   if ((Defining_Keyboard_Macro) || (Executing_Keyboard_Macro))
     {
	msg_error("Macro Buffer locked!");
	return(0);
     }
   if (Macro_Ptr_Max == Macro_Buffer) return (0);
}
*/

void get_last_macro ()
{
   char *m, *s;
   char buf[2 * MACRO_SIZE + 1];
   
   if (Defining_Keyboard_Macro) 
     {
	msg_error("Complete Macro first!");
	return;
     }
   
   m = Macro_Buffer;
   if (m == Macro_Ptr_Max)
     {
	msg_error("Macro not defined.");
	return;
     }
   
   s = buf;
   while (m < Macro_Ptr_Max)
     {
	if (*m == 0)
	  {
	     *s++ = '@';
	     *s++ = '@';
	  }
	else *s++ = *m;
	m++;
     }
   *s = 0;
   SLang_push_string(buf);
}
