/* 
   Copyright 1994-2003 Free Software Foundation, Inc.

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

   This library 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 
   USA

   You may contact the author at:

   mailto::camille@bluegrass.net

   or by snail mail at:

   David Lindauer
   850 Washburn Ave Apt 99
   Louisville, KY 40222
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "_rtti.h"

extern "C" void __raise_throw() ;
extern "C" void __call_terminate() ;
// we won't call this next
extern "C" long ___xceptionhandle(void *p, void *record, void *context, void *param) ;

static bool uncaught = false ;

bool _RTL_FUNC uncaught_exception()
{
   return uncaught ;
}

void _RTL_FUNC __undecorate(char *buf, char *name) 
{
   int count = *(unsigned char *)name++ ;
   if (*name == '@')
      name++ ;
   while(count ) {
      while (count && *name != '@')
         *buf++ = *name++,count-- ;
      if (count && *name == '@') {
         *buf++ = ';' ;
         *buf++ = ';' ;
         name++,count-- ;
      }
   }
   buf[0] = 0 ;
}
static void _complexdest(int addr, CLASSXCEPT *xt,int count)
{
   if (xt->flags & XD_CM_SYM) 
      for (int i=0; i <count; i++) {
         if (xt->destructor)
            (*xt->destructor)(addr) ;
         else {
            CLASSXCEPTREF *xr = (CLASSXCEPTREF *)((unsigned char *)(xt + 1) + xt->name[0]) ;
            while (xr->flags) {
               if (xr->flags & XD_CM_SYM) {
                  _complexdest(addr + xr->offset,xr->xt,1) ;
               }
               xr = (CLASSXCEPTREF *)((unsigned char *)xr + (xr->flags & XD_X_MASK)) ;
               xr ++ ;
            }
         }
         addr += xt->size ;
      }
}
static void _unwinddestructor(XCEPTDATA *d, int xeip)
{
   FUNCXCEPTHDR *fh = d->xceptBlock ;
   FUNCXCEPT *f = (FUNCXCEPT *)(fh + 1) ;
   while (f->flags) {
      if (f->flags & XD_CM_SYM) {
           if ((xeip > f->start + d->xceptBaseEIP) && (xeip <= f->end + d->xceptBaseEIP)) {
             if (!(d->flags & XD_CL_TRYBLOCK) || f->start >= d->thisxt->start && f->end < d->thisxt->end) {

                 int count = 1 ;
                 if (f->flags & XD_ARRAY)
                    count = *(int *)(f+1) ;
                 _complexdest(d->ebp+f->ebpoffs,f->xt, count) ;
             }
           }
      }

      f = (FUNCXCEPT *)((unsigned char *)f + (f->flags & XD_X_MASK))  ;
      f ++ ;
   }
}
static int findSubClassX(CLASSXCEPTREF *xr, CLASSXCEPT *xt)
{
    while (xr->flags) {
       if (xr->flags & (XD_CL_PRIMARY | XD_CL_BASE)) {
	   		int offset ;
			CLASSXCEPTREF *xr1 ;
	   		if (xr->xt == xt)
				return xr->offset ;
			if ((xt->flags & XD_REF) &&  (void *)xr->xt == (void *)xt->destructor)
				return xr->offset ;
			xr1 = (CLASSXCEPTREF *)((unsigned char *)(xr->xt + 1) + xr->xt->name[0]);
			offset = findSubClassX(xr1,xt) ;
			if (offset != -1)
				return offset + xr->offset;
       }
       xr = (CLASSXCEPTREF *)((unsigned char *)xr + (xr->flags & XD_X_MASK)) ;
       xr ++ ;
    }
	return -1 ;
}
static int findSubClass(FUNCXCEPT *f, CLASSXCEPT *xt)
{
	CLASSXCEPTREF *xr ;
	if (!f->xt)
		return 0 ;
	if (f->xt == xt)
		return 0 ;
	if ((f->xt->flags & XD_REF) && f->xt->destructor == xt)
		return 0 ;
    xr = (CLASSXCEPTREF *)((unsigned char *)(xt + 1) + xt->name[0]) ;
	return findSubClassX(xr,f->xt) ;
}
void _RTL_FUNC _ThrowException(void *xt, void *cc, void *instance, void *data1, 
							   unsigned arrsize)
{
   char buf[256] ;
   bool first = true ;
   XCEPTDATA *data = (XCEPTDATA *)data1;
   char *xx ;
   int xebp ;
   int xeip ;

   if (data) {
      xeip = data->eip ;
      xebp = data->ebp ;
   } else {
      asm mov eax,[ebp]
      asm mov [xebp],eax
      asm mov eax,[ebp+4]
      asm mov [xeip],eax
   }
   while (1) {
      XCEPTDATA *d ;
      asm mov eax,fs:[0]
      asm mov [d],eax
      while (d->sig != XC_SIG && d->_xceptfunc == ___xceptionhandle) {
	  	void *q = *(void **)d;
         asm mov eax,[d]
         asm mov eax,[eax]
         asm mov fs:[0],eax
         asm mov [d],eax 
      }
      if (d->sig != XC_SIG)
         break ;         
   	  if (!data && (d->flags & XD_CL_TRYBLOCK))
	  {
	  	  uncaught = true;
	      __call_terminate() ;
	  }
      while (xebp && xebp != d->ebp) {
//	  	int *q = *(int **)xebp;
//	  	xeip = *(q+1);
//		xebp = *q;
         asm mov eax,[xebp]
         asm mov ecx,[eax]
         asm mov [xebp],ecx
         asm mov ecx,[eax+4]
         asm mov [xeip],ecx
      }
      if (xebp) {
         FUNCXCEPTHDR *fh = d->xceptBlock ;
         FUNCXCEPT *f ;
         if (d == data) {
            f = data->thisxt ;
            f = (FUNCXCEPT *)((unsigned char *)f + (f->flags & XD_X_MASK)) ; 
            f ++ ;
         } else {
            f = (FUNCXCEPT *)(fh+1) ;
         }
//         if (first) {
            void **a = fh->xtrecord ;
            if (a) {
               while (*a) {
                  if (*a == xt) {
                     break ;
                  }
                  a++ ;
               }
               if (!*a) {
                  xt = 0 ;
                  f = (FUNCXCEPT *)0 ;
               }
            }
//         }
         first = false ;
         while (f && f->flags) {
            if (f->flags & XD_CL_TRYBLOCK) {
               if ((xeip >= f->start + d->xceptBaseEIP) && (xeip < f->end + d->xceptBaseEIP)) {
				  int offset ;
	              if ((offset = findSubClass(f,(CLASSXCEPT *)xt)) != -1) {
                     int xx ;
                     d->flags = XD_CL_TRYBLOCK ;
				   	 d->copycons = cc;
                     if (f->xt && instance) {
					 	d->instance = (void *)(d->ebp + f->ebpoffs) ;
                        d->thrownxt = (CLASSXCEPT *)xt ;
					 	if (f->xt->flags & XD_REF) {
							int size ;
							void * p ;
							int of = d->flags;
							d->flags |= XD_CL_BYREF ;
							p = malloc(size = ((CLASSXCEPT *)xt)->size) ;
							if (p) {
								if (d->copycons)
								{
									typedef void (*CCF)(void *ths, void *old);
									(*(CCF)d->copycons)(p, instance);
								}
								else
									std::memcpy(p,instance,size) ;
								if (d->thrownxt && d->thrownxt->destructor)
									_complexdest((int)instance, d->thrownxt, 1);
								if (of & XD_CL_BYREF)
									free(instance);
								*(void **)(d->instance) = p ;
								d->instance = p ;
							} else {
								// out of memory
								__raise_throw() ;
								__call_terminate() ;
							}
						} else {
							int of = d->flags;
							d->flags &= ~XD_CL_BYREF;
							if (d->copycons)
							{
								typedef void (*CCF)(void *ths, void *old);
								(*(CCF)d->copycons)(d->instance, instance);
							}
							else
		                        std::memcpy(d->instance,instance,f->xt->size) ;
							if (d->thrownxt && d->thrownxt->destructor)
								_complexdest((int)instance, d->thrownxt, 1);
							if (of & XD_CL_BYREF)
								free(instance);
						}
                     } else
                        d->instance = 0 ;
                     d->eip = xeip ;
                     d->thisxt = f ;
                     xx = *(int *)(f+1) ;
                     asm mov eax,[xx]
                     asm mov ebp,[xebp]
                     asm jmp eax ;
                  }
               }
            }
            f = (FUNCXCEPT *)((unsigned char *)f + (f->flags & XD_X_MASK)) ; 
            f ++ ;
         }
         d->flags &= ~XD_CL_TRYBLOCK ;
         _unwinddestructor(d, xeip) ;
         d = d->next ;
		 _EAX = d;
         asm mov fs:[0],eax
      } else {
         uncaught = true ;
         printf("internal error") ;
		 __raise_throw() ;
         __call_terminate() ;
      }
   }
   uncaught = true ;
   __call_terminate() ;
}
// legacy with old compiler
void _RTL_FUNC _ThrowException(void *xt, void *instance, void *data1)
{
	_ThrowException(xt, 0, instance, data1, 1);
}
void _RTL_FUNC _RethrowException()
{
   XCEPTDATA *d ;
   asm mov eax,fs:[0]
   asm mov [d],eax
   if (d->sig == XC_SIG && (d->flags & XD_CL_TRYBLOCK)) {
      _ThrowException(d->thrownxt,d->copycons,d->instance,d, 1) ;
   } else {
   	  __raise_throw() ;
      __call_terminate() ;
   }
}
void _RTL_FUNC _CatchCleanup()
{
   XCEPTDATA *d ;
   asm mov eax,fs:[0]
   asm mov [d],eax
   if (d->sig == XC_SIG && (d->flags & XD_CL_TRYBLOCK)) {
      if (d->instance && d->thrownxt && d->thrownxt->destructor)
         _complexdest((int)d->instance,d->thrownxt,1) ;
   	  if (d->flags & XD_CL_BYREF)
	  	free(d->instance) ;
      _unwinddestructor(d,d->eip) ;
	  d->flags = 0 ;
   } else
      __call_terminate() ;
}