/* For free support for VSIDE, please visit www.vsdsp-forum.com */

#if 0
#define USE_MEM_TRACK
#endif

#include <vo_stdio.h>
#include <volink.h>
#include <apploader.h>
#include <string.h>
#include <stdlib.h>
#include <lists.h>
#include <sysmemory.h>
#include <ctype.h>
#include <kernel.h>
#include <getmemory.h>
#include <math.h>
#ifdef USE_MEM_TRACK
#include "memtrack.h"
#endif


#define DEFAULT_STEP 64

#define MAX(a,b) (((a)>(b))?(a):(b))

#if 0
extern struct LIST iFlist;
#else
void iFlist(void);
#endif
extern struct LIST xFlist;
extern struct LISTY __mem_y yFlist;
#ifdef USE_MEM_TRACK
struct MemTrackElement __mem_y *memTrackP = NULL;
#endif

#define MAX_FRAGS 256

struct DynMemElement {
  u_int16 addr;
  u_int16 size;
};

struct DynMemList {
  int n;
  struct DynMemElement dynMemElement[MAX_FRAGS];
} __mem_y dynMemList[3];


static char ixyStr[3] = {'I','X','Y'};

struct MEMORYNODE {
	struct MINNODE node;
	u_int16 size;
};

struct MEMORYNODEY {
	struct MINNODEY node;
	u_int16 size;
};

u_int16 IsAllocated(register u_int16 ixy, register u_int16 addr) {
  s_int16 sum = 0;
  s_int16 isFree = 0;
  switch (ixy) {
  case 0:
    {
      volatile void *m = HeadNodeI(iFlist);
      while (m) {
	u_int16 size = GetSizeNodeI(m);
	u_int16 iAddr = IMEMADDR(m);
	//	printf("I: %04x %4x\n", iAddr, size);
	if (addr >= iAddr && addr < iAddr+size) {
	  isFree = 1;
	}
	sum += size;
	m = NextNodeI(m);
      }
    }
    break;
  case 1:
    {
      struct MEMORYNODE *m = (void *)HeadNode(&xFlist);
      while (m) {
	u_int16 size = m->size;
	//	printf("X: %04x %4x\n", (u_int16)m, size);
	if (addr >= (u_int16)m && addr < (u_int16)m+size) {
	  isFree = 1;
	}
	sum += size;
	m = (void *)NextNode((void *)m);
      }
    }
    break;
  case 2:
    {
      struct MEMORYNODEY __mem_y *m = (void __mem_y *)HeadNodeY(&yFlist);
      while (m) {
	u_int16 size = m->size;
	//	printf("Y: %04x %4x\n", (u_int16)m, size);
	if (addr >= (u_int16)m && addr < (u_int16)m+size) {
	  isFree = 1;
	}
	sum += size;
	m = (void __mem_y *)NextNodeY((void __mem_y *)m);
      }
    }
    break;
  default:
    return -1;
    break;
  }
  return (1-isFree) * sum;
}



s_int16 IsAllocatedByLib(register u_int16 ixy, register u_int16 addr,
			 register u_int16 *lib) {
  u_int16 sections;
  int i;

  lib += *lib+6; /* lib += entries + 6 */
  sections = *lib++;

  for (i=0; i<sections; i++) {
    u_int16 secSize = (*lib >> 2) << 1;
    u_int16 secPage = *lib++&3;
    u_int16 secAddr = *lib++;

    /* See if this address was inside a section. */
    if (secPage == ixy && addr >= secAddr && addr < secAddr+secSize) {
      return 1;
    }

#ifdef USE_MEM_TRACK
    /* If code section, and memTrack is on, see if this matches with
       tracked memory. */
    if (memTrackP && secPage == 0 && (ixy == 1 || ixy == 2)) {
      int j, y = (ixy==2);
      struct MemTrackElement __mem_y *elem = memTrackP;
      for (j=0; j<MEM_TRACK_LIST_SIZE; j++) {
	if (y == (elem->sizeAndY>>15) && elem->addr &&
	    addr >= elem->addr && addr < elem->addr+(elem->sizeAndY&0x7FFF) &&
	    elem->owner >= secAddr && elem->owner < secAddr+secSize) {
	  return 1;
	}
	elem++;
      }
    }
#endif
  }
  return 0;
}



