/// \file vo_fat.c FAT16/FAT32 Filesystem driver for VsOS
/// \author Panu-Kristian Poiksalo and Pasi Ojala, VLSI Solution Oy



#include <vo_stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#include "vsos.h"
#include "vo_fat.h"
//#include "forbid_stdout.h"
#include "strings.h"
#include <sysmemory.h>
#include <crc32.h>

#if 1
#define OPTIMIZE_SPEED
#endif

#define MIN(a,b) (((a)<(b))?(a):(b))

#define LINES 16
#define CHARS_PER_LINE 64
#define TOT_CHARS (LINES*CHARS_PER_LINE)

s_int32 VoFatCountFreeKiB(register VO_FILE *f, register u_int32 findMiB,
			  register u_int16 verbose) {
  //  FatFileInfo *fi = (FatFileInfo*)f->fileInfo;
  FatDeviceInfo *di = (FatDeviceInfo*)f->dev->deviceInfo;
  s_int32 res = 0;
  // totalClusters includes non-existent clusters 0 and 1
  u_int32 end = di->totalClusters;
  u_int32 maxFindClusters = (findMiB == 0xFFFFFFFFU) ?
    0xFFFFFFFFU : findMiB*(2048/di->fatSectorsPerCluster);
  u_int32 reportInterval;
  u_int32 nextReport = reportInterval+2;
  u_int16 charsOnThisLine = 0;
  u_int32 freeForThisReport = 0;
  u_int32 nextCluster;
  u_int32 k = 2, stop = end;
  s_int16 step = 1;

  reportInterval = (end+(TOT_CHARS-1))/TOT_CHARS;
  //  printf("ReportInterval %ld\n", reportInterval);
  if (!reportInterval) {
    reportInterval = 1;
  } else {
    /* Round reportInterval up to next power of two. */
    reportInterval = 1L<<(LongLog2(reportInterval-1));
  }
  nextReport = reportInterval;
  //  printf("ReportInterval %ld\n", reportInterval);
  if (verbose) {
    printf("Cluster  %ld KiB per symbol: "
	   "# all used, + partially used, . all free\n"
	   "0x0000000 ",
	   (reportInterval<<1)*di->fatSectorsPerCluster);
    if (reportInterval == 1) {
      printf("  "); charsOnThisLine = 2; nextReport = 3; freeForThisReport = 0;
    } else if (reportInterval == 2) {
      printf(" ");  charsOnThisLine = 1; nextReport = 4; freeForThisReport = 0;
    }
  }

  if (!verbose && findMiB < 0xFFFFFFFFU) {
    /* If we are not super-verbose and we are looking for only a given amount
       of free space, let's run scanning backwards. While this will make
       things like SD Multiple Block Read fail and make SD operations slower,
       it still often result in significantly faster operation, particularly
       when a large memory card is relatively full. The reason is that memory
       cards are typically filled from start to end, so if we want to look
       for a limited amount of empty space, looking from the end may lead
       to a significantly faster seek time. */
    k = end-1;
    stop = 2-1;
    step = -1;
  }
#if 0
  printf("## Start %ld, stop %ld, step %d\n", k, stop, step);
  printf("nextRep %lx\n", nextReport);
#endif

  while (k!=stop) {
    if (verbose && k>=nextReport) {
      int outCh = '#';
      if (freeForThisReport == reportInterval) outCh = '.';
      else if (freeForThisReport) outCh = '+';
      printf("%c", outCh);
      //      printf("\n##1 nextRep %lx %lx\n", nextReport, nextReport+reportInterval);
      if (++charsOnThisLine >= CHARS_PER_LINE) {
	printf("\n0x%07lx ", k);
	charsOnThisLine = 0;
      }
      nextReport += reportInterval;
      freeForThisReport = 0;
    }
    if (!(nextCluster = VoFatReadClusterRecord(f, k))) {
      res++;
      freeForThisReport++;
    } else if (nextCluster == 0xFFFFFFFFU) {
      /* Note: 0x0FFFFFFF is end of file, 0xFFFFFFFF is read error! */
      res = -1;
      goto finally;
    }
#ifdef OPTIMIZE_SPEED
    if (di->fatBits == 32) {
      if (step > 0 && !(k & 127) && end-k >= 128) {
	/* Look forwards */
	u_int32 *sB = (u_int32 *)(&f->sectorBuffer[2]);
	int i;
	if (!verbose || k+128 < nextReport) {
	  for (i=1; i<128; i++) {
	    if (!(*sB++)) {
	      res++;
	      freeForThisReport++;
	    }
	  }
	  k += 127;
	} else {
	  for (i=1; i<128; i++) {
	    k++;
	    if (verbose && k>=nextReport) {
	      int outCh = '#';
	      if (freeForThisReport == reportInterval) outCh = '.';
	      else if (freeForThisReport) outCh = '+';
	      printf("%c", outCh);
	      //	      printf("\n##2 nextRep %lx, k %lx, i %x, k+i %lx, charsOnLine %d\n", nextReport, k, i, k+i, charsOnThisLine+1);
	      if (++charsOnThisLine >= CHARS_PER_LINE) {

		printf("\n0x%06lx ", k);
		charsOnThisLine = 0;
	      }
	      nextReport += reportInterval;
	      freeForThisReport = 0;
	    }

	    if (!(*sB++)) {
	      res++;
	      freeForThisReport++;
	    }
	  }
	}
      } else if (step < 0 && !((k+1) & 127) && k >= 128) {
	/* Look backwards.
	   Note: when going backwards, we can't be verbose. */
	u_int32 *sB = (u_int32 *)(&f->sectorBuffer[0]);
	int i;
	for (i=0; i<127; i++) {
	  if (!(*sB++)) {
	    res++;
	    freeForThisReport++;
	  }
	}
	k -= 127;
      }
    }
#endif
    if (res >= maxFindClusters) {
      res = maxFindClusters;
      break;
    }
    k += step;
  } /* while (k != stop) */
 finally:
  if (charsOnThisLine) {
    printf("\n");
  }

  if (res < 0) return -1;

  if (di->fatSectorsPerCluster == 1) {
    return res / 2;
  }
  return res * (di->fatSectorsPerCluster / 2);
}
