/*
 * BRLTTY - A background process providing access to the console screen (when in
 *          text mode) for a blind person using a refreshable braille display.
 *
 * Copyright (C) 1995-2025 by The BRLTTY Developers.
 *
 * BRLTTY comes with ABSOLUTELY NO WARRANTY.
 *
 * This is free software, placed under the terms of the
 * GNU Lesser General Public License, as published by the Free Software
 * Foundation; either version 2.1 of the License, or (at your option) any
 * later version. Please see the file LICENSE-LGPL for details.
 *
 * Web Page: http://brltty.app/
 *
 * This software is maintained by Dave Mielke <dave@mielke.cc>.
 */

#include "prologue.h"

#include <string.h>

#include "cmdline.h"
#include "cmdput.h"
#include "log.h"
#include "file.h"

#include "ttb.h"
#include "ttb_internal.h"

#include "atb.h"
#include "atb_internal.h"

#include "ctb.h"
#include "ctb_internal.h"

BEGIN_COMMAND_LINE_OPTIONS(programOptions)
END_COMMAND_LINE_OPTIONS(programOptions)

static const char *tablePath;

BEGIN_COMMAND_LINE_PARAMETERS(programParameters)
  { .name = "table",
    .description = "the path to the table",
    .setting = &tablePath,
  },
END_COMMAND_LINE_PARAMETERS(programParameters)

BEGIN_COMMAND_LINE_NOTES(programNotes)
END_COMMAND_LINE_NOTES

BEGIN_COMMAND_LINE_DESCRIPTOR(programDescriptor)
  .name = "tbl2hex",
  .purpose = strtext("Write the hexadecimal array representation of a compiled table."),

  .options = &programOptions,
  .parameters = &programParameters,
  .notes = COMMAND_LINE_NOTES(programNotes),
END_COMMAND_LINE_DESCRIPTOR

typedef struct {
  void *object;
  const unsigned char *bytes;
  size_t size;
} TableData;

typedef struct {
  const char *extension;
  int (*load) (const char *path, TableData *data);
  void (*unload) (TableData *data);
} TableEntry;

static int
loadTextTable (const char *path, TableData *data) {
  TextTable *table = compileTextTable(path);
  if (!table) return 0;

  data->object = table;
  data->bytes = table->header.bytes;
  data->size = table->size;
  return 1;
}

static void
unloadTextTable (TableData *data) {
  destroyTextTable(data->object);
}

static int
loadAttributesTable (const char *path, TableData *data) {
  AttributesTable *table = compileAttributesTable(path);
  if (!table) return 0;

  data->object = table;
  data->bytes = table->header.bytes;
  data->size = table->size;
  return 1;
}

static void
unloadAttributesTable (TableData *data) {
  destroyAttributesTable(data->object);
}

static int
loadContractionTable (const char *path, TableData *data) {
  ContractionTable *table = compileContractionTable(path);
  if (!table) return 0;

  data->object = table;
  data->bytes = table->data.internal.header.bytes;
  data->size = table->data.internal.size;
  return 1;
}

static void
unloadContractionTable (TableData *data) {
  destroyContractionTable(data->object);
}

static const TableEntry tableEntries[] = {
  {
    .extension = TEXT_TABLE_EXTENSION,
    .load = loadTextTable,
    .unload = unloadTextTable
  }
  ,
  {
    .extension = ATTRIBUTES_TABLE_EXTENSION,
    .load = loadAttributesTable,
    .unload = unloadAttributesTable
  }
  ,
  {
    .extension = CONTRACTION_TABLE_EXTENSION,
    .load = loadContractionTable,
    .unload = unloadContractionTable
  }
  ,
  {
    .extension = NULL
  }
};

static const TableEntry *
findTableEntry (const char *extension) {
  const TableEntry *entry = tableEntries;

  while (entry->extension) {
    if (strcmp(entry->extension, extension) == 0) return entry;
    entry += 1;
  }

  logMessage(LOG_ERR, "unrecognized file extension: %s", extension);
  return NULL;
}

static void
dumpBytes (const unsigned char *bytes, size_t count) {
  const unsigned char *byte = bytes;
  const unsigned char *end = byte + count;
  int first = 1;
  int digits;

  if (count) {
    char buffer[0X10];
    digits = snprintf(buffer, sizeof(buffer), "%X", (unsigned int)count-1);
  } else {
    digits = 1;
  }

  while (byte < end) {
    while (!*byte && (byte < (end - 1))) byte += 1;

    {
      unsigned int counter = 0;
      unsigned int maximum = 8;

      if ((byte + maximum) != end) {
        while (maximum > 1) {
          if (byte[maximum-1]) break;
          maximum -= 1;
        }
      }

      while (byte < end) {
        if (first) {
          first = 0;
        } else {
          putByte(',');
          if (!counter) putNewline();
        }

        if (!counter) {
          putf("[0X%0*X] =", digits, (unsigned int)(byte-bytes));
        }

        putf(" 0X%02X", *byte++);
        if (++counter == maximum) break;
      }
    }
  }

  if (!first) putNewline();
}

int
main (int argc, char *argv[]) {
  PROCESS_COMMAND_LINE(programDescriptor, argc, argv);

  ProgramExitStatus exitStatus;

  const char *extension = locatePathExtension(tablePath);

  if (extension) {
    const TableEntry *entry = findTableEntry(extension);

    if (entry) {
      TableData data;
      if (entry->load(tablePath, &data)) {
        dumpBytes(data.bytes, data.size);
        exitStatus = PROG_EXIT_SUCCESS;
        entry->unload(&data);
      } else {
        exitStatus = PROG_EXIT_FATAL;
      }
    } else {
      exitStatus = PROG_EXIT_SEMANTIC;
    }
  } else {
    logMessage(LOG_ERR, "no file extension");
    exitStatus = PROG_EXIT_SEMANTIC;
  }

  return exitStatus;
}

#include "ctb_internal.h"

const unsigned char *
getInternalContractionTableBytes (void) {
  return NULL;
}

const ContractionTableTranslationMethods *
getContractionTableTranslationMethods_native (void) {
  return NULL;
}

const ContractionTableTranslationMethods *
getContractionTableTranslationMethods_external (void) {
  return NULL;
}

const ContractionTableTranslationMethods *
getContractionTableTranslationMethods_louis (void) {
  return NULL;
}
