/* floodfill.c
 * This file is part of Amsam photo cropping program
 * Copyright 2021, 2023, 2024, 2025, 2026 Nandakumar Edamana
 * Distributed under GNU General Public License, version 3
 */ 

#include <assert.h>
#include <stdarg.h>
#include <stdbool.h>

#include "floodfill.h"


int point_stack_get_count(PointStack *this)
{
	return this->count;
}

_Bool point_stack_is_empty(PointStack *this)
{
	return 0 == this->count;
}

void point_stack_push(PointStack *this, Point newitem)
{
	point_stack__resize(this, this->count + 1);
	this->arr[this->count - 1] = newitem;
}

void point_stack_push_multiple(PointStack *this, int n, Point *newitems)
{
	int newcount = this->count + n;
	if (this->alloccount < newcount) {
		this->arr = (Point *) realloc(this->arr, newcount * sizeof(this->arr[1u]));
		if (this->arr == NULL) {
			perror(NULL);
			exit(EXIT_FAILURE);
		}

		this->alloccount = newcount;
	}

	for (int i = 0; i < n; i += 1) {
		this->arr[this->count + i] = newitems[i];
	}

	this->count = newcount;
}

Point point_stack_peek(PointStack *this)
{
	assert(this->count > 0);
	return this->arr[this->count - 1];
}

Point point_stack_pop(PointStack *this)
{
	assert(this->count > 0);

	Point retitem = this->arr[this->count - 1];
	point_stack__resize(this, this->count - 1);

	return retitem;
}

Point point_stack_pop_bottom(PointStack *this)
{
	assert(this->count > 0);

	Point retval = this->arr[0];

	this->count -= 1;
	memmove(this->arr, this->arr + 1, this->count * sizeof(this->arr[0]));

	return retval;
}

void point_stack_replace_top(PointStack *this, Point newitem)
{
	assert(this->count > 0);

	this->arr[this->count - 1] = newitem;
}

void point_stack__resize(PointStack *this, int newcount)
{
	if (newcount > this->alloccount) {
		int alloccountbak = this->alloccount;

		this->alloccount = 2 * this->alloccount;

		assert(this->alloccount > alloccountbak);

		assert(newcount <= this->alloccount);
		this->arr = (Point *) realloc(this->arr, this->alloccount * sizeof(this->arr[1u]));
		if (this->arr == NULL) {
			perror(NULL);
			exit(EXIT_FAILURE);
		}
	}

	this->count = newcount;
}

void point_stack_destruct(PointStack *this)
{
	if (this->arr) {
		free(this->arr);
	}
}

void point_stack_construct(PointStack *this)
{
	this->alloccount = 8;
	Point *_tmp_1 = (Point *) calloc((size_t) 8, sizeof(Point));
	if (_tmp_1 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	this->arr = _tmp_1;
	this->count = 0;
}

VaraRect nan_img_floodfill(Image *img, int x, int y, DrawingParams dp)
{
	PointStack *ptstack = (PointStack *) malloc(sizeof(PointStack));
	if (ptstack == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	point_stack_construct(ptstack);

	LinearPixel fillpxl = dp.fgcolor;
	LinearPixel ifpxl = nan_img_get_linear_pixel(img, x, y);

	if (linear_pixels_match(ifpxl, fillpxl)) {
		if (ptstack) {
			point_stack_destruct(ptstack);
			free(ptstack);
		}

		return (VaraRect) { 0, 0, 0, 0 };
	}

	floodfill_add_point_to_stack_if_match(img, ptstack, (Point) { x, y }, ifpxl);

	int xmin = x;
	int xmax = x;
	int ymin = y;
	int ymax = y;

	while (!point_stack_is_empty(ptstack)) {
		Point point = point_stack_pop(ptstack);

		nan_img_set_linear_pixel(img, point.x, point.y, fillpxl);

		if (point.x < xmin) {
			xmin = point.x;
		} else if (point.x > xmax) {
			xmax = point.x;
		}

		if (point.y < ymin) {
			ymin = point.y;
		} else if (point.y > ymax) {
			ymax = point.y;
		}

		if (point.x > 0) {
			floodfill_add_point_to_stack_if_match(img, ptstack, (Point) { point.x - 1, point.y }, ifpxl);
		}

		if (point.x < img->width - 1) {
			floodfill_add_point_to_stack_if_match(img, ptstack, (Point) { 1 + point.x, point.y }, ifpxl);
		}

		if (point.y > 0) {
			floodfill_add_point_to_stack_if_match(img, ptstack, (Point) { point.x, point.y - 1 }, ifpxl);
		}

		if (point.y < img->height - 1) {
			floodfill_add_point_to_stack_if_match(img, ptstack, (Point) { point.x, 1 + point.y }, ifpxl);
		}
	}

	if (ptstack) {
		point_stack_destruct(ptstack);
		free(ptstack);
	}

	return rectangle_new_from_boundaries(xmin, ymin, xmax, ymax);
}

void floodfill_add_point_to_stack_if_match(Image *img, PointStack *ptstack, Point point, LinearPixel ifpxl)
{
	if (!linear_pixels_match(ifpxl, nan_img_get_linear_pixel(img, point.x, point.y))) {
		return;
	}

	point_stack_push(ptstack, point);
}

Point point_default()
{
	Point s;
	s.x = 0;
	s.y = 0;
	return s;
}
