/*
 * libbrlapi - A library providing access to braille terminals for applications.
 *
 * Copyright (C) 2006-2021 by
 *   Samuel Thibault <Samuel.Thibault@ens-lyon.org>
 *   Sébastien Hinderer <Sebastien.Hinderer@ens-lyon.org>
 *
 * libbrlapi 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>.
 */

package org.a11y.brlapi;

import java.util.Arrays;

public class WriteArguments extends Component {
  private String text = null;
  private byte andMask[] = null;
  private byte orMask[] = null;
  private int regionBegin = 0;
  private int regionSize = 0;
  private int cursorPosition = Constants.CURSOR_LEAVE;
  private int displayNumber = Constants.DISPLAY_DEFAULT;

  public WriteArguments () {
  }

  public String getText () {
    return text;
  }

  public WriteArguments setText (String text) {
    this.text = text;
    return this;
  }

  public byte[] getAndMask () {
    return andMask;
  }

  public WriteArguments setAndMask (byte[] mask) {
    andMask = mask;
    return this;
  }

  public byte[] getOrMask () {
    return orMask;
  }

  public WriteArguments setOrMask (byte[] mask) {
    orMask = mask;
    return this;
  }

  public int getRegionBegin () {
    return regionBegin;
  }

  public WriteArguments setRegionBegin (int begin) {
    regionBegin = begin;
    return this;
  }

  public int getRegionSize () {
    return regionSize;
  }

  public WriteArguments setRegionSize (int size) {
    regionSize = size;
    return this;
  }

  public WriteArguments setRegion (int begin, int size) {
    setRegionBegin(begin);
    setRegionSize(size);
    return this;
  }

  public int getCursorPosition () {
    return cursorPosition;
  }

  public WriteArguments setCursorPosition (int position) {
    cursorPosition = position;
    return this;
  }

  public int getDisplayNumber () {
    return displayNumber;
  }

  public WriteArguments setDisplayNumber (int number) {
    displayNumber = number;
    return this;
  }

  public final static String TEXT = "text";
  public final static String AND_MASK = "and-mask";
  public final static String OR_MASK = "or-mask";
  public final static String REGION_BEGIN = "region begin";
  public final static String REGION_SIZE = "region size";
  public final static String CURSOR_POSITION = "cursor position";
  public final static String DISPLAY_NUMBER = "display number";

  private static void checkRange (String description, int value, Integer minimum, Integer maximum) {
    try {
      if (minimum != null) Parse.checkMinimum(description, value, minimum);
      if (maximum != null) Parse.checkMaximum(description, value, maximum);
    } catch (SyntaxException exception) {
      throw new IllegalStateException(exception.getMessage());
    }
  }

  public final void check (int cellCount, boolean fix) {
    boolean haveRegionBegin = regionBegin != 0;
    boolean haveRegionSize = regionSize != 0;
    boolean haveRegion = haveRegionBegin || haveRegionSize;

    boolean haveText = text != null;
    boolean haveAndMask = andMask != null;
    boolean haveOrMask = orMask != null;
    boolean haveContent = haveText || haveAndMask || haveOrMask;

    if (haveRegion || haveContent) {
      if (!haveContent) {
        throw new IllegalStateException(
          String.format(
            "region content (%s, %s, and/or %s) not specified",
            TEXT, AND_MASK, OR_MASK
          )
        );
      }

      if (!haveRegionBegin) {
        if (!fix) {
          throw new IllegalStateException(
            String.format(
              "%s not set", REGION_BEGIN
            )
          );
        }

        regionBegin = 1;
        haveRegion = haveRegionBegin = true;
      }

      if (!haveRegionSize) {
        if (!fix) {
          throw new IllegalStateException(
            String.format(
              "%s not set", REGION_SIZE
            )
          );
        }

        regionSize = 0;
        if (haveText) regionSize = Math.max(regionSize, text.length());
        if (haveAndMask) regionSize = Math.max(regionSize, andMask.length);
        if (haveOrMask) regionSize = Math.max(regionSize, orMask.length);
        haveRegion = haveRegionSize = true;
      }

      checkRange(REGION_BEGIN, regionBegin, 1, cellCount);
      checkRange(REGION_SIZE, regionSize, 1, null);

      {
        int maximum = cellCount + 1 - regionBegin;

        if (!fix) {
          checkRange(REGION_SIZE, regionSize, null, maximum);
        } else if (regionSize > maximum) {
          regionSize = maximum;
        }
      }

      if (haveText) {
        int textLength = text.length();

        if (textLength > regionSize) {
          if (!fix) {
            throw new IllegalStateException(
              String.format(
                "%s length is greater than %s: %d > %d",
                TEXT, REGION_SIZE,
                textLength, regionSize
              )
            );
          }

          text = text.substring(0, regionSize);
        } else if (textLength < regionSize) {
          if (!fix) {
            throw new IllegalStateException(
              String.format(
                "%s length is less than %s: %d < %d",
                TEXT, REGION_SIZE,
                textLength, regionSize
              )
            );
          }

          StringBuilder newText = new StringBuilder(text);
          while (newText.length() < regionSize) newText.append(' ');
          text = newText.toString();
        }
      }

      if (haveAndMask) {
        int andSize = andMask.length;

        if (andSize > regionSize) {
          if (!fix) {
            throw new IllegalStateException(
              String.format(
                "%s size is greater than %s: %d > %d",
                AND_MASK, REGION_SIZE,
                andSize, regionSize
              )
            );
          }

          byte[] newMask = new byte[regionSize];
          System.arraycopy(andMask, 0, newMask, 0, regionSize);
          andMask = newMask;
        } else if (andSize < regionSize) {
          if (!fix) {
            throw new IllegalStateException(
              String.format(
                "%s size is less than %s: %d < %d",
                AND_MASK, REGION_SIZE,
                andSize, regionSize
              )
            );
          }

          byte[] newMask = new byte[regionSize];
          System.arraycopy(andMask, 0, newMask, 0, andSize);
          Arrays.fill(newMask, andSize, (regionSize - andSize), (byte)BYTE_MASK);
          andMask = newMask;
        }
      }

      if (haveOrMask) {
        int orSize = orMask.length;

        if (orSize > regionSize) {
          if (!fix) {
            throw new IllegalStateException(
              String.format(
                "%s size is greater than %s: %d > %d",
                OR_MASK, REGION_SIZE,
                orSize, regionSize
              )
            );
          }

          byte[] newMask = new byte[regionSize];
          System.arraycopy(orMask, 0, newMask, 0, regionSize);
          orMask = newMask;
        } else if (orSize < regionSize) {
          if (!fix) {
            throw new IllegalStateException(
              String.format(
                "%s size is less than %s: %d < %d",
                OR_MASK, REGION_SIZE,
                orSize, regionSize
              )
            );
          }

          byte[] newMask = new byte[regionSize];
          System.arraycopy(orMask, 0, newMask, 0, orSize);
          Arrays.fill(newMask, orSize, (regionSize - orSize), (byte)0);
          orMask = newMask;
        }
      }
    }

    if (cursorPosition != Constants.CURSOR_OFF) {
      if (cursorPosition != Constants.CURSOR_LEAVE) {
        checkRange(CURSOR_POSITION, cursorPosition, 1, cellCount);
      }
    }
  }
}
