module std::core::array;

<*
 A slice2d allows slicing an array like int[10][10] into an arbitrary "int[][]"-like counterpart
 Typically you'd use array::slice2d(...) to create one.
*>
struct Slice2d <Type>
{
	Type* ptr;
	usz inner_len;
	usz ystart;
	usz ylen;
	usz xstart;
	usz xlen;
}

<*
 @return `The length of the "outer" slice`
*>
fn usz Slice2d.len(&self) @operator(len)
{
	return self.ylen;
}

<*
 @return `The total number of elements.`
*>
fn usz Slice2d.count(&self)
{
	return self.ylen * self.xlen;
}

<*
 Step through each element of the slice.
*>
macro void Slice2d.@each(&self; @body(usz[<2>], Type))
{
	foreach (y, line : *self)
	{
		foreach (x, val : line)
		{
			@body({ x, y }, val);
		}
	}
}

<*
 Step through each element of the slice *by reference*
*>
macro void Slice2d.@each_ref(&self; @body(usz[<2>], Type*))
{
	foreach (y, line : *self)
	{
		foreach (x, &val : line)
		{
			@body({ x, y }, val);
		}
	}
}

<*
 Return a row as a slice.

 @param idy : "The row to return"
 @return "The slice for the particular row"
 @require idy >= 0 && idy < self.ylen
*>
macro Type[] Slice2d.get_row(self, usz idy) @operator([])
{
	return (self.ptr + self.inner_len * (idy + self.ystart))[self.xstart:self.xlen];
}

<*
 Get the value at a particular x/y position in the slice.

 @param coord : "The xy coordinate"
 @return "The value at that coordinate"
 @require coord.y >= 0 && coord.y < self.ylen : "y value out of range"
 @require coord.x >= 0 && coord.x < self.xlen : "x value out of range"
*>
macro Type Slice2d.get_coord(self, usz[<2>] coord)
{
	return *self.get_coord_ref(coord);
}

<*
 Get a pointer to the value at a particular x/y position in the slice.

 @param coord : "The xy coordinate"
 @return "A pointer to the value at that coordinate"
 @require coord.y >= 0 && coord.y < self.ylen : "y value out of range"
 @require coord.x >= 0 && coord.x < self.xlen : "x value out of range"
*>
macro Type* Slice2d.get_coord_ref(self, usz[<2>] coord)
{
	return self.get_xy_ref(coord.x, coord.y);
}

<*
 Get the value at a particular x/y position in the slice.

 @param x : "The x coordinate"
 @param y : "The x coordinate"
 @return "The value at that coordinate"
 @require y >= 0 && y < self.ylen : "y value out of range"
 @require x >= 0 && x < self.xlen : "x value out of range"
*>
macro Type Slice2d.get_xy(self, x, y)
{
	return *self.get_xy_ref(x, y);
}

<*
 Get the value at a particular x/y position in the slice by reference.

 @param x : "The x coordinate"
 @param y : "The y coordinate"
 @return "A pointer to the value at that coordinate"
 @require y >= 0 && y < self.ylen : "y value out of range"
 @require x >= 0 && x < self.xlen : "x value out of range"
*>
macro Type* Slice2d.get_xy_ref(self, x, y)
{
	return self.ptr + self.inner_len * (y + self.ystart) + self.xstart + x;
}

<*
 Set the ´value at a particular x/y position in the slice.

 @param coord : "The xy coordinate"
 @param value : "The new value"
 @require coord.y >= 0 && coord.y < self.ylen : "y value out of range"
 @require coord.x >= 0 && coord.x < self.xlen : "x value out of range"
*>
macro void Slice2d.set_coord(self, usz[<2>] coord, Type value)
{
	*self.get_coord_ref(coord) = value;
}

<*
 Set the value at a particular x/y position in the slice.

 @param x : "The x coordinate"
 @param y : "The y coordinate"
 @param value : "The new value"
 @require y >= 0 && y < self.ylen : "y value out of range"
 @require x >= 0 && x < self.xlen : "x value out of range"
*>
macro void Slice2d.set_xy(self, x, y, Type value)
{
	*self.get_xy_ref(x, y) = value;
}

<*
 Reslice a slice2d returning a new slice.

 @param x : "The starting x"
 @param xlen : "The length along x"
 @param y : "The starting y"
 @param ylen : "The length along y"
 @require y >= 0 && y < self.ylen
 @require x >= 0 && x < self.xlen
*>
fn Slice2d Slice2d.slice(&self, isz x = 0, isz xlen = 0, isz y = 0, isz ylen = 0)
{
	if (xlen < 1) xlen = self.xlen + xlen;
	if (ylen < 1) ylen = self.ylen + ylen;
	return { self.ptr, self.inner_len, y + self.ystart, ylen, x + self.xstart, xlen };
}
