/**
 * Copyright (c) 2011-2021 Bill Greiman
 * This file is part of the SdFat library for SD memory cards.
 *
 * MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
#include "FatLib.h"
#ifndef DOXYGEN_SHOULD_SKIP_THIS
//------------------------------------------------------------------------------
static uint16_t getLfnChar(DirLfn_t* ldir, uint8_t i) {
  if (i < 5) {
    return getLe16(ldir->unicode1 + 2*i);
  } else if (i < 11) {
    return getLe16(ldir->unicode2 + 2*i - 10);
  } else if (i < 13) {
    return getLe16(ldir->unicode3 + 2*i - 22);
  }
  return 0;
}
//------------------------------------------------------------------------------
static void printHex(print_t* pr, uint8_t h) {
  if (h < 16) {
    pr->write('0');
  }
  pr->print(h, HEX);
}
//------------------------------------------------------------------------------
static void printHex(print_t* pr, uint8_t w, uint16_t h) {
  char buf[5];
  char* ptr = buf + sizeof(buf);
  *--ptr = 0;
  for (uint8_t i = 0; i < w; i++) {
    char c = h & 0XF;
    *--ptr = c < 10 ? c + '0' : c + 'A' - 10;
    h >>= 4;
  }
  pr->write(ptr);
}
//------------------------------------------------------------------------------
static void printHex(print_t* pr, uint16_t val) {
  bool space = true;
  for (uint8_t i = 0; i < 4; i++) {
    uint8_t h = (val >> (12 - 4*i)) & 15;
    if (h || i == 3) {
      space = false;
    }
    if (space) {
      pr->write(' ');
    } else {
      pr->print(h, HEX);
    }
  }
}
//------------------------------------------------------------------------------
static void printHex(print_t* pr, uint32_t val) {
  bool space = true;
  for (uint8_t i = 0; i < 8; i++) {
    uint8_t h = (val >> (28 - 4*i)) & 15;
    if (h || i == 7) {
      space = false;
    }
    if (space) {
      pr->write(' ');
    } else {
      pr->print(h, HEX);
    }
  }
}
//------------------------------------------------------------------------------
template<typename Uint>
static void printHexLn(print_t* pr, Uint val) {
  printHex(pr, val);
  pr->println();
}
//------------------------------------------------------------------------------
static bool printFatDir(print_t* pr, DirFat_t* dir) {
  DirLfn_t* ldir = reinterpret_cast<DirLfn_t*>(dir);
  if (!dir->name[0]) {
    pr->println(F("Unused"));
    return false;
  } else if (dir->name[0] == FAT_NAME_DELETED) {
    pr->println(F("Deleted"));
  } else if (isFileOrSubdir(dir)) {
    pr->print(F("SFN: "));
    for (uint8_t i = 0; i < 11; i++) {
      printHex(pr, dir->name[i]);
      pr->write(' ');
    }
    pr->write(' ');
    pr->write(dir->name, 11);
    pr->println();
    pr->print(F("attributes: 0X"));
    printHexLn(pr, dir->attributes);
    pr->print(F("caseFlags: 0X"));
    printHexLn(pr, dir->caseFlags);
    uint32_t fc = ((uint32_t)getLe16(dir->firstClusterHigh) << 16)
                 | getLe16(dir->firstClusterLow);
    pr->print(F("firstCluster: "));
    pr->println(fc, HEX);
    pr->print(F("fileSize: "));
    pr->println(getLe32(dir->fileSize));
  } else if (isLongName(dir)) {
    pr->print(F("LFN: "));
    for (uint8_t i = 0; i < 13; i++) {
      uint16_t c = getLfnChar(ldir, i);
      if (15 < c && c < 128) {
        pr->print(static_cast<char>(c));
      } else {
        pr->print("0X");
        pr->print(c, HEX);
      }
      pr->print(' ');
    }
    pr->println();
    pr->print(F("order: 0X"));
    pr->println(ldir->order, HEX);
    pr->print(F("attributes: 0X"));
    pr->println(ldir->attributes, HEX);
    pr->print(F("checksum: 0X"));
    pr->println(ldir->checksum, HEX);
  } else {
    pr->println(F("Other"));
  }
  pr->println();
  return true;
}
//------------------------------------------------------------------------------
void FatFile::dmpFile(print_t* pr, uint32_t pos, size_t n) {
  char text[17];
  text[16] = 0;
  if (n >= 0XFFF0) {
    n = 0XFFF0;
  }
  if (!seekSet(pos)) {
    return;
  }
  for (size_t i = 0; i <= n; i++) {
    if ((i & 15) == 0) {
      if (i) {
        pr->write(' ');
        pr->write(text);
        if (i == n) {
          break;
        }
      }
      pr->write('\r');
      pr->write('\n');
      if (i >= n) {
        break;
      }
      printHex(pr, 4, i);
      pr->write(' ');
    }
    int16_t h = read();
    if (h < 0) {
      break;
    }
    pr->write(' ');
    printHex(pr, 2, h);
    text[i&15] = ' ' <= h && h < 0X7F ? h : '.';
  }
  pr->write('\r');
  pr->write('\n');
}
//------------------------------------------------------------------------------
bool FatPartition::dmpDirSector(print_t* pr, uint32_t sector) {
  DirFat_t dir[16];
  if (!cacheSafeRead(sector, reinterpret_cast<uint8_t*>(dir))) {
    pr->println(F("dmpDir failed"));
    return false;
  }
  for (uint8_t i = 0; i < 16; i++) {
    if (!printFatDir(pr, dir + i)) {
      return false;
    }
  }
  return true;
}
//------------------------------------------------------------------------------
bool FatPartition::dmpRootDir(print_t* pr, uint32_t n) {
  uint32_t sector;
  if (fatType() == 16) {
    sector = rootDirStart();
  } else if (fatType() == 32) {
    sector = clusterStartSector(rootDirStart());
  } else {
    pr->println(F("dmpRootDir failed"));
    return false;
  }
  return dmpDirSector(pr, sector + n);
}
//------------------------------------------------------------------------------
void FatPartition::dmpSector(print_t* pr, uint32_t sector, uint8_t bits) {
  uint8_t data[FatPartition::m_bytesPerSector];
  if (!cacheSafeRead(sector, data)) {
    pr->println(F("dmpSector failed"));
    return;
  }
  for (uint16_t i = 0; i < m_bytesPerSector;) {
    if (i%32 == 0) {
      if (i) {
        pr->println();
      }
      printHex(pr, i);
    }
    pr->write(' ');
    if (bits == 32) {
      printHex(pr, *reinterpret_cast<uint32_t*>(data + i));
      i += 4;
    } else if (bits == 16) {
      printHex(pr, *reinterpret_cast<uint16_t*>(data + i));
      i += 2;
    } else {
      printHex(pr, data[i++]);
    }
  }
  pr->println();
}
//------------------------------------------------------------------------------
void FatPartition::dmpFat(print_t* pr, uint32_t start, uint32_t count) {
  uint16_t nf = fatType() == 16 ? 256 : fatType() == 32 ? 128 : 0;
  if (nf == 0) {
    pr->println(F("Invalid fatType"));
    return;
  }
  pr->println(F("FAT:"));
  uint32_t sector = m_fatStartSector + start;
  uint32_t cluster = nf*start;
  for (uint32_t i = 0; i < count; i++) {
    uint8_t* pc = fatCachePrepare(sector + i, FsCache::CACHE_FOR_READ);
    if (!pc) {
      pr->println(F("cache read failed"));
      return;
    }
    for (size_t k = 0; k < nf; k++) {
      if (0 == cluster%8) {
        if (k) {
          pr->println();
        }
        printHex(pr, cluster);
      }
      cluster++;
      pr->write(' ');
      uint32_t v = fatType() == 32 ? getLe32(pc + 4*k) : getLe16(pc + 2*k);
      printHex(pr, v);
    }
    pr->println();
  }
}
#endif  // DOXYGEN_SHOULD_SKIP_THIS
