/*->c.flex */


#define BOOL int
#define TRUE 1
#define FALSE 0

#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>

#include "h.os"
#include "h.werr"
#include "h.flex"
#include "h.wimp"
#include "h.wimpt"
#include "h.err"

#include "h.trace"
#include "h.main"



/* There are two alternative implementations in this file. */


typedef struct
{
  flex_ptr anchor;      /* *anchor should point back to here. */
  int size;             /* in bytes. Exact size of logical area. */
                        /* then the actual store follows. */
} flex__rec;



static void flex__fail(int i)
{
  werr(TRUE, "fatal store error fl-2-%i.", i); 
#if TRACE
  i = *(int *)-4 ;     /* go bang! */
#endif
}



static int roundup(int i) {
  return 0xfffffffc & (i + 3);
}

static char *flex__freep;       /* free flex memory */
static char *flex__lim;         /* limit of flex memory */

/* From base upwards, it's divided into store blocks of
  a flex__rec
  the space
  align up to next word.
*/



void flex__wimpslot(char **top) {
  /* read/write the top of available memory. *top == 0 -> just read. */
  int dud = -1;
  int slot = ((int) *top);
  if (slot != -1) slot -= 0x8000;
  tracef1("flex__wimpslot in: %i.\n", slot);
  wimpt_noerr(wimp_slotsize(&slot, &dud, &dud));
  *top = (char*) slot + 0x8000;
  tracef1("flex__wimpslot out: %i.\n", slot);
}

BOOL flex__more(int n)
{
  /* Tries to get at least n more bytes, raising flex__lim and
  returning TRUE if it can. */
  char *prev = flex__lim;

  flex__lim += n;
  flex__wimpslot(&flex__lim);
  tracef4("flex__more, freep=%i prevlim=%i n=%i lim=%i.\n",
    (int) flex__freep, (int) prev, n, (int) flex__lim);

  if (flex__lim < prev + n)
  {
   tracef0("flex__more FAILS.\n");
   flex__lim = prev;             /* restore starting state:
                                    extra memory is useless */
   flex__wimpslot(&flex__lim);
   return FALSE ;
  }
  else return TRUE ;
}




#ifdef NEVER

void flex__give(void) {
  /* Gives away memory, lowering flex__lim, if possible. */
#if TRACE
  int prev = (int) flex__lim;
#endif

  flex__lim = flex__freep;
  flex__wimpslot(&flex__lim);
  tracef3("flex__give, prev=%i freep=%i lim=%i.\n",
    prev, (int) flex__freep, (int) flex__lim);
}

#endif



void flex_demon(void)
{
 flex__lim = flex__freep;
 flex__wimpslot(&flex__lim);
 remzeroevent(MEMORYDEMON);
}


void flex__give(void)
{
 addzeroevent(MEMORYDEMON);
}





BOOL flex__ensure(int n) {
  n -= flex__lim - flex__freep;
  tracef3("flex__ensure %i: %x %x.\n", n, (int) flex__lim, (int) flex__freep);
  if (n <= 0 || flex__more(n)) return TRUE; else return FALSE;
}

BOOL flex_alloc(flex_ptr anchor, int n)
{
  flex__rec *p;

  tracef2("flex_alloc %x %i.\n", (int) anchor, n);

  if (n < 0 || ! flex__ensure(sizeof(flex__rec) + roundup(n))) {
    *anchor = 0;
    return FALSE;
  };

  p = (flex__rec*) flex__freep;
  flex__freep += sizeof(flex__rec) + roundup(n);

  p->anchor = anchor;
  p->size = n;
  *anchor = p + 1; /* sizeof(flex__rec), that is */
  return TRUE;
}




os_error * flex_alloce(flex_ptr anchor, int n)
{
 if(flex_alloc(anchor,n)) return(NULL);
 else                     return(&err_memory);
}




#if TRACE

static char *flex__start ;

/* show all flex pointers for debugging purposes */
void flex_display(char * name)
{
 flex__rec *p = (flex__rec *) flex__start ;
 FILE * fp;


 fp=fopen(name,"wb");


 fprintf(fp,"*****flex display: %x %x %x\n",
          (int) flex__start, (int) flex__freep, (int) flex__lim) ;

 dprintf(0,"*****flex display: %x %x %x\n",
          (int) flex__start, (int) flex__freep, (int) flex__lim) ;

 while (1)
 {
  if ((int) p >= (int) flex__freep) break;

  fprintf(fp,"flex block @ %x->%x->%x",
        (int)p, (int)(p->anchor), (int)(*(p->anchor))) ;

  dprintf(0,"flex block @ %x->%x->%x", 
        (int)p, (int)(p->anchor), (int)(*(p->anchor))) ;

  if (*(p->anchor) != p + 1)
  {
   fprintf(fp,"<<< bad block!");
   dprintf(0,"<<< bad block!");
  }

  fprintf(fp,"\n") ;
  p = (flex__rec*) (((char*) (p + 1)) + roundup(p->size));

  fflush(fp);
 }

 fclose(fp);
}

#endif




