/* flaneth - flash and ethernet
   Copyright (C) 2007-2012 Stefan Schuermans <stefan@schuermans.info>
   Copyleft: GNU public license V2 - http://www.gnu.org/copyleft/gpl.html
   a BlinkenArea project - http://www.blinkenarea.org/ */

#include <string.h>

#include "app_bbm.h"
#include "app_cfg.h"
#include "apps.h"
#include "cf.h"
#include "debug.h"
#include "dosfs.h"
#include "tasks.h"

/**
 * @brief list directory and dump file
 * @param[in] sectorBuf scratch buffer to store a sector
 * @param[in] vi volume information structure
 */
static void AppsListDump(uint8_t sectorBuf[SECTOR_SIZE], VOLINFO *vi)
{
  DIRINFO di;
  DIRENT de;
  uint8_t filename[12];
  FILEINFO fi;
  uint8_t buf[64];
  uint32_t len, i;

  // list files in root directory
  di.scratch = sectorBuf;
  if (DFS_OpenDir(vi, (uint8_t *)"", &di) != DFS_OK) {
    debug_apps_printf("cannot open root directory");
    return;
  }
  debug_apps_printf("files in root directory:");
  while (DFS_GetNext(vi, &di, &de) == DFS_OK) {
    if (de.name[0] == 0) {
      // ignore deleted files
    } else if (de.attr == ATTR_LONG_NAME) {
      // ignore long names
    } else if (de.attr & ATTR_VOLUME_ID) {
      debug_apps_printf("volume ID: %-11.11s", de.name);
    } else if (de.attr & ATTR_DIRECTORY) {
      debug_apps_printf("directory: %-11.11s", de.name);
    } else {
      debug_apps_printf("file: %-11.11s", de.name);
      memcpy(filename, de.name, 11); // remember last filename
      filename[11] = 0;
    }
  }

  // dump last file
  if (DFS_OpenFile(vi, filename, DFS_READ, sectorBuf, &fi) != DFS_OK) {
    debug_apps_printf("cannot open file %s", filename);
    return;
  }
  debug_apps_printf("dumping file %s (max. %u bytes):", filename, sizeof(buf));
  if (DFS_ReadFile(&fi, sectorBuf, buf, &len, sizeof(buf)) != DFS_OK)
    len = 0;
  for (i = 0; i < len; ++i)
    debug_apps_printf("%3lu: %02hX", i, buf[i]);
  debug_apps_printf("done", filename);
}

// run applications
void AppsRun(void) // (extern)
{
  uint8_t sectorBuf[SECTOR_SIZE];
  uint32_t partStart, partSize;
  uint8_t partAct, partType;
  VOLINFO vi;
  const char *sec_clus, *fstype;

  // get start of first partition
  partStart = DFS_GetPtnStart(0, sectorBuf, 0, &partAct, &partType, &partSize);
  if (partStart == 0xFFFFFFFF) {
    debug_apps_printf("cannot find first partition");
    return;
  }
  debug_apps_printf("partition: start %lu size %lu", partStart, partSize);

  // get volume info
  if (DFS_GetVolInfo(0, sectorBuf, partStart, &vi) != DFS_OK) {
    debug_apps_printf("cannot get volume info");
    return;
  }
  switch (vi.filesystem) {
    case FAT12: sec_clus = "sector: "; fstype = "FAT12";   break;
    case FAT16: sec_clus = "sector: "; fstype = "FAT16";   break;
    case FAT32: sec_clus = "cluster:"; fstype = "FAT32";   break;
    default:    sec_clus = "sector: "; fstype = "unknown"; break;
  }
  debug_apps_printf("filesystem:                    %s", fstype);
  debug_apps_printf("volume label:                  %-11.11s", vi.label);
  debug_apps_printf("sector/s per cluster:          %hu", vi.secperclus);
  debug_apps_printf("reserved sector/s:             %hu", vi.reservedsecs);
  debug_apps_printf("volume total sectors:          %lu", vi.numsecs);
  debug_apps_printf("sectors per FAT:               %lu", vi.secperfat);
  debug_apps_printf("first FAT at sector:           %lu", vi.fat1);
  debug_apps_printf("root dir at %s           %lu", sec_clus, vi.rootdir);
  debug_apps_printf("root dir entries:              %hu", vi.rootentries);
  debug_apps_printf("data area commences at sector: %lu", vi.dataarea);
  debug_apps_printf("clusters in data area:         %lu", vi.numclusters);
  debug_apps_printf("sectors per cluster:           %hu", vi.secperclus);

  // list directory, dump file
  AppsListDump(sectorBuf, &vi);

  // run one-short applications
  AppCfgRun(sectorBuf, &vi);

  // initialize applications
  AppBbmState bbm0 = { .sectorBuf = &sectorBuf, .vi = &vi, .no = 0 };
  AppBbmState bbm1 = { .sectorBuf = &sectorBuf, .vi = &vi, .no = 1 };
  AppBbmState bbm2 = { .sectorBuf = &sectorBuf, .vi = &vi, .no = 2 };
  AppBbmInit(&bbm0);
  AppBbmInit(&bbm1);
  AppBbmInit(&bbm2);

  // run applications
  while (CfIsPresent()) {
    AppBbmRun(&bbm0);
    AppBbmRun(&bbm1);
    AppBbmRun(&bbm2);
    Tasks();
  }
}