const char *GetLibName(register u_int16 *lib) {
  static char s[9];
  char *p;
  if (!lib) {
    s[0] = '\0';
  } else {
    char *libStr;
    lib += *lib + 6;
    libStr = (char *)(lib + *lib * 2 + 2);
#if 0
    printf("lib strlen=%d s=\"%s\"\n", strlen(libStr), libStr);
#endif
  strcpy(s, (char *)(lib + *lib * 2 + 2));
    if ((p = strchr(s, '.'))) {
      *p = '\0';
    }
  }
  return s;
}

#define MAX_LIB_NAME_LEN 8

char *libChars = NULL;//[MAX_LIB+1] = {0};
char *libNames;

struct LibData {
  char idChar;
  char name[MAX_LIB_NAME_LEN+1];
};
struct LibData *libData = NULL;






void RemoveFromDynList(register u_int16 ixy, register u_int16 addr,
		       register u_int16 size) {
  u_int16 dmeN = dynMemList[ixy].n, i;
  struct DynMemElement __mem_y *dme = &dynMemList[ixy].dynMemElement[0];

#if 0
  printf("\n#Rem(%d, %x, %x)... ", ixy, addr, size);
#endif
  for (i=0; i<dmeN; i++) {
    /* Is removable! */
    if (dme->addr <= addr && dme->addr+dme->size >= addr+size) {
      if (dme->addr == addr) {
	/* Remove from start: no new elements. */
	dme->addr += size;
	dme->size -= size;
#if 0
	printf("##1");
#endif
	break;
      } else if (dme->addr+dme->size == addr+size) {
	/* Remove from end: no new elements. */
	dme->size -= size;
#if 0
	printf("##2");
#endif
	break;
      } else {
	/* Remove from middle: split memory area */
	int x;
#if 0
	printf("##3: Split %x:%x to ", dme->addr, dme->size);
#endif
	memmoveYY(dme+1, dme, sizeof(*dme)*(dmeN-i));
	dme->size = addr-dme->addr;
	dme++;
	x = (addr+size)-dme->addr;
	dme->addr += x;
	dme->size -= x;
	dynMemList[ixy].n++;
#if 0
	printf("%x:%x + %x:%x## ",
	       dme[-1].addr, dme[-1].size, dme->addr, dme->size);
#endif
	break;
      }
    }
    dme++;
  }

#if 0
  if (i >= dmeN) {
    printf("### TROUBLE ###");
  }
  printf("\n");
#endif
}

/* Remove dynamically allocated list elements with size 0 */
void CleanDynList(void) {
  int ixy;
  for (ixy=0; ixy<3; ixy++) {
    int i = 0;
    while (i<dynMemList[ixy].n) {
      if (!dynMemList[ixy].dynMemElement[i].size) {
	/* Remove 0-sized element, do not update counter. */
	memmoveYY(&dynMemList[ixy].dynMemElement[i],
		  &dynMemList[ixy].dynMemElement[i+1],
		  sizeof(dynMemList[0].dynMemElement[0])*
		  (--dynMemList[ixy].n - i));
      } else {
	/* If not 0-sized, update counter. */
	i++;
      }
    }
  }
}



u_int16 GetNextFree(register int ixy, register u_int16 previous,
		    register u_int16 *size) {
  u_int16 retVal = 0;
  if (!ixy) {
    retVal = previous ?
      (u_int16)NextNodeI((void *)previous) :
      (u_int16)HeadNodeI(iFlist);
      *size = GetSizeNodeI((void *)retVal);        // garbage if !retVal
  } else if (ixy == 1) {
    retVal = previous ?
      (u_int16)NextNode((void *)previous) :
      (u_int16)HeadNode(&xFlist);
    *size  = ((struct MEMORYNODE *)retVal)->size;  // garbage if !retVal
  } else {
    retVal = previous ?
      (u_int16)NextNodeY((void __mem_y *)previous) :
      (u_int16)HeadNodeY(&yFlist);
    *size  = ((struct MEMORYNODEY __mem_y *)retVal)->size; // garbage if !retVal
  }
  return retVal;
}


