package stateandbehavior;

import java.util.Arrays;

public class Rectangle {
	private int[] minPoint; 
  private int[] maxPoint; 

  public Rectangle() {
    this.minPoint = (new int[] {});
    this.maxPoint = (new int[] {});
  }

  /**
    * returnerer henholdsvis x- og y-koordinatene til punktet med
	  * lavest (x,y)-verdier som er inneholdt i dette rektanglet. Dersom dette rektanglet er tomt, så skal 0 returneres.
	  */
	public int getMinX() {
		return this.isEmpty() ? 0 : this.minPoint[0];
	}

  /**
   * Se {@link #getMinX() getMinX()}
   */
	public int getMinY() {
		return this.isEmpty() ? 0 : this.minPoint[1];
	}
  
	/**
	  * returnerer henholdsvis x- og y-koordinatene til punktet med høyest (x,y)-verdier som er inneholdt i dette rektanglet. Dersom dette rektanglet er tomt, så skal 0 returneres.
	  */	
	public int getMaxX() {
		return this.isEmpty() ? 0 : this.maxPoint[0];
	}
	
  /**
   * Se {@link #getMaxX() getMaxX()}
   */
	public int getMaxY() {
		return this.isEmpty() ? 0 : this.maxPoint[1];
	}

	/** returnerer henholdsvis bredden og høyden til rektanglet. Begge skal returnere 0, dersom dette rektanglet er tomt. */
	public int getWidth() {
    if (this.isEmpty()) return 0;
    return this.maxPoint[0] - this.minPoint[0] + 1;
	}

	public int getHeight() {
    if (this.isEmpty()) return 0;
    return this.maxPoint[1] - this.minPoint[1] + 1;
	}

	/** returnerer true om rektanglet er tomt, dvs. om bredden og/eller høyden er 0. */
	public boolean isEmpty(){
	  return this.minPoint.length == 0;
	}

  /** returnerer true om punktet (x,y) er inneholdt i dette rektanglet, og false ellers. */
	public boolean contains(int x, int y) {
    if (this.isEmpty()) return false;
		return Arrays.asList(
      (this.maxPoint[0] >= x),
			(this.maxPoint[1] >= y),
			(this.minPoint[0] <= x),
      (this.minPoint[1] <= y))
        .stream()
        .allMatch(b -> b);
	}

	/** returnerer true om hele rect, dvs. alle punktene i rect, er inneholdt i dette rektanglet, og false ellers. Dersom rect er tomt, så skal false returneres. */
	public boolean contains(Rectangle rect) {
    if (rect.isEmpty()) return false;
    return this.contains(rect.getMaxX(), rect.getMaxY())
        && this.contains(rect.getMinX(), rect.getMinY());

	}

  /** utvider (om nødvendig) dette rektanglet slik at det (akkurat) inneholder punktet (x,y). Etter kallet skal altså contains(x, y) returnere true. Returnerer true om dette rektanglet faktisk ble endret, ellers false. */
	public boolean add(int x, int y) {

    if (this.isEmpty()) {
      this.maxPoint = (new int[] {x, y});
      this.minPoint = (new int[] {x, y});
      return true;
    }

    boolean wasChanged = false;

		if (x > this.maxPoint[0]) {
			this.maxPoint[0] = x;
			wasChanged = true;
		}
		if (y > this.maxPoint[1]) {
			this.maxPoint[1] = y;
			wasChanged = true;
		}
		if (x < this.minPoint[0]) {
			this.minPoint[0] = x;
			wasChanged = true;
		}
		if (y < this.minPoint[1]) {
			this.minPoint[1] = y;
			wasChanged = true;
		}

		return wasChanged;
	}

	/** utvider (om nødvendig) dette rektanglet slik at det (akkurat) inneholder hele rect-argumentet. Returnerer true om dette rektanglet faktisk ble endret, ellers false. Dersom rect er tomt, så skal dette rektanglet ikke endres. */
	public boolean add(Rectangle rect) {
    boolean addedp1 = this.add(rect.getMaxX(), rect.getMaxY());
		boolean addedp2 = this.add(rect.getMinX(), rect.getMinY());
    return (addedp1 || addedp2);
	}

	/** returnerer et nytt Rectangle-objekt som tilsvarer kombisjonen av dette rektanglet og rect-argumentet. Alle punktene som finnes i ett av rektanglene skal altså være inneholdt i rektanglet som returneres. */
	public Rectangle union(Rectangle rect) {
		Rectangle result = new Rectangle();
		result.add(this);
		result.add(rect);
		return result;
	}

  /** returnerer et nytt Rectangle-objekt som tilsvarer overlappet mellom dette rektanglet og rect-argumentet. Alle punktene som finnes i begge rektanglene skal altså være inneholdt i rektanglet som returneres. */
	public Rectangle intersection(Rectangle rect) {
    if (this.intersects(rect)) {
      int maxX = Math.max(this.minPoint[0], rect.getMinX());
      int maxY = Math.max(this.minPoint[1], rect.getMinY());
      int minX = Math.min(this.maxPoint[0], rect.getMaxX());
      int minY = Math.min(this.maxPoint[1], rect.getMaxY());

      Rectangle result = new Rectangle();
      result.add(minX, minY);
      result.add(maxX, maxY);
      return result;
    } else {
      return new Rectangle();
    }
	}

	/** returnerer true om dette rektanglet og rect-argumentet overlapper, dvs. om det finnes ett eller flere punkter som er inneholdt i begge disse rektanglene. */
	public boolean intersects(Rectangle rect) {
    if (this.isEmpty() || rect.isEmpty()) return false;
    return !((this.minPoint[0] > rect.getMaxX() || rect.getMinX() > this.maxPoint[0])
          && (this.minPoint[1] > rect.getMaxY() || rect.getMinY() > this.maxPoint[1]));
  }
  
  @Override
  public String toString() {
    if (this.isEmpty()) return "Empty";
    return String.format("(%d, %d), (%d, %d), [%d x %d]",
      minPoint[0],
      minPoint[1],
      maxPoint[0],
      maxPoint[1],
      getWidth(),
      getHeight()
    );
  }
}