#include <exec/types.h>
#include <exec/libraries.h>
#include <exec/execbase.h>
#include <inline/exec.h>
#include <libraries/dosextens.h>

#include <sys/syscall.h>

/* get the current revision number. Version control is automatically done by
 * OpenLibrary(), I just have to check the revision number 
 */
#include "../library/version.h"

struct Library *ixemulbase;

static int start_stdio();

/*
 * Have to take care.. I may not use any library functions in this file,
 * since they are exactly then used, when the library itself couldn't be
 * opened...
 */
static char *build_warn (char *t1, int num1, char *t2, int num2);

extern int main();
extern int expand_cmd_line;
extern char *default_wb_window;
extern int errno;
extern char **environ;
extern char *_ctype_;
extern int sys_nerr;
extern struct Library *SysBase, *DOSBase;
extern struct __sFILE **__sF;
static int ENTRY();
static int exec_entry();

asm("
	.text

	jmp	pc@(_ENTRY-.+2)	| by default jump to normal AmigaDOS startup

	| this is a struct exec, for now only OMAGIC is supported
	.globl	exec		| on purpose not called _exec
exec:
	.word	0		| a_mid
	.word	0407		| a_magic = OMAGIC
	| until gld knows about this, the following fields are not filled out.
	| the relocation fields will be used to provide self-relocating programs
	| that are then residentable
	.long	0		| a_text
	.long	0		| a_data
	.long	0		| a_bss
	.long	0		| a_syms
	.long	_exec_entry	| a_entry
	.long	0		| a_trsize
	.long	0		| a_drsize

	| word alignement is guaranteed
");

static int
exec_entry (struct Library *ixembase, int argc, char *argv[], char *env[])
{
  ixemulbase = ixembase;
  environ = env;
  return ix_exec_entry (argc, argv, env, &errno, start_stdio);
}

static int
ENTRY()
{
  register unsigned char *rega0  asm("a0");
  register unsigned long  regd0  asm("d0");

  UBYTE *aline = rega0;
  ULONG alen = regd0;

  int res = 20;

  ixemulbase = OpenLibrary ("ixemul.library", IX_VERSION);
  if (ixemulbase)
    {
      /* just warn, in case the user tries to run program which might require
       * more functions than are currently available under this revision. */
      if (ixemulbase->lib_Version == IX_VERSION &&
	  ixemulbase->lib_Revision < IX_REVISION)
	{
	  /* I wish I had stdio in the library... */
	  int omask = sigsetmask (~0);
	  __request_msg (build_warn ("ixemul.library warning: current revision ",
	  	         	     ixemulbase->lib_Revision,
	  	         	     ", needed revision ",
	  	         	     IX_REVISION), "Continue");
	  sigsetmask (omask);
	}

      res = ix_startup (aline, alen, 
		        expand_cmd_line, default_wb_window, start_stdio, &errno);

      CloseLibrary (ixemulbase);
    }
  else
    {
      struct Process *me = (struct Process *)((*(struct ExecBase **)4)->ThisTask);

      __request_msg (build_warn ("Need at least version ", 
				 IX_VERSION, 
				 " of ixemul.library.", 0), "Abort");
      
      /* quickly deal with the WB startup message, as the library couldn't do
       * this for us. Nothing at all is done that isn't necessary to just shutup
       * workbench..*/
      if (! me->pr_CLI)
        {
	  Forbid (); 
	  ReplyMsg ((WaitPort (& me->pr_MsgPort), GetMsg (& me->pr_MsgPort)));
	}
      
      res = 20;
    }

  return res;
}

int
start_stdio(int a1, int a2, int a3)
{
  int res;

  /* more to follow ;-) */
  ix_get_vars2 (5, &_ctype_, &sys_nerr, &SysBase, &DOSBase, &__sF);

/*  _init_stdio(); */

  res = main (a1, a2, a3);
  return res;
}

/* this thing is best done with sprintf else, but it has to work without the
 * library as well ;-(
 */
static char *
itoa (int num)
{
  short snum = num;

  /* how large can a long get...?? */
  static char buf[16];
  char *cp;
  
  buf[15] = 0;
  for (cp = &buf[15]; snum; snum /= 10) 
    *--cp = (snum % 10) + '0';

  return cp;
}

static char *
pstrcpy (char *start, char *arg)
{
  while (*start++=*arg++) ;
  return start-1;
}

static char *
build_warn (char *t1, int num1, char *t2, int num2)
{
  static char buf[255];
  char *cp;
  
  cp = pstrcpy (buf, t1);
  cp = pstrcpy (cp, itoa (num1));
  cp = pstrcpy (cp, t2);
  if (num2)
    pstrcpy (cp, itoa (num2));

  return buf;
}