struct IxyElement {
  u_int16 addr;
  u_int16 size;
  u_int16 tot;
};

void ListFree(void) {
#if 1
  struct IxyElement ixyTab[3], *ie;
  int ixy;
  
  printf("Free memory list:\n");
  ie = ixyTab;
  for (ixy=0; ixy<3; ixy++) {
    ie->addr = GetNextFree(ixy, 0, &ie->size);
    ie->tot = 0;
    ie++;
  }

  while (ixyTab[0].addr || ixyTab[1].addr || ixyTab[2].addr) {
    printf(" ");
    ie = ixyTab;
    for (ixy=0; ixy<3; ixy++) {
      if (ie->addr) {
	printf("   %c: %04x-%04x, size %4x",
	       ixyStr[ixy],
	       ie->addr, ie->addr+ie->size-1, ie->size);
	ie->tot += ie->size;
	RemoveFromDynList(ixy, ie->addr, ie->size);
	ie->addr = GetNextFree(ixy, ie->addr, &ie->size);
      } else if (ixy < 2) {
	printf("%26s", "");
      }
      ie++;
    }
    printf("\n");
  }
  printf("%17stotal%5x%16stotal%5x%16stotal%5x\n",
	 "", ixyTab[0].tot, "", ixyTab[1].tot, "", ixyTab[2].tot);
#else
  s_int16 tot[3] = {0};
  int ixy;
  u_int16 addr[3];
  u_int16 size[3];
  
  printf("Free memory list:\n");
  for (ixy=0; ixy<3; ixy++) {
    addr[ixy] = GetNextFree(ixy, 0, size+ixy);
  }
  
  while (addr[0] || addr[1] || addr[2]) {
    printf(" ");
    for (ixy=0; ixy<3; ixy++) {
      if (addr[ixy]) {
	printf("   %c: %04x-%04x, size %4x",
	       ixyStr[ixy], addr[ixy], addr[ixy]+size[ixy]-1, size[ixy]);
	tot[ixy] += size[ixy];
	RemoveFromDynList(ixy, addr[ixy], size[ixy]);
	addr[ixy] = GetNextFree(ixy, addr[ixy], size+ixy);
      } else if (ixy < 2) {
	printf("%26s", "");
      }
    }
    printf("\n");
  }
  printf("%17stotal%5x%16stotal%5x%16stotal%5x\n",
	 "", tot[0], "", tot[1], "", tot[2]);
#endif
}



void ListLibs(void) {
  int i;
  printf("Static allocations:\n");
  for (i=0; i<loadedLibs; i++) {
    u_int16 *lib, *p;
    libChars[i] = '.';
    printf("  %s:\n", GetLibName(loadedLib[i]));

    if ((lib = loadedLib[i])) {
      u_int16 sections;
      int j;
      int anythingToPrint = 0;
      int lineNo = 1;

      lib += *lib+6; /* lib += entries + 6 */
      sections = *lib++;

#if 0
      p = lib;
      for (j=0; j<sections; j++) {
	u_int16 secSize = (*p >> 2) << 1;
	u_int16 secPage = *p++&3;
	u_int16 secAddr = *p++;
	
	printf("    %c: %04x-%04x, size %4x\n",
	       ixyStr[secPage], secAddr, secAddr+secSize-1, secSize);
      }
#endif

      do {
	int n[3] = {0, 0, 0};
	u_int16 *ixyLib[3] = {NULL, NULL, NULL};

	anythingToPrint = 0;
	p = lib;
	for (j=0; j<sections; j++) {
	  u_int16 secPage = *p&3;
	  if (++n[secPage] == lineNo) {
	    ixyLib[secPage] = p;
	    anythingToPrint = 1;
	  }
	  p += 2;
	}
	lineNo++;
	if (anythingToPrint) {
	  printf(" ");
	  for (j=0; j<3; j++) {
	    if (ixyLib[j]) {
	      u_int16 secSize, secPage, secAddr;
	      p = ixyLib[j];
	      secSize = (*p >> 2) << 1;
	      secPage = *p++&3;
	      secAddr = *p++;
	      RemoveFromDynList(j, secAddr, secSize);
	      printf("   %c: %04x-%04x, size %4x",
		     ixyStr[j], secAddr, secAddr+secSize-1, secSize);
	    } else {
	      printf("%26s", "");
	    }
	  }
	  printf("\n");
	}
      } while (anythingToPrint);
    }
  }
}



