/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const MIN_SELECTION_SIZE = 48;

/**
 * This class is copied from https://searchfox.org/mozilla-central/source/browser/components/screenshots/overlayHelpers.mjs
 * with some slight modifications such as forcing the region to be a square.
 * Bug 1974999: Actually import and use the screenshots Region class.
 * The class holds references to each side the of region.
 * x1 is the left boundary, x2 is the right boundary, y1 is the top boundary,
 * and y2 is the bottom boundary. Sometimes the sides can get switched where
 * x1 > x2 or y1 > y2. To mitigate this, the getters (left, right, top, bottom)
 * will choose to return the "correct" value. For example, the left getter
 * returns the min of x1 and x2. The same goes for the other three getters.
 */
export class Region {
  #x1;
  #x2;
  #y1;
  #y2;
  #viewDimensions;

  constructor(viewDimensions) {
    this.resetDimensions();
    this.#viewDimensions = viewDimensions;
  }

  /**
   * Sets the dimensions if the given dimension is defined.
   * Otherwise will reset the dimensions
   *
   * @param {object} dims The new region dimensions
   *  {
   *    left: new left dimension value or undefined
   *    top: new top dimension value or undefined
   *    right: new right dimension value or undefined
   *    bottom: new bottom dimension value or undefined
   *   }
   */
  set #dimensions(dims) {
    if (dims == null) {
      this.resetDimensions();
      return;
    }

    if (dims.left != null) {
      this.left = dims.left;
    }
    if (dims.top != null) {
      this.top = dims.top;
    }
    if (dims.right != null) {
      this.right = dims.right;
    }
    if (dims.bottom != null) {
      this.bottom = dims.bottom;
    }
  }

  /**
   * Set the new dimension for the region. This is called from pointer move
   * events where the region is being moved.
   *
   * @param {object} dims The new dimensions. The object should contain left,
   *   top, right, bottom.
   * @param {string} direction The corner of the region being dragged. It is
   *   used to determine which sides of the region to contain to a square.
   */
  resizeToSquare(dims, direction) {
    // eslint-disable-next-line no-shadow
    let { left, right, top, bottom } = dims;
    switch (direction) {
      case "mover-topLeft": {
        let newDiameter = Math.max(
          MIN_SELECTION_SIZE,
          this.right - left,
          this.bottom - top
        );
        this.left = this.right - newDiameter;
        this.top = this.bottom - newDiameter;
        break;
      }
      case "mover-topRight": {
        let newDiameter = Math.max(
          MIN_SELECTION_SIZE,
          right - this.left,
          this.bottom - top
        );
        this.right = this.left + newDiameter;
        this.top = this.bottom - newDiameter;
        break;
      }
      case "mover-bottomRight": {
        let newDiameter = Math.max(
          MIN_SELECTION_SIZE,
          right - this.left,
          bottom - this.top
        );
        this.right = this.left + newDiameter;
        this.bottom = this.top + newDiameter;
        break;
      }
      case "mover-bottomLeft": {
        let newDiameter = Math.max(
          MIN_SELECTION_SIZE,
          this.right - left,
          bottom - this.top
        );
        this.left = this.right - newDiameter;
        this.bottom = this.top + newDiameter;
        break;
      }
      default: {
        if (
          left < 0 ||
          right > this.#viewDimensions.width ||
          top < 0 ||
          bottom > this.#viewDimensions.height
        ) {
          // The region would be invalid so just return
          return;
        }
        this.#dimensions = dims;
        break;
      }
    }

    this.forceSquare(direction);
  }

  get dimensions() {
    return {
      left: this.left,
      top: this.top,
      right: this.right,
      bottom: this.bottom,
      width: this.width,
      height: this.height,
      radius: this.radius,
    };
  }

  resetDimensions() {
    this.#x1 = 0;
    this.#x2 = 0;
    this.#y1 = 0;
    this.#y2 = 0;
  }

  /**
   * Sort the coordinates so x1 < x2 and y1 < y2
   */
  sortCoords() {
    if (this.#x1 > this.#x2) {
      [this.#x1, this.#x2] = [this.#x2, this.#x1];
    }
    if (this.#y1 > this.#y2) {
      [this.#y1, this.#y2] = [this.#y2, this.#y1];
    }
  }

  forceSquare(direction) {
    if (this.width === this.height && this.width >= MIN_SELECTION_SIZE) {
      // Already square and large enough
      return;
    }

    let newDiameter = Math.max(
      MIN_SELECTION_SIZE,
      Math.min(this.width, this.height)
    );
    switch (direction) {
      case "mover-topLeft": {
        this.left = this.right - newDiameter;
        this.top = this.bottom - newDiameter;
        break;
      }
      case "mover-topRight": {
        this.right = this.left + newDiameter;
        this.top = this.bottom - newDiameter;
        break;
      }
      case "mover-bottomRight": {
        this.right = this.left + newDiameter;
        this.bottom = this.top + newDiameter;
        break;
      }
      case "mover-bottomLeft": {
        this.left = this.right - newDiameter;
        this.bottom = this.top + newDiameter;
        break;
      }
      default: {
        newDiameter = Math.max(MIN_SELECTION_SIZE, this.width, this.height);
        if (this.left === 0) {
          this.right = newDiameter;
        }
        if (this.right === this.#viewDimensions.width) {
          this.left = this.right - newDiameter;
        }
        if (this.top === 0) {
          this.bottom = newDiameter;
        }
        if (this.bottom === this.#viewDimensions.height) {
          this.top = this.bottom - newDiameter;
        }
      }
    }
  }

  get top() {
    return Math.min(this.#y1, this.#y2);
  }
  set top(val) {
    this.#y1 = Math.min(this.#viewDimensions.height, Math.max(0, val));
  }

  get left() {
    return Math.min(this.#x1, this.#x2);
  }
  set left(val) {
    this.#x1 = Math.min(this.#viewDimensions.width, Math.max(0, val));
  }

  get right() {
    return Math.max(this.#x1, this.#x2);
  }
  set right(val) {
    this.#x2 = Math.min(this.#viewDimensions.width, Math.max(0, val));
  }

  get bottom() {
    return Math.max(this.#y1, this.#y2);
  }
  set bottom(val) {
    this.#y2 = Math.min(this.#viewDimensions.height, Math.max(0, val));
  }

  get width() {
    return Math.abs(this.#x2 - this.#x1);
  }
  get height() {
    return Math.abs(this.#y2 - this.#y1);
  }

  get radius() {
    return Math.floor(this.width / 2);
  }

  get x1() {
    return this.#x1;
  }
  get x2() {
    return this.#x2;
  }
  get y1() {
    return this.#y1;
  }
  get y2() {
    return this.#y2;
  }
}

/**
 * A class which saves the current view dimensions.
 */
export class ViewDimensions {
  #height = null;
  #width = null;
  #devicePixelRatio = null;

  set dimensions(dimensions) {
    if (dimensions.height != null) {
      this.#height = dimensions.height;
    }
    if (dimensions.width != null) {
      this.#width = dimensions.width;
    }
    if (dimensions.devicePixelRatio != null) {
      this.#devicePixelRatio = dimensions.devicePixelRatio;
    }
  }

  get dimensions() {
    return {
      height: this.height,
      width: this.width,
      devicePixelRatio: this.devicePixelRatio,
    };
  }

  get width() {
    return this.#width;
  }

  get height() {
    return this.#height;
  }

  get devicePixelRatio() {
    return this.#devicePixelRatio;
  }

  reset() {
    this.#width = 0;
    this.#height = 0;
  }
}
