package com.bytezone.diskbrowser.infocom;

import java.io.File;

import com.bytezone.diskbrowser.disk.Disk;

// -----------------------------------------------------------------------------------//
class Header extends InfocomAbstractFile
// -----------------------------------------------------------------------------------//
{
  final String[] propertyNames = new String[32];

  private final File file;
  int version;
  int highMemory;
  int programCounter;
  int dictionaryOffset;
  int objectTableOffset;
  int globalsOffset;
  int staticMemory;
  int abbreviationsTable;
  int fileLength;
  int checksum;
  int stringPointer;

  final Abbreviations abbreviations;
  final ObjectManager objectManager;
  final Globals globals;
  final Grammar grammar;
  final Dictionary dictionary;
  final CodeManager codeManager;
  final StringManager stringManager;

  // ---------------------------------------------------------------------------------//
  Header (String name, byte[] buffer, Disk disk)
  // ---------------------------------------------------------------------------------//
  {
    super (name, buffer);
    this.file = disk.getFile ();

    version = getByte (00);
    highMemory = getWord (0x04);
    programCounter = getWord (0x06);

    dictionaryOffset = getWord (0x08);
    objectTableOffset = getWord (0x0A);
    globalsOffset = getWord (0x0C);
    staticMemory = getWord (0x0E);
    abbreviationsTable = getWord (0x18);

    fileLength = getWord (0x1A) * 2;            // 2 for versions 1-3
    checksum = getWord (0x1C);
    int interpreterNumber = getByte (0x1E);
    int interpreterVersion = getByte (0x1F);
    int revision = getWord (0x30);

    System.out.printf ("Version    : %d%n", version);
    System.out.printf ("Interpreter: %d.%d%n", interpreterNumber, interpreterVersion);
    System.out.printf ("Revision   : %d%n", revision);

    if (fileLength == 0)
      fileLength = buffer.length;

    // do the basic managers
    abbreviations = new Abbreviations (this);
    dictionary = new Dictionary (this);
    globals = new Globals (this);                     // may display ZStrings

    // set up an empty object to store Routines in
    codeManager = new CodeManager (this);

    grammar = new Grammar ("Grammar", buffer, this);

    // add all the ZObjects, and analyse them to find stringPtr, DICT etc.
    objectManager = new ObjectManager (this);

    // add all the ZStrings
    stringManager = new StringManager ("Strings", buffer, this);

    codeManager.addRoutines (programCounter);

    // add entries for AbstractFile.getHexDump ()
    hexBlocks.add (new HexBlock (0, 64, "Header data:"));
  }

  // ---------------------------------------------------------------------------------//
  String getPropertyName (int id)
  // ---------------------------------------------------------------------------------//
  {
    if (id >= propertyNames.length)
      return "Bad id : " + id;
    return propertyNames[id];
  }

  // ---------------------------------------------------------------------------------//
  public String getAbbreviation (int index)
  // ---------------------------------------------------------------------------------//
  {
    return abbreviations.getAbbreviation (index);
  }

  // ---------------------------------------------------------------------------------//
  public boolean containsWordAt (int address)
  // ---------------------------------------------------------------------------------//
  {
    return dictionary.containsWordAt (address);
  }

  // ---------------------------------------------------------------------------------//
  public String wordAt (int address)
  // ---------------------------------------------------------------------------------//
  {
    return dictionary.wordAt (address);
  }

  // ---------------------------------------------------------------------------------//
  @Override
  public String getText ()
  // ---------------------------------------------------------------------------------//
  {
    StringBuilder text = new StringBuilder ();

    text.append (String.format ("Disk name                %s%n", file.getName ()));
    text.append (String.format ("Version                  %d%n", version));
    text.append ("\nDynamic memory:\n");
    text.append (String.format ("  Abbreviation table     %04X  %,6d%n",
        abbreviationsTable, abbreviationsTable));
    text.append (String.format ("  Objects table          %04X  %,6d%n",
        objectTableOffset, objectTableOffset));
    text.append (String.format ("  Global variables       %04X  %,6d%n", globalsOffset,
        globalsOffset));

    text.append ("\nStatic memory:\n");
    text.append (String.format ("  Grammar table etc      %04X  %,6d%n", staticMemory,
        staticMemory));
    text.append (String.format ("  Dictionary             %04X  %,6d%n", dictionaryOffset,
        dictionaryOffset));
    text.append ("\nHigh memory:\n");
    text.append (
        String.format ("  ZCode                  %04X  %,6d%n", highMemory, highMemory));
    text.append (String.format ("  Program counter        %04X  %,6d%n", programCounter,
        programCounter));
    text.append (
        String.format ("\nFile length             %05X %,7d%n", fileLength, fileLength));
    text.append (
        String.format ("Checksum                 %04X  %,6d%n", checksum, checksum));
    text.append (String.format ("%nZString offset          %05X %,7d%n", stringPointer,
        stringPointer));

    text.append (String.format ("Total strings                     %d%n",
        stringManager.strings.size ()));
    text.append (String.format ("Total objects                     %d%n",
        objectManager.getObjects ().size ()));

    text.append (getAlternate ());

    return text.toString ();
  }

  // ---------------------------------------------------------------------------------//
  private String getAlternate ()
  // ---------------------------------------------------------------------------------//
  {
    StringBuilder text = new StringBuilder ("\n\n");

    text.append (getLine (0, 1, "version"));
    text.append (getLine (1, 3, "flags 1"));
    text.append (getLine (4, 2, "high memory"));
    text.append (getLine (6, 2, "program counter"));
    text.append (getLine (8, 2, "dictionary"));
    text.append (getLine (10, 2, "object table"));
    text.append (getLine (12, 2, "global variables"));
    text.append (getLine (14, 2, "static memory"));
    text.append (getLine (16, 2, "flags 2"));
    text.append (getLine (24, 2, "abbreviations table"));
    text.append (getLine (26, 2, "length of file (x2 = " + fileLength + ")"));
    text.append (getLine (28, 2, "checksum"));
    text.append (getLine (50, 1, "revision number"));
    return text.toString ();
  }

  // ---------------------------------------------------------------------------------//
  private String getLine (int offset, int size, String description)
  // ---------------------------------------------------------------------------------//
  {
    StringBuilder text = new StringBuilder ();
    text.append (String.format ("%04X - %04X  ", offset, offset + size - 1));
    for (int i = 0; i < size; i++)
      text.append (String.format ("%02X ", buffer[offset + i]));
    while (text.length () < 24)
      text.append (" ");
    text.append (description);
    text.append ("\n");
    return text.toString ();
  }

  // ---------------------------------------------------------------------------------//
  ZObject getObject (int index)
  // ---------------------------------------------------------------------------------//
  {
    return objectManager.getObject (index);
  }

  // ---------------------------------------------------------------------------------//
  int getByte (int offset)
  // ---------------------------------------------------------------------------------//
  {
    return buffer[offset] & 0xFF;
  }

  // ---------------------------------------------------------------------------------//
  int getWord (int offset)
  // ---------------------------------------------------------------------------------//
  {
    return ((buffer[offset] << 8) & 0xFF00) | ((buffer[offset + 1]) & 0xFF);
  }
}