void ListDynamic(void) {
  int n;
  int i;
  printf("Dynamic allocations:\n");
  CleanDynList();
  n = MAX(MAX(dynMemList[0].n, dynMemList[1].n), dynMemList[2].n);
  for (i=0; i<n; i++) {
    int j;
    printf(" ");
    for (j=0; j<3; j++) {
      struct DynMemElement __mem_y *dme = &dynMemList[j].dynMemElement[i];
      if (i < dynMemList[j].n) {
	printf("   %c: %04x-%04x, size %4x",
	       ixyStr[j], dme->addr, dme->addr+dme->size-1, dme->size);
      } else {
	printf("%26s", "");
      }
    }
    printf("\n");
  }
}



void ListMemMap(register u_int16 step, register s_int16 compress) {
  u_int16 i, ixy;
  printf("Memory map with libs: ");
  for (i=0; i<loadedLibs; i++) {
    if (loadedLib[i]) {
      char *p, c;
      if ((p = strchr(&libNames[i*(MAX_LIB_NAME_LEN+1)], tolower(c=libChars[i])))) {
	*p = c;
	printf("%s%s", i?" ":"", &libNames[i*(MAX_LIB_NAME_LEN+1)]);
      } else {
	printf("%s%c=%s", i?" ":"", libChars[i], &libNames[i*(MAX_LIB_NAME_LEN+1)]);
      }
    }
  }
  printf("\n");

#if 0
  if (memTrackP) {
    int i;
    struct MemTrackElement __mem_y *elem = memTrackP;
    for (i=0; i<MEM_TRACK_LIST_SIZE; i++) {
      if (elem->addr) {
	printf("%02x: %04x %04x %04x\n",
	       i, elem->addr, elem->sizeAndY, elem->owner);
      }
      elem++;
    }
  }
#endif

#if 0
  printf("\n        ");
  for (i=0; i<8; i+=2) {
    printf("%-7x %-8x ", i*step*8, (i+1)*step*8);
  }
#endif

  for (ixy=0; ixy<3; ixy++) {
    u_int32 addr32, highAddr32;
    u_int16 addr, column = 0;
    u_int16 lowAddr;
    s_int16 skipped = 0;
    lowAddr = (u_int16)(memPools[ixy].addr & ((u_int16)(0x10000-64*step)));
    highAddr32 = (u_int32)memPools[ixy].addr+memPools[ixy].size;
#if 0
    printf("mp=0x%x, %d 0x%x 0x%x, lowAddr %04x, highAddr %04lx\n",
	   memPools, ixy, memPools[ixy].addr, memPools[ixy].size,
	   lowAddr, highAddr32);
#endif

#if 0
    if (ixy) {
      printf("        ");
      for (i=0; i<8; i+=2) {
	printf("%-8s%-9s", "|", "|");
      }
    }
    printf("\n");
#endif
    printf("%10s", "");
    for (i=0; i<8; i+=2) {
      printf("%-7x %-8x ", i*step*8, (i+1)*step*8);
    }
    printf("\n");

    for (addr32=lowAddr; addr32<highAddr32; addr32+=step) {
      int skip = 0;
      addr = (u_int16)addr32;
      if (!column && compress) {
	u_int32 end = (u_int32)addr+64*step;
	u_int16 addr2=0, size2;
	while ((addr2 = GetNextFree(ixy, addr2, &size2))) {
	  u_int32 end2 = (u_int32)addr2+size2;
	  if (addr2 <= addr && end2 >= end && end <= highAddr32) skip = 1;
	}
      }
      if (skip) {
	skipped++;
	addr32 += 63*step;
      } else {
	if (!column) {
	  if (skipped) {
#if 0
	    printf("    %04x-%04x\n", addr-skipped*64*step, addr-1);
	    skipped = 0;
#else
	    printf("%10s", "");
	    do {
	      printf(">");
	    } while (--skipped);
#endif
	    printf("\n");
	  }
	  printf("  %c %04x: ", ixyStr[ixy], addr);
	} else if (!(column & 15)) {
	  printf(" ");
	}
	if (addr < memPools[ixy].addr ||
	    addr >= memPools[ixy].addr + memPools[ixy].size) {
	  printf(" ");
	} else if (IsAllocated(ixy, addr)) {
	  char allocChar = '#';
	  /* Ok, we know this piece is allocated by someone. But by whom? */
	  for (i=0; i<loadedLibs; i++) {
	    if (loadedLib[i] && IsAllocatedByLib(ixy, addr, loadedLib[i])) {
	      allocChar = libChars[i];
	    }
	  }
	  printf("%c", allocChar);
	} else {
	  /* Memory is free */
	  printf(".");
	}
	if (++column >= 64) {
	  column = 0;
	  printf("\n");
	}
      }
    }
  }

  printf("\n");
}


