using System;
using System.Collections.ObjectModel;
using System.Linq;
using UnityEngine;
namespace DTT.Utils.Workflow
{
///
/// A grid representation of a data array.
///
public class GridBase
{
///
/// The minimum amount of rows or columns used for the grid.
///
public const int MIN_ROW_OR_COLUMN_SIZE = 2;
///
/// The amount of columns used in the grid.
///
public int Columns
{
get => _columns;
set
{
if (value < MIN_ROW_OR_COLUMN_SIZE)
throw new ArgumentOutOfRangeException(nameof(value));
_columns = value;
Resize();
}
}
///
/// The amount of used in the grid.
///
public int Rows
{
get => _rows;
set
{
if (value < MIN_ROW_OR_COLUMN_SIZE)
throw new ArgumentOutOfRangeException(nameof(value));
_rows = value;
Resize();
}
}
///
/// Returns a value from the grid.
///
/// The x coordinate of the value.
/// The y coordinate of the value.
public T this[int x, int y] => GetValue(x, y);
///
/// Returns a value from the grid.
///
/// The coordinates of the value.
public T this[Vector2Int coordinates] => GetValue(coordinates.x, coordinates.y);
///
/// The collection of values used by the grid.
///
public ReadOnlyCollection Values => Array.AsReadOnly(_values);
///
/// The array values used by the grid.
///
private T[] _values;
///
/// The amount of columns used in the grid.
///
private int _columns;
///
/// The amount of rows used in the grid.
///
private int _rows;
///
/// Creates a new default grid with a minimum size.
///
public GridBase()
{
_columns = MIN_ROW_OR_COLUMN_SIZE;
_rows = MIN_ROW_OR_COLUMN_SIZE;
Resize();
}
///
/// Creates a new grid with a given amount of columns and rows.
///
/// The amount of columns used in the grid.
/// The amount of rows used in the grid.
public GridBase(int columns, int rows)
{
if (columns < MIN_ROW_OR_COLUMN_SIZE)
throw new ArgumentOutOfRangeException(nameof(columns));
if (rows < MIN_ROW_OR_COLUMN_SIZE)
throw new ArgumentOutOfRangeException(nameof(rows));
_rows = rows;
_columns = columns;
Resize();
}
///
/// Creates a new grid with a given amount of columns and rows.
///
/// The amount of columns used in the grid.
/// The amount of rows used in the grid.
/// The values used by the grid.
public GridBase(int columns, int rows, T[] values) : this(columns, rows)
{
_columns = columns;
_rows = rows;
Resize();
Populate(values);
}
///
/// Creates a new grid with a given amount of values.
///
/// The values to initialize the grid with.
public GridBase(T[,] values)
{
_columns = values.GetLength(0);
_rows = values.GetLength(1);
Populate(values);
}
///
/// Populates the grid with values to use.
///
/// The values to be used by the grid.
public void Populate(T[] values) => values.CopyTo(_values, 0);
///
/// Populates the grid with values to use.
///
///
/// Whether to force a resize of the grid if the given size is bigger.
public void Populate(T[,] values, bool forceResize = true)
{
T[] flattened = values.Cast().ToArray();
int columns = values.GetLength(0);
int rows = values.GetLength(1);
if(forceResize)
Resize(columns, rows);
Populate(flattened);
}
///
/// Returns a value from the grid.
///
/// The coordinates of the value.
/// The value from the grid.
public T GetValue(Vector2Int coordinates) => GetValue(coordinates.x, coordinates.y);
///
/// Returns a value from the grid.
///
/// The x coordinate of the value.
/// The y coordinate of the value.
/// The value from the grid.
public T GetValue(int x, int y) => IsInvalid(x, y) ? default : _values[GetIndex(x, y)];
///
/// Swaps values of two different grid coordinates. Uses the 'GetValue' method to
/// swap the values which means default values will be used if coordinates
/// are out of bounds.
///
/// The first grid coordinate.
/// The second grid coordinate.
public void SwapValues(Vector2Int coordinates1, Vector2Int coordinates2) =>
SwapValues(coordinates1.x, coordinates1.y, coordinates2.x, coordinates2.y);
///
/// Swaps values of two different grid coordinates. Uses the 'GetValue' method to
/// swap the values which means default values will be used if coordinates
/// are out of bounds.
///
/// The x coordinate of the first value
/// The y coordinate of the first value
/// The x coordinate of the second value
/// The y coordinate of the second value
public void SwapValues(int x1, int y1, int x2, int y2)
{
T temp = GetValue(x1, y1);
SetValue(x1, y1, GetValue(x2, y2));
SetValue(x2, y2, temp);
}
///
/// Sets the value at a given grid coordinate.
///
/// The coordinates at which to set the value.
/// The new value.
public void SetValue(Vector2Int coordinates, T newValue) => SetValue(coordinates.x, coordinates.y, newValue);
///
/// Sets the value at a given grid coordinate.
///
/// The x coordinate.
/// The y coordinate.
/// The new value to assign.
public void SetValue(int x, int y, T newValue)
{
if (IsInvalid(x, y))
return;
_values[GetIndex(x, y)] = newValue;
}
///
/// Returns the array index based on given coordinates.
///
/// The x coordinate.
/// The y coordinate.
/// The array index.
public int GetIndex(int x, int y) => (Columns * y) + x;
///
/// Returns whether given coordinates lie within the grid.
///
/// The x coordinate.
/// The y coordinate.
/// Whether given coordinates lie within the grid.
public bool IsInvalid(int x, int y) => (x < 0 || x >= Columns) || (y < 0 || y >= Rows);
///
/// Resizes the grid using given column and row values.
///
/// The amount of columns used in the grid.
/// The amount of rows used in the grid.
public void Resize(int columns, int rows)
{
_columns = columns;
_rows = rows;
Resize();
}
///
/// Resizes the grid using stored column and row values.
///
private void Resize()
{
int size = _columns * _rows;
if (_values == null)
{
_values = new T[_columns * _rows];
}
else
{
Array.Resize(ref _values, size);
}
}
}
}