/* -*- Mode: C; c-file-style: "gnu" -*-
   cmd_line.c -- the actual implementation of the debugger commands.
   Created: Chris Toshok <toshok@hungry.com>, 30-Nov-1997
 */
/*
  This file is part of Japhar, the GNU Virtual Machine for Java Bytecodes.
  Japhar is a project of The Hungry Programmers, GNU, and OryxSoft.

  Copyright (C) 1997, 1998 The Hungry Programmers

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program 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 General Public License for more details.
  
  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#include "config.h"
#include "interp.h"
#include "stack.h"
#include "break.h"
#include "cmds.h"
#include "jvmdi.h"
#include "ClazzFile.h"
#include "gc.h"

#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <setjmp.h>

extern ClazzFile *class_to_debug;
extern JNIEnv *debugger_env;
extern JavaVM *debugger_vm;
extern sigjmp_buf resume_jmp;

extern jframeID break_frame;
extern int current_bpnum;

extern jboolean debugger_running;

/* A structure which contains information on the commands this program
   can understand. */
COMMAND commands[] = {
  { "where", cmd_where, "Show a stack trace." },
  { "up", cmd_up, "Move up on the stack." },
  { "down", cmd_down, "Move down on the stack." },
  { "list", cmd_list, "List current source." },
  { "info", cmd_info, "Show info on current program state." },
  { "print", cmd_print, "Print out a java value." },
  { "help", cmd_help, "Display this text." },
  { "run", cmd_run, "Begin execution of the program." },
  { "step", cmd_step, "Step to the next instruction." },
  { "next", cmd_next, "Step to the next instruction at the same stack level." },
  { "cont", cmd_cont, "Continue execution of the program." },
  { "break", cmd_break, "Set a breakpoint." },
  { "enable", cmd_enablebp, "Enable a breakpoint." },
  { "disable", cmd_disablebp, "Disable a breakpoint." },
  { "delete", cmd_deletebp, "Delete a breakpoint.", },
  { "quit", cmd_quit, "Quit japhard." },
  { "start", cmd_start, "Start thread." },
  { "stop", cmd_stop, "Stop thread." },
  { "switch", cmd_switch, "Switch to another thread." },
  { "gc", cmd_gc, "Run the Garbage Collector." },
  { "finalize", cmd_finalize, "Run the finalizers for those objects awaiting finalization." },
  { (char*)NULL, (Function*)NULL, (char*)NULL }
};

int 
cmd_list()
{
  return 0;
}

int 
cmd_info(char *arg)
{
  return 0;
}

int 
cmd_print(char *arg)
{
  /* if we're in a native method we can't look at any variables. */
  
  /* first check if it's a local variable in this method. */
  
  /* if not, check if it's a field of this object. */
  
  return 0;
}

int 
cmd_help(char *arg)
{
  return 0;
}

int 
cmd_run(char *arg)
{
  jobjectArray params;
  jclass string_cls;
  int num_args;
  char *arg_line;
  static jvalue arg_value[1];
  jmethodID mainMethodID;

  /* first get the number of arguments. */
  num_args = 0;
  
  arg_line = strdup(arg);

  if (strtok(arg_line, " \t"))
    {
      num_args = 1;

      while (strtok(NULL, " \t"))
	num_args++;
    }

  free(arg_line);

  string_cls = (*debugger_env)->FindClass(debugger_env, "java/lang/String");
  if ((*debugger_env)->ExceptionOccurred(debugger_env))
    return -1;

  mainMethodID = (*debugger_env)->GetStaticMethodID(debugger_env, class_to_debug,
						    "main", "([Ljava/lang/String;)V");

  if (!mainMethodID)
    return -1;

  params = (*debugger_env)->NewObjectArray(debugger_env, num_args, string_cls, NULL);
  params = (*debugger_env)->NewGlobalRef(debugger_env, params);

  if (num_args)
    {
      int i = 0;
      char *new_arg;
      jstring str;

      arg_line = strdup(arg);
      new_arg = strtok(arg_line, " \t");
      str = (*debugger_env)->NewStringUTF(debugger_env, new_arg);
      str = (*debugger_env)->NewGlobalRef(debugger_env, str);

      (*debugger_env)->SetObjectArrayElement(debugger_env, params, i, str);
      i ++;

      while ( i < num_args )
	{
	  new_arg = strtok(NULL, " \t");
	  str = (*debugger_env)->NewStringUTF(debugger_env, new_arg);
	  str = (*debugger_env)->NewGlobalRef(debugger_env, str);
		  
	  (*debugger_env)->SetObjectArrayElement(debugger_env, params, i, str);

	  i++;
	}

      free(arg_line);
    }

  arg_value[0].l = params;

  debugger_running = JNI_TRUE;

  (*debugger_env)->CallStaticVoidMethodA(debugger_env, class_to_debug, mainMethodID, arg_value);

  return 0;
}

int 
cmd_step()
{
  return 0;
}

int 
cmd_next()
{
  return 0;
}