ioresult main(char *parameters) {
  int nParam = RunProgram("ParamSpl", parameters);
  char *p = parameters;
  u_int16 i, currChar = '0';
  u_int16 step = DEFAULT_STEP;
  u_int16 res = S_ERROR;
  u_int16 verbose = 1;
  s_int16 listFree = 0, listLibs = 0, listMemMap = -1, listDynamic = 0;
  s_int16 listComprMemMap = 0;

#ifdef USE_MEM_TRACK
  memTrackP = GetSymbolAddress("memTrack");
#endif

  if (!(libChars = calloc(loadedLibs+1, sizeof(libChars[0]))) ||
      !(libNames = calloc(loadedLibs*(MAX_LIB_NAME_LEN+1), sizeof(libNames[0])))) {
    printf("E: Out of memory\n");
    goto finally;
  }

  for (i=0; i<nParam; i++) {
    if (!strcmp(p, "-v")) {
      verbose = 1;
    } else if (!strcmp(p, "+v")) {
      verbose = 0;
    } else if (!strcmp(p, "-f")) {
      listFree = 1;
    } else if (!strcmp(p, "+f")) {
      listFree = 0;
    } else if (!strcmp(p, "-l")) {
      listLibs = 1;
    } else if (!strcmp(p, "+l")) {
      listLibs = 0;
    } else if (!strcmp(p, "-d")) {
      listDynamic = 1;
    } else if (!strcmp(p, "+d")) {
      listDynamic = 0;
    } else if (!strcmp(p, "-m")) {
      listMemMap = 1;
    } else if (!strcmp(p, "+m")) {
      listMemMap = 0;
    } else if (!strcmp(p, "-c")) {
      listComprMemMap = 1;
    } else if (!strcmp(p, "+c")) {
      listComprMemMap = 0;
    } else if (!strcmp(p, "-a")) {
      listFree = listLibs = listDynamic = listComprMemMap = 1;
    } else if (!strcmp(p, "-b")) {
      if ((step*=2) > 1024) step = 1024;
    } else if (!strcmp(p, "+b")) {
      if ((step/=2) < 1) step = 1;
    } else if (!strncmp(p, "-b", 2)) {
      s_int32 t32 = strtol(p+2, NULL, 0);
      if (t32 < 1 || t32 > 1024) {
	printf("E: Bad parameter \"%s\"\n", p);
	goto finally;
      } else {
	step = (u_int16)LongCeilPow2(t32);
      }
    } else if (!strcmp(p, "-h")) {
      printf("Usage: Frags [-v|+v|-f|+l|-b|+b|-bx|-h]\n"
	     "-v|+v\tVerbose on|off\n"
	     "-m|+m\tShow memory map on|off\n"
	     "-c|+c\tShow compressed memory map on|off\n"
	     "-f|+f\tList free memory on|off\n"
	     "-l|+l\tLibList on|off\n"
	     "-d|+d\tDynamically allocated list on|off\n"
	     "-b|+b\tDouble|half number of bytes per symbol\n"
	     "-bx\tSet number of bytes per symbol to x (default: %d)\n"
	     "-a\tList all, equiv to \"-v -c -f -l -d\"\n"
	     "-h\tShow this help\n", DEFAULT_STEP, 2*DEFAULT_STEP);
      res = S_OK;
      goto finally;
    } else {
      printf("E: Bad parameter \"%s\"\n", p);
      goto finally;
    }
    p += strlen(p)+1;
  }

  /* If no print operation defined on/off, print Mem Map as default. */
  if (listMemMap < 0) {
    listMemMap = !(listLibs | listFree | listDynamic | listComprMemMap);
  }

  /* Setup dynamically allocated list */
  for (i=0; i<3; i++) {
    dynMemList[i].n = 1;
    dynMemList[i].dynMemElement[0].addr = memPools[i].addr;
    dynMemList[i].dynMemElement[0].size = memPools[i].size;
  }
  
  /* Clear library name character list. */
  for (i=0; i<loadedLibs; i++) {
    char *p = &libNames[i*(MAX_LIB_NAME_LEN+1)];
    libChars[i] = '.';
    strcpy(p, GetLibName(loadedLib[i]));
    while (*p) {
      *p = tolower(*p);
      p++;
    }
  }

  /* Assign characters to each library name.
     Try to be intelligent about it: first check each available name's
     first character and assign if free, then second character, etc. */
  for (i=0; i<MAX_LIB_NAME_LEN; i++) {
    int j;
    for (j=0; j<loadedLibs; j++) {
      char testChar = toupper(libNames[j*(MAX_LIB_NAME_LEN+1)+i]);
      if (libChars[j] == '.' && strlen(&libNames[j*(MAX_LIB_NAME_LEN+1)]) > i
	  && !strchr(libChars, testChar)) {
	libChars[j] = testChar;
      }
    }
  }

#if 0
  /* To test following piece of code. */
  libChars[1] = libChars[4] = '.';
#endif

  /* If there still are blank characters (highly unlikely), fill them
     with numbers. */
  for (i=0; i<loadedLibs; i++) {
    if (libChars[i] == '.') {
      while (strchr(libChars, currChar)) {
	currChar++;
      }
      libChars[i] = currChar;
    }
  }


  if (listMemMap) {
    ListMemMap(step, 0);
  }
  if (listComprMemMap) {
    ListMemMap(step, 1);
  }
  if (listFree) {
    ListFree();
  }
  if (listLibs) {
    ListLibs();
  }
  if (listDynamic) {
    ListDynamic();
  }

  if (verbose) {
    u_int16 lowestFree = memPools[0].addr+memPools[0].size-highestISoFar;
    printf("Free: I: %5u words (%u%%), X: %5u words (%u%%), "
	   "Y: %5u words (%u%%)\n",
	   IsAllocated(0, 0),
	   (u_int16)((u_int32)IsAllocated(0, 0)*100/memPools[0].size),
	   IsAllocated(1, 0),
	   (u_int16)((u_int32)IsAllocated(1, 0)*100/memPools[1].size),
	   IsAllocated(2, 0),
	   (u_int16)((u_int32)IsAllocated(2, 0)*100/memPools[2].size));
    printf("FLow: I: %5u words (%u%%), highestISoFar: 0x%04x\n",
	   lowestFree,
	   (u_int16)((u_int32)lowestFree*100/memPools[0].size),
	   highestISoFar);
  }

  res = S_OK;
 finally:
  if (libNames) {
    free(libNames);
    libNames = NULL;
  }
  if (libChars) {
    free(libChars);
    libNames = NULL;
  }
  return res;
}
