//=====Copyright 2017 Clayton Breckel=====//
package com.brekcel.RLGdx.console

import com.badlogic.gdx.graphics.Color

/** The base class of the console. Used to blit things onto the RootConsole. Has no inherit way of displaying content onto the screen. */
open class Console {

	/**
	 * If true, checks before each set call whether or not the given coords are in bound. If true, and a given coord is out of bounds, it will just not draw it, otherwise it will naturally throw an array out of bounds exception.
	 * *FOR THE LOOPS ONLY*.
	 */
	var safe: Boolean

	/** The 2d array containing all of the cells */
	val cells: Array<Array<Cell>>

	/**	The width of the console */
	val width: Int

	/** The height of the console */
	val height: Int

	/** Range of [0,width) */
	val widthRange: IntRange

	/** Range of [0,height) */
	val heightRange: IntRange

	/**
	 * Creates a new console that. Used to create "subconsoles" to be copied onto the main RootConsole to draw.
	 * @param width The width of this console
	 * @param height The height of this console
	 * @param safe Whether or not it uses the [safe] check
	 */
	constructor(width: Int, height: Int, safe: Boolean) {
		this.width = width
		this.height = height
		this.widthRange = 0 until width
		this.heightRange = 0 until height
		this.safe = safe
		cells = Array(width, { Array(height, { Cell() }) })
	}

	/**
	 * Sets the entire console to the given Cell
	 * @param symbol The symbol to set the console to
	 * @param foreColor The Forecolor to set the console to
	 * @param backColor The backcolor to set the console to
	 */
	fun clear(symbol: Char, foreColor: Color, backColor: Color) = clear(Cell(symbol, foreColor, backColor))

	/**
	 * Sets the entire console to the given Cell
	 * @param cell The Cell to set each cell to
	 */
	@JvmOverloads
	fun clear(cell: Cell = Cell()) {
		widthRange.forEach { x ->
			heightRange.forEach { y ->
				cells[x][y].set(cell)
			}
		}
	}

	/**
	 * Sets the cell from the given x,y coord to the given Cell
	 * @param x The x coord of the cell to be set
	 * @param y The y coord of the cell to be set
	 * @param cell The Cell to set each cell to
	 */
	fun set(x: Int, y: Int, cell: Cell) {
		cells[x][y].set(cell)
	}

	/**
	 * Sets the cells beginning from the given x,y to the x+width and y+height coord's to the given Cell
	 * @param x The beginning x coord of the cells to be set
	 * @param y The beginning y coord of the cells to be set
	 * @param width The amount of cells in the x direction to be set
	 * @param height The amount of cells in the given y direction to be set
	 * @param c The character to set each cell to
	 * @param foreColor The ForeColor to set each cell to
	 * @param backColor The BackColor to set each cell to
	 */
	fun set(x: Int, y: Int, width: Int, height: Int, c: Char, foreColor: Color, backColor: Color) = set(x, y, width, height, Cell(c, foreColor, backColor))

	/**
	 * Sets the cells beginning from the given x,y to the x+width and y+height coord's to the given Cell
	 * @param x The beginning x coord of the cells to be set
	 * @param y The beginning y coord of the cells to be set
	 * @param width The amount of cells in the x direction to be set
	 * @param height The amount of cells in the given y direction to be set
	 * @param cell The Cell to set each cell to
	 */
	fun set(x: Int, y: Int, width: Int, height: Int, cell: Cell) {
		x.until(x + width).forEach x@ { x1 ->
			if (safe && x1 > this.width)
				return@x
			y.until(y + height).forEach y@ { y1 ->
				if (safe && y1 > this.height)
					return@y
				cells[x1][y1].set(cell)
			}
		}
	}

	//CHARACTER BASED SET

	/**
	 * Sets the char at the given (x,y) coord to the given character
	 * @param x The x position of the cell to be changed
	 * @param y The y positon of the cell to be changed
	 * @param c The character to set
	 */
	fun set(x: Int, y: Int, c: Char) {
		cells[x][y].char = c
	}

	/**
	 * Sets the cell's chars beginning from the given x,y to x+width,y+height coord's to the given character
	 * @param x The beginning x coord of the cells to be set
	 * @param y The beginning y coord of the cells to be set
	 * @param width The amount of cells in the x direction to be set
	 * @param height The amount of cells in the given y direction to be set
	 * @param c The character to set each cell to
	 */
	fun set(x: Int, y: Int, width: Int, height: Int, c: Char) {
		x.until(x + width).forEach x@ { x1 ->
			if (safe && x1 > this.width)
				return@x
			y.until(y + height).forEach y@ { y1 ->
				if (safe && y1 > this.height)
					return@y
				cells[x1][y1].char = c
			}
		}
	}