int 
cmd_cont()
{
  jint threads_count;
  jthread *threads;
  
  /* resume all the interpreter threads */
  if (JVMDI_GetAllThreads(debugger_env,
			  &threads_count,
			  &threads) == JVMDI_ERROR_NONE)
    {
      int i;
      for (i = 0; i < threads_count; i ++)
	JVMDI_ResumeThread(debugger_env, threads[i]);
      
      free(threads);

      BREAK_disableBreakpoint(current_bpnum);

      ((StackFrame*)break_frame)->pc --;

      siglongjmp(resume_jmp, 1);
    }

  return 0;
}

int 
cmd_break(char *arg)
{
  if (strchr(arg, '.')) /* it's of the ClassName.methodName variety */
    {
      if (strrchr(arg, '(') && strrchr(arg, '.')) /* it's of the ClassName.methodName(pc)
						     variety */
	{
	  int pc;
	  char *dot = strrchr(arg, '.');
	  char *lparen = strchr(dot, '(');
	  char *rparen = strchr(lparen, ')');
	  char *classname;
	  char *methodname;

	  *dot = '\0';
	  *lparen = '\0';
	  *rparen = '\0';

	  pc = atoi(lparen + 1);

	  classname = arg;
	  methodname = dot + 1;

	  BREAK_addPCBreakpoint3(classname,
				 methodname,
				 pc);

	  *dot = '.';
	  *lparen = '(';
	  *rparen = ')';
	}
      else
	{
	  char *dot = strrchr(arg, '.');
	  char *classname;
	  char *methodname;

	  *dot = '\0';

	  classname = arg;
	  methodname = dot + 1;

	  BREAK_addMethodBreakpoint(classname, methodname);

	  *dot = '.';
	}
    }
  else if (strchr(arg, ':')) /* it's of the .javaFile:lineNumber variety. */
    {
      assert(0); /* we don't do these yet. */
    }
  else /* it's of the methodName variety, so we look for it in this class. */
    {
      ClazzFile *cf = jclass_to_clazzfile(debugger_env, class_to_debug);

      BREAK_addMethodBreakpoint(getClassName(debugger_env, cf),
				arg);
    }

  return 0;
}

int 
cmd_enablebp(char *arg)
{
  int bp_num = atoi(arg);

  BREAK_enableBreakpoint(bp_num);

  return 0;
}

int 
cmd_disablebp(char *arg)
{
  int bp_num = atoi(arg);

  BREAK_disableBreakpoint(bp_num);

  return 0;
}

int 
cmd_deletebp(char *arg)
{
  int bp_num = atoi(arg);

  BREAK_removeBreakpoint(bp_num);

  return 0;
}

int 
cmd_start(char *name)
{
  return 0;
}

int 
cmd_stop(char *name)
{
  return 0;
}

int 
cmd_switch(char *name)
{
  return 0;
}

int 
cmd_quit()
{
  exit(0);
}

int 
cmd_where(char *arg)
{
  if (!debugger_running)
    {
      printf ("Debug session not yet started.  Type 'run' to start.\n");
    }
  else
    {
      print_backtrace();
    }
#if 0
      JThreadInfo *thread_info = THREAD_getJavaInfo();
      StackFrame *f;
      int i = 0;

      f = TOPFRAME(thread_info);

      printf ("Stack for thread '%s':\n", NAME(thread_info));
      printf ("---\n");
      while (f < thread_info->stack_highwater)
	{
	  if (f->flags & FRAME_NATIVE)
	    printf ("#%d - <native method %s.%s", i,
		    CLASSNAME(f), METHODNAME(f));
	  else
	    printf ("#%d in %s.%s at pc %d\n", i,
		    CLASSNAME(f), METHODNAME(f), PC(f));

	  f = get_frame_parent(f);
	  i ++;
	}
#endif
      
   return 0;
}

int
cmd_up(char *arg)
{
  int num_levels;
  
  if (!arg || !*arg)
    {
      num_levels = 1;
    }
  else
    {
      num_levels = atoi(arg);

      up_frame(num_levels);
    }

  /* right now we just punt and go up one level. */
#if 0
  current_frame = get_frame_parent(current_frame);
  
  if (current_frame->flags & FRAME_NATIVE)
    {
      printf ("- <native method %s.%s",
	      CLASSNAME(current_frame), METHODNAME(current_frame));
    }
  else
    {
      printf ("- in %s.%s at pc %d\n",
	      CLASSNAME(current_frame), METHODNAME(current_frame), PC(current_frame));
    }
#endif

  return 0;
}

int
cmd_down(char *arg)
{
  int num_levels;

  if (!arg || !*arg)
    {
      num_levels = 1;
    }
  else
    {
      num_levels = atoi(arg);

      down_frame(num_levels);
    }

#if 0
  /* right now we just punt and go down one level. */
  current_frame = get_frame_parent(current_frame);
#endif

  return 0;
}

int
cmd_gc()
{
  JGC_runCollector(debugger_vm);

  return 0;
}

int
cmd_finalize()
{
  JGC_runFinalizers(debugger_vm);

  return 0;
}
