package com.bytezone.diskbrowser.appleworks;

import java.util.ArrayList;
import java.util.List;

import com.bytezone.diskbrowser.applefile.AbstractFile;
import com.bytezone.diskbrowser.utilities.HexFormatter;

public class AppleworksSSFile extends AbstractFile
{
  Header header;
  List<Row> rows = new ArrayList<Row> ();

  public AppleworksSSFile (String name, byte[] buffer)
  {
    super (name, buffer);

    header = new Header ();

    int ptr = header.ssMinVers == 0 ? 300 : 302;
    while (ptr < buffer.length)
    {
      int length = HexFormatter.unsignedShort (buffer, ptr);

      if (length == 0xFFFF)
        break;

      ptr += 2;
      Row row = new Row (ptr);
      rows.add (row);
      ptr += length;
    }
  }

  @Override
  public String getText ()
  {
    StringBuilder text = new StringBuilder (header.toString ());

    for (Row row : rows)
    {
      text.append ("\n");
      for (Cell cell : row.cells)
        text.append (cell);
    }

    return text.toString ();
  }

  static String getCellName (int row, int column)
  {
    char c1 = (char) ('A' + column / 26 - 1);
    char c2 = (char) ('A' + column % 26);
    return "" + (c1 == '@' ? "" : c1) + c2 + row;
  }

  private class Header
  {
    private final int[] columnWidths = new int[127];
    private final char calcOrder;
    private final char calcFrequency;
    private final int lastRow;
    private final int lastColumn;
    private final char windowLayout;
    private final boolean windowSynch;
    private final Window currentWindow;
    private final Window secondWindow;
    private final boolean cellProtection;
    private final int platenWidth;
    private final int leftMargin;
    private final int rightMargin;
    private final int charsPerInch;

    private final int paperLength;
    private final int topMargin;
    private final int bottomMargin;
    private final int linesPerInch;
    private final char spacing;

    private final byte[] printerCodes = new byte[14];
    private final boolean printDash;
    private final boolean printHeader;
    private final boolean zoomed;

    private final int ssMinVers;

    public Header ()
    {
      int ptr = 4;
      for (int i = 0; i < columnWidths.length; i++)
        columnWidths[i] = buffer[ptr++] & 0xFF;

      calcOrder = (char) buffer[131];
      calcFrequency = (char) buffer[132];
      lastRow = HexFormatter.unsignedShort (buffer, 133);
      lastColumn = buffer[135] & 0xFF;
      windowLayout = (char) buffer[136];
      windowSynch = buffer[137] != 0;

      currentWindow = new Window (138);
      secondWindow = new Window (162);

      cellProtection = buffer[213] != 0;
      platenWidth = buffer[215] & 0xFF;
      leftMargin = buffer[216] & 0xFF;
      rightMargin = buffer[217] & 0xFF;
      charsPerInch = buffer[218] & 0xFF;

      paperLength = buffer[219] & 0xFF;
      topMargin = buffer[220] & 0xFF;
      bottomMargin = buffer[221] & 0xFF;
      linesPerInch = buffer[222] & 0xFF;
      spacing = (char) buffer[223];

      System.arraycopy (buffer, 224, printerCodes, 0, printerCodes.length);

      printDash = buffer[238] != 0;
      printHeader = buffer[239] != 0;
      zoomed = buffer[240] != 0;

      ssMinVers = buffer[242];
    }

    @Override
    public String toString ()
    {
      StringBuilder text = new StringBuilder ();

      text.append (String.format ("Calc order ..... %s %n", calcOrder));
      text.append (String.format ("Calc freq ...... %s %n", calcFrequency));
      text.append (String.format ("Last row ....... %d %n", lastRow));
      text.append (String.format ("Last column .... %d %n", lastColumn));
      text.append (String.format ("Window layout .. %s %n", windowLayout));
      text.append (String.format ("Window synch ... %s %n", windowSynch));
      text.append (String.format ("Min version .... %s %n%n", ssMinVers));

      String[] s1 = currentWindow.toString ().split ("\n");
      String[] s2 = secondWindow.toString ().split ("\n");
      for (int i = 0; i < s1.length; i++)
        text.append (String.format ("%-30s %-30s%n", s1[i], s2[i]));
      text.append ("\n");

      text.append (String.format ("Cell protect ... %s %n", cellProtection));
      text.append (String.format ("Platen width ... %d %n", platenWidth));
      text.append (String.format ("Left margin .... %d %n", leftMargin));
      text.append (String.format ("Right margin ... %d %n", rightMargin));
      text.append (String.format ("Chars per inch . %d %n", charsPerInch));

      text.append (String.format ("Paper length ... %d %n", paperLength));
      text.append (String.format ("Top margin ..... %d %n", topMargin));
      text.append (String.format ("Bottom margin .. %d %n", bottomMargin));
      text.append (String.format ("Lines per inch . %d %n", linesPerInch));
      text.append (String.format ("Spacing ........ %s %n", spacing));

      String prC = HexFormatter.getHexString (printerCodes);
      text.append (String.format ("Printer codes .. %s %n", prC));
      text.append (String.format ("Print dash ..... %s %n", printDash));
      text.append (String.format ("Print header ... %s %n", printHeader));
      text.append (String.format ("Zoomed ......... %s %n", zoomed));

      return text.toString ();
    }
  }