	/**
	 * Prints a string starting at the given x,y coord
	 * @param x The x coord to start the message at
	 * @param y The y coord of the message
	 * @param s The String to be printed
	 * @param foreColor *OPTIONAL* The Forecolor to be printed
	 * @param backColor *OPTIONAL* The Backcolor to be printed
	 * @param w *OPTIONAL* The width, in chars, of the string that's printed. If w < s.length, will break after printing w chars. If w > s.length, will fill remaining chars with 0. Defaults to w = s.length.
	 */
	@JvmOverloads
	fun print(x: Int, y: Int, s: String, foreColor: Color? = null, backColor: Color? = null, w: Int = s.length) {
		0.until(w).forEach i@ { i ->
			if ((safe && x + i > width) || i > w)
				return@i
			cells[x + i][y].char = if (i < s.length) s[i] else 0.toChar()
			if (foreColor != null)
				cells[x + i][y].foreColor = foreColor
			if (backColor != null)
				cells[x + i][y].backColor = backColor
		}
	}



	//FORECOLOR BASED SET

	/**
	 * Sets the ForeColor at the given (x,y) coord to the given Color
	 * @param x The x position of the cell to be changed
	 * @param y The y positon of the cell to be changed
	 * @param c The ForeColor to set
	 */
	fun setForeColor(x: Int, y: Int, c: Color) {
		cells[x][y].foreColor = c.cpy()
	}

	/**
	 * Sets the cell's Forecolors beginning from the given x,y to x+width,y+height coord's to the given Color
	 * @param x The beginning x coord of the cells to be set
	 * @param y The beginning y coord of the cells to be set
	 * @param width The amount of cells in the x direction to be set
	 * @param height The amount of cells in the given y direction to be set
	 * @param foreColor The ForeColor to set each cell to
	 */
	fun setForeColor(x: Int, y: Int, width: Int, height: Int, foreColor: Color) {
		x.until(x + width).forEach x@ { x1 ->
			if (safe && x1 > this.width)
				return@x
			y.until(y + height).forEach y@ { y1 ->
				if (safe && y1 > this.height)
					return@y
				cells[x1][y1].foreColor = foreColor
			}
		}
	}

	//BACKCOLOR BASED SET
	/**
	 * Sets the BackColor at the given (x,y) coord to the given Color
	 * @param x The x position of the cell to be changed
	 * @param y The y positon of the cell to be changed
	 * @param c The BackColor to set
	 */
	fun setBackColor(x: Int, y: Int, c: Color) {
		cells[x][y].backColor = c
	}

	/**
	 * Sets the cell's BackColor beginning from the given x,y to x+width,y+height coord's to the given Color
	 * @param x The beginning x coord of the cells to be set
	 * @param y The beginning y coord of the cells to be set
	 * @param width The amount of cells in the x direction to be set
	 * @param height The amount of cells in the given y direction to be set
	 * @param backColor The BackColor to set each cell to
	 */
	fun setBackColor(x: Int, y: Int, width: Int, height: Int, backColor: Color) {
		x.until(x + width).forEach x@ { x1 ->
			if (safe && x1 > this.width)
				return@x
			y.until(y + height).forEach y@ { y1 ->
				if (safe && y1 > this.height)
					return@y
				cells[x1][y1].backColor = backColor
			}
		}
	}

	//BLIT
	/** Blit's (<a href="https://en.wikipedia.org/wiki/Bit_blit">Bit Blit - Wikipedia</a>) this console to the given console at the destination's destX and destY coords
	 * Equivalent to console.blit(0, 0, console.width, console.height, destConsole,destX,destY)
	 * @param dest The destination console to receive the blit
	 * @param destX The X coord of the destination where the blit will begin
	 * @param destY The Y coord of the destination where the blit will begin
	 */
	fun blit(dest: Console, destX: Int, destY: Int) = blit(0, 0, width, height, dest, destX, destY)

	/**
	 * Blit's (<a href="https://en.wikipedia.org/wiki/Bit_blit">Bit Blit - Wikipedia</a>) this console to the given console beginning from the Source's X,Y coords for width,height to the dest's X,Y Coords
	 * @param srcX The X coord of where the blit will begin from this console
	 * @param srcY The Y coord of where the blit will begin from this console
	 * @param width The number of cells to be copied in the X direction
	 * @param height The number of cells to be copied in the Y direction
	 * @param dest The destination console to receive the blit
	 * @param destX The X coord of the destination where the blit will begin
	 * @param destY The Y coord of the destination where the blit will begin
	 */
	fun blit(srcX: Int, srcY: Int, width: Int, height: Int, dest: Console, destX: Int, destY: Int) {
		widthRange.forEach { x ->
			heightRange.forEach { y ->
				val dx = x + destX
				val dy = y + destY
				val sx = x + srcX
				val sy = y + srcY
				if (!safe || (dx >= 0 && dy >= 0 && dx < dest.width && dy < dest.height && sx >= 0 && sy >= 0 && sx < width && sy < height))
					dest.cells[dx][dy] = cells[sx][sy]
			}
		}
	}
}