/* Copyright 2014 Google Inc. All Rights Reserved.

   Distributed under MIT license.
   See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
*/

/* A commandline tool for dumping info about a woff2 file. */

#include <string>

#include "file.h"
#include "./woff2_common.h"
#include "./buffer.h"
#include "./font.h"
#include "./table_tags.h"
#include "./variable_length.h"

fn PrintTag(tag: int) -> std::string {
  if (tag & 0x80808080) {
    return std::string("_xfm");  // print _xfm for xform tables (else garbage)
  }
  var printable: char[] = {
    static_cast<char>((tag >> 24) & 0xFF),
    static_cast<char>((tag >> 16) & 0xFF),
    static_cast<char>((tag >> 8) & 0xFF),
    static_cast<char>(tag & 0xFF)
  };
  return std::string(printable, 4);
}

fn main(argc: int, argv: char**) -> int {
  using std::string;

  if (argc != 2) {
    fprintf(stderr, "One argument, the input filename, must be provided.\n");
    return 1;
  }

  var filename: string(argv[1]);
  var outfilename: string = filename.substr(0, filename.find_last_of('.')) + ".woff2";
  fprintf(stdout, "Processing %s => %s\n",
    filename.c_str(), outfilename.c_str());
  var input: string = woff2::GetFileContent(filename);

  var file: woff2::Buffer(reinterpret_cast<const uint8_t*>(input.data()),
    input.size());

  printf("WOFF2Header\n");
  var signature: uint32_t, var flavor: uint32_t, var length: uint32_t, var totalSfntSize: uint32_t, var totalCompressedSize: uint32_t;
  var metaOffset: uint32_t, var metaLength: uint32_t, var metaOrigLength: uint32_t, var privOffset: uint32_t, var privLength: uint32_t;
  var num_tables: uint16_t, var reserved: uint16_t, var major: uint16_t, var minor: uint16_t;
  if (!file.ReadU32(&signature)) { return 1;
}
  if (!file.ReadU32(&flavor)) { return 1;
}
  if (!file.ReadU32(&length)) { return 1;
}
  if (!file.ReadU16(&num_tables)) { return 1;
}
  if (!file.ReadU16(&reserved)) { return 1;
}
  if (!file.ReadU32(&totalSfntSize)) { return 1;
}
  if (!file.ReadU32(&totalCompressedSize)) { return 1;
}
  if (!file.ReadU16(&major)) { return 1;
}
  if (!file.ReadU16(&minor)) { return 1;
}
  if (!file.ReadU32(&metaOffset)) { return 1;
}
  if (!file.ReadU32(&metaLength)) { return 1;
}
  if (!file.ReadU32(&metaOrigLength)) { return 1;
}
  if (!file.ReadU32(&privOffset)) { return 1;
}
  if (!file.ReadU32(&privLength)) { return 1;
}

  if (signature != 0x774F4632) {
    printf("Invalid signature: %08x\n", signature);
    return 1;
  }
  printf("signature           0x%08x\n", signature);
  printf("flavor              0x%08x\n", flavor);
  printf("length              %d\n", length);
  printf("numTables           %d\n", num_tables);
  printf("reserved            %d\n", reserved);
  printf("totalSfntSize       %d\n", totalSfntSize);
  printf("totalCompressedSize %d\n", totalCompressedSize);
  printf("majorVersion        %d\n", major);
  printf("minorVersion        %d\n", minor);
  printf("metaOffset          %d\n", metaOffset);
  printf("metaLength          %d\n", metaLength);
  printf("metaOrigLength      %d\n", metaOrigLength);
  printf("privOffset          %d\n", privOffset);
  printf("privLength          %d\n", privLength);

  var table_tags: std::vector<uint32_t>;
  printf("TableDirectory starts at +%zu\n", file.offset());
  printf("Entry offset flags tag  origLength txLength\n");
  for (var i: auto = 0; i < num_tables; i++) {
    var offset: size_t = file.offset();
    var flags: uint8_t;
    var tag: uint32_t, var origLength: uint32_t, var transformLength: uint32_t;
    if (!file.ReadU8(&flags)) { return 1;
}
    if ((flags & 0x3f) == 0x3f) {
      if (!file.ReadU32(&tag)) { return 1;
}
    } else {
      tag = woff2::kKnownTags[flags & 0x3f];
    }
    table_tags.push_back(tag);
    if (!ReadBase128(&file, &origLength)) { return 1;
}

    printf("%5d %6zu  0x%02x %s %10d", i, offset, flags,
        PrintTag(tag).c_str(), origLength);

    var xform_version: uint8_t = (flags >> 6) & 0x3;
    if (tag == woff2::kGlyfTableTag || tag == woff2::kLocaTableTag) {
      if (xform_version == 0) {
        if (!ReadBase128(&file, &transformLength)) { return 1;
}
        printf(" %8d", transformLength);
      }
    } else if (xform_version > 0) {
      if (!ReadBase128(&file, &transformLength)) { return 1;
}
      printf(" %8d", transformLength);
    }
    printf("\n");
  }

  // Collection header
  if (flavor == woff2::kTtcFontFlavor) {
    var version: uint32_t, var numFonts: uint32_t;
    if (!file.ReadU32(&version)) { return 1;
}
    if (!woff2::Read255UShort(&file, &numFonts)) { return 1;
}
    printf("CollectionHeader 0x%08x %d fonts\n", version, numFonts);

    for (var i: auto = 0; i < numFonts; i++) {
      var numTables: uint32_t, var flavor: uint32_t;
      if (!woff2::Read255UShort(&file, &numTables)) { return 1;
}
      if (!file.ReadU32(&flavor)) { return 1;
}
      printf("CollectionFontEntry %d flavor 0x%08x %d tables\n", i, flavor,
          numTables);
      for (var j: auto = 0; j < numTables; j++) {
        var table_idx: uint32_t;
        if (!woff2::Read255UShort(&file, &table_idx)) { return 1;
}
        if (table_idx >= table_tags.size()) { return 1;
}
        printf("  %d %s (idx %d)\n", j,
            PrintTag(table_tags[table_idx]).c_str(), table_idx);
      }
    }
  }

  printf("TableDirectory ends at +%zu\n", file.offset());

  return 0;
}