  private class Window
  {
    private final int justification;
    private final CellFormat format;
    private final int r1;
    private final int c1;
    private final int r2;
    private final int c2;
    private final int r3;
    private final int c3;
    private final int r4;
    private final int c4;
    private final int r5;
    private final int c5;
    private final int r6;
    private final int c6;
    private final int r7;
    private final int c7;
    private final int bodyRows;
    private final boolean rightColumnNotDisplayed;
    private final boolean topTitleSwitch;
    private final boolean sideTitleSwitch;

    public Window (int offset)
    {
      justification = buffer[offset] & 0xFF;

      format = new CellFormat (buffer[offset + 1], buffer[offset + 2]);

      r1 = buffer[offset + 3] & 0xFF;
      c1 = buffer[offset + 4] & 0xFF;
      r2 = HexFormatter.unsignedShort (buffer, offset + 5);
      c2 = buffer[offset + 7] & 0xFF;
      r3 = HexFormatter.unsignedShort (buffer, offset + 8);
      c3 = buffer[offset + 10] & 0xFF;
      r4 = HexFormatter.unsignedShort (buffer, offset + 11);
      c4 = buffer[offset + 13] & 0xFF;
      r5 = buffer[offset + 14] & 0xFF;
      c5 = buffer[offset + 15] & 0xFF;
      r6 = HexFormatter.unsignedShort (buffer, offset + 16);
      c6 = buffer[offset + 18] & 0xFF;
      r7 = buffer[offset + 19] & 0xFF;
      c7 = buffer[offset + 20] & 0xFF;

      bodyRows = buffer[offset + 21] & 0xFF;
      rightColumnNotDisplayed = buffer[offset + 21] != 0;

      int flags = buffer[offset + 23] & 0xFF;
      topTitleSwitch = (flags & 0x80) != 0;
      sideTitleSwitch = (flags & 0x40) != 0;
    }

    @Override
    public String toString ()
    {
      StringBuilder text = new StringBuilder ();

      text.append (String.format ("Justification .. %s %n", justification));
      text.append (String.format ("Format ......... %s %n", format.mask ()));
      text.append (String.format ("Decimals ....... %s %n", format.decimals));
      text.append (String.format ("Top line ....... %d %n", r1));
      text.append (String.format ("Left column .... %d %n", c1));
      text.append (String.format ("Title top line . %d %n", r2));
      text.append (String.format ("Title left col . %d %n", c2));
      text.append (String.format ("Top line ....... %d %n", r3));
      text.append (String.format ("Left column .... %d %n", c3));
      text.append (String.format ("Title top line . %d %n", r4));
      text.append (String.format ("Title left col . %d %n", c4));
      text.append (String.format ("Title top line . %d %n", r5));
      text.append (String.format ("Title left col . %d %n", c5));
      text.append (String.format ("Top line ....... %d %n", r6));
      text.append (String.format ("Left column .... %d %n", c6));
      text.append (String.format ("Title top line . %d %n", r7));
      text.append (String.format ("Title left col . %d %n", c7));
      text.append (String.format ("Body rows ...... %d %n", bodyRows));
      text.append (String.format ("Right col hidden %s %n", rightColumnNotDisplayed));
      text.append (String.format ("Top title sw ... %s %n", topTitleSwitch));
      text.append (String.format ("Left title sw .. %s %n", sideTitleSwitch));

      return text.toString ();
    }
  }

  private class Row
  {
    private final int rowNumber;
    private final List<Cell> cells = new ArrayList<Cell> ();

    public Row (int ptr)
    {
      rowNumber = HexFormatter.unsignedShort (buffer, ptr);
      ptr += 2;                                   // first control byte

      int column = 0;
      int val;

      while ((val = buffer[ptr++] & 0xFF) != 0xFF)
      {
        if (val > 0x80)
        {
          column += (val - 0x80);                 // skip columns
          continue;
        }

        if (ptr >= buffer.length)
        {
          System.out.println ("too long for buffer");
          break;
        }

        int b1 = buffer[ptr] & 0xFF;

        if ((b1 & 0xE0) == 0 || (b1 & 0xA0) == 0x20)                // Label - 000 or 0.1
          cells.add (new CellLabel (buffer, rowNumber, column++, ptr, val));
        else if ((b1 & 0xA0) == 0xA0)                               // Constant - 1.1
        {
          if (val > 0)
            cells.add (new CellConstant (buffer, rowNumber, column++, ptr, val));
        }
        else if ((b1 & 0xA0) == 0x80)                               // Value - 1.0
          cells.add (new CellValue (buffer, rowNumber, column++, ptr, val));
        else
          System.out.println ("Unknown Cell value : " + val);

        ptr += val;
      }
    }

    @Override
    public String toString ()
    {
      StringBuilder text = new StringBuilder ();

      text.append (String.format ("Row number ..... %s %n", rowNumber));
      for (Cell cell : cells)
      {
        text.append (cell);
        text.append ("\n");
      }

      return text.toString ();
    }
  }
}