void flex__reanchor(flex__rec *p, int by) {
  /* Move all the anchors from p upwards. This is in anticipation
  of that block of the heap being shifted. */

  while (1) {
    if ((int) p >= (int) flex__freep) break;
   tracef1("flex__reanchor %x\n",(int) p) ;
    if (*(p->anchor) != p + 1) flex__fail(6);
    *(p->anchor) = ((char*) (p + 1)) + by;
    p = (flex__rec*) (((char*) (p + 1)) + roundup(p->size));
  };
}



void flex_free(flex_ptr anchor)
{
  flex__rec *p = ((flex__rec*) *anchor) - 1;
  int roundsize = roundup(p->size);
  flex__rec *next = (flex__rec*) (((char*) (p + 1)) + roundsize);

  tracef1("flex_free %i.\n", (int) anchor);

  if (p->anchor != anchor) {
    flex__fail(0);
  };

  flex__reanchor(next, - (sizeof(flex__rec) + roundsize));

  memmove(
     p,
     next,
     flex__freep - (char*) next);

  flex__freep -= sizeof(flex__rec) + roundsize;

  flex__give();

  *anchor = 0;
}



int flex_size(flex_ptr anchor)
{
  flex__rec *p = ((flex__rec*) *anchor) - 1;
  if (p->anchor != anchor) {
    flex__fail(4);
  }
  return(p->size);
}







int flex_extend(flex_ptr anchor, int newsize)
{
  flex__rec *p = ((flex__rec*) *anchor) - 1;
  return(flex_midextend(anchor, p->size, newsize - p->size));
}


os_error * flex_extende(flex_ptr anchor, int newsize)
{
 if(flex_extend(anchor,newsize)) return(NULL);
 else                            return(&err_memory);
}



BOOL flex_midextend(flex_ptr anchor, int at, int by)
{
  flex__rec *p;
  flex__rec *next;

  tracef3("flex_midextend %i at=%i by=%i.\n", (int) anchor, at, by);

  p = ((flex__rec*) *anchor) - 1;
  if (p->anchor != anchor) {
    flex__fail(1);
  }
  if (at > p->size) {
    flex__fail(2);
  }
  if (by < 0 && (-by) > at) {
    flex__fail(3);
  }
  if (by == 0) {
    /* do nothing */
  } else if (by > 0) { /* extend */

    int growth = roundup(p->size + by) - roundup(p->size);
    /* Amount by which the block will actually grow. */

    if (! flex__ensure(growth)) {
      return FALSE;
    };

    next = (flex__rec*) (((char*) (p + 1)) + roundup(p->size));
    /* The move has to happen in two parts because the moving
    of objects above is word-aligned, while the extension within
    the object may not be. */

    flex__reanchor(next, growth);

    memmove(
      ((char*) next) + roundup(growth),
      next,
      flex__freep - (char*) next);

    flex__freep += growth;

    memmove(
      ((char*) (p + 1)) + at + by,
      ((char*) (p + 1)) + at,
      p->size - at);
    p->size += by;

  } else { /* The block shrinks. */
    int shrinkage;

    next = (flex__rec*) (((char*) (p + 1)) + roundup(p->size));

    by = -by; /* a positive value now */
    shrinkage = roundup(p->size) - roundup(p->size - by);
      /* a positive value */

    memmove(
      ((char*) (p + 1)) + at - by,
      ((char*) (p + 1)) + at,
      p->size - at);
    p->size -= by;

    flex__reanchor(next, - shrinkage);

    memmove(
      ((char*) next) - shrinkage,
      next,
      flex__freep - (char*) next);

    flex__freep -= shrinkage;

    flex__give();

  };
  return TRUE;
}



os_error * flex_midextende(flex_ptr anchor, int at, int by)
{
 if(flex_midextend(anchor,at,by)) return(NULL);
 else                             return(&err_memory);
}



int flex_storefree(void)
{
 /* totally imaginary, controlled/displayed by OS. */
 return(0);
}





void flex_init(void)
{
  flex__lim = (char*) -1;
  flex__wimpslot(&flex__lim);


#if TRACE
  flex__start =
#endif


  flex__freep = flex__lim;
  tracef1("flex__lim = %i.\n", (int) flex__lim);

  /* Check that we're in the Wimp environment. */
  {
    void *a;
    if (! flex_alloc(&a, 1)) {
      werr(TRUE, "Not enough memory, or not within *desktop world.");
    };
    flex_free(&a);
  };
}



/* called when we propose to change amount of stuff in flex block */

int flex_chunk(flex_ptr anchor,int size,int chunksize)
{
 flex__rec *p = ((flex__rec*) *anchor)-1;

 if((size>=p->size) || (p->size>(size+chunksize)))
   return(flex_extend(anchor,(size/chunksize+1)*chunksize));

 return(TRUE);
}



os_error * flex_chunke(flex_ptr anchor,int size,int chunksize)
{
 if(flex_chunk(anchor,size,chunksize)) return(NULL);
 else                                  return(&err_memory);
}



void flex_debug(flex_ptr anchor,int line)
{
 flex__rec *p = ((flex__rec*) *anchor)-1;

 dprintf(line,"anchor=%x p=%x p->anchor=%x",anchor,p,p->anchor);
}


