/*
 *  ratlibsession.c
 *  RatLib
 *
 *  Created by Curtis Jones on 2009.11.13.
 *  Copyright 2009 __MyCompanyName__. All rights reserved.
 *
 */

#include "ratlibsession.h"
#include "logger.h"
#include "ratlib.h"

/**
 *
 *
 */
int
ratlibsession_init (ratlibsession_t *session, opool_t *pool, ratlibimage_t *image)
{
	int error;
	
	if (unlikely(session == NULL))
		LOG_ERROR_AND_RETURN(-1, "%s.. null ratlibsession_t\n", __PRETTY_FUNCTION__);
	
	if (unlikely(image == NULL))
		LOG_ERROR_AND_RETURN(-2, "%s.. null ratlibimage_t\n", __PRETTY_FUNCTION__);
	
	if (unlikely(0 != (error = cobject_init(&session->cobject, (cobject_destroy_func)ratlibsession_destroy, pool))))
		LOG_ERROR_AND_RETURN(-101, "%s.. failed to cobject_init, 0x%08X [%d]\n", __PRETTY_FUNCTION__, error, error);
	
	if (unlikely(0 != (error = ratlibgrid_init(&session->grid, NULL, image->width, image->height))))
		LOG_ERROR_AND_RETURN(-102, "%s.. failed to ratlibgrid_init, 0x%08X [%d]\n", __PRETTY_FUNCTION__, error, error);
	
	session->image = ratlibimage_retain(image);
	
	return 0;
}

/**
 *
 *
 */
int
ratlibsession_destroy (ratlibsession_t *session)
{
	int error;
	
	if (unlikely(session == NULL))
		LOG_ERROR_AND_RETURN(-1, "%s.. null ratlibsession_t\n", __PRETTY_FUNCTION__);
	
	if (session->image != NULL) {
		ratlibimage_release(session->image);
		session->image = NULL;
	}
	
	if (session->begpoint != NULL) {
		ratlibpoint_release(session->begpoint);
		session->begpoint = NULL;
	}
	
	if (session->endpoint != NULL) {
		ratlibpoint_release(session->endpoint);
		session->endpoint = NULL;
	}
	
	if (session->solution != NULL) {
		ratlibpoint_release(session->solution);
		session->solution = NULL;
	}
	
	if (unlikely(0 != (error = cobject_destroy(&session->cobject))))
		LOG_ERROR_AND_RETURN(-101, "%s.. failed to cobject_destroy, 0x%08X [%d]\n", __PRETTY_FUNCTION__, error, error);
	
	return 0;
}

/**
 *
 *
 */
int
ratlibsession_setbeg (ratlibsession_t *session, ratlibpoint_t *point)
{
	if (unlikely(session == NULL))
		LOG_ERROR_AND_RETURN(-1, "%s.. null ratlibsession_t\n", __PRETTY_FUNCTION__);
	
	if (session->begpoint != NULL)
		ratlibpoint_release(session->begpoint);
	
	session->begpoint = ratlibpoint_retain(point);
	
	return 0;
}

/**
 *
 *
 */
int
ratlibsession_setend (ratlibsession_t *session, ratlibpoint_t *point)
{
	if (unlikely(session == NULL))
		LOG_ERROR_AND_RETURN(-1, "%s.. null ratlibsession_t\n", __PRETTY_FUNCTION__);
	
	if (session->endpoint != NULL)
		ratlibpoint_release(session->endpoint);
	
	session->endpoint = ratlibpoint_retain(point);
	
	return 0;
}

/**
 *
 *
 */
inline int
ratlibsession_setsolution (ratlibsession_t *session, ratlibpoint_t *point)
{
	if (unlikely(session == NULL))
		LOG_ERROR_AND_RETURN(-1, "%s.. null ratlibsession_t\n", __PRETTY_FUNCTION__);
	
	if (unlikely(point == NULL))
		LOG_ERROR_AND_RETURN(-2, "%s.. null ratlibpoint_t\n", __PRETTY_FUNCTION__);
	
	if (session->solution != NULL)
		ratlibpoint_release(session->solution);
	
	session->solution = ratlibpoint_retain(point);
	
	return 0;
}

/**
 *
 *
 */
int
ratlibsession_solve (ratlibsession_t *session)
{
	int error;
	
	if (unlikely(session == NULL))
		LOG_ERROR_AND_RETURN(-1, "%s.. null ratlibsession_t\n", __PRETTY_FUNCTION__);
	
	ratlibgrid_t *grid = &session->grid;
	ratlibimage_t *image = session->image;
	ratlibpoint_t *solution = NULL;
	uint32_t set1 = RATLIB_GRID_SET1;
	uint32_t set2 = RATLIB_GRID_SET2;
	uint32_t tmpset, newset = RATLIB_GRID_SET3;
	pixel_t *beg = ratlibimage_pixelatpoint(session->image, session->begpoint);
	pixel_t *end = ratlibimage_pixelatpoint(session->image, session->endpoint);
	
	if (beg == NULL || end == NULL)
		LOG_ERROR_AND_GOTO(-101, done, "%s.. no begin or end point defined.\n", __PRETTY_FUNCTION__);
	
	if (!pixel_similar(beg,end))
		LOG_ERROR_AND_GOTO(-102, done, "%s.. begin point and end point colors do not match.\n", __PRETTY_FUNCTION__);
	
	// the initial set contains only the starting point
	ratlibgrid_set_mark(grid, set1, session->begpoint);
	
	while (1) {
		ratlibpoint_t *point1, *point2;
		
		if (0 == ratlibgrid_set_count(grid,set1))
			LOG_ERROR_AND_BREAK("%s.. something is broken!\n", __PRETTY_FUNCTION__);
		
		// for each leading-edge point...
		while (0 == ratlibgrid_set_any(grid, set1, &point1) && point1 != NULL) {
			// if this leading-edge point is the same as the end point, we're done
			if (ratlibpoint_equals(point1, session->endpoint)) {
				solution = ratlibpoint_retain(point1);
				ratlibpoint_release(point1);
				goto found;
			}
			
			// get the set of all adjacent points that we haven't "visited" yet
			ratlibgrid_set_aups(grid, point1, RATLIB_GRID_VISITED, set2);
			
			// for each adjacent point to the leading-edge point...
			while (0 == ratlibgrid_set_any(grid, set2, &point2) && point2 != NULL) {
				if (!pixel_similar( ratlibimage_pixelatpoint(image, point2), beg )) {
					ratlibpoint_release(point2);
					continue;
				}
				
				ratlibpoint_setprev(point2, point1);
				
				ratlibgrid_markasvisited(grid, point2);
				ratlibgrid_set_mark(grid, newset, point2);
				ratlibpoint_release(point2);
			}
			
			ratlibpoint_release(point1);
		}
		
		tmpset = set1;
		set1 = newset;
		newset = tmpset;
		ratlibgrid_set_clear(grid, newset);
	}
	
found:
	if (solution != NULL) {
		LOG3("%s.. found solution!\n", __PRETTY_FUNCTION__);
		ratlibsession_setsolution(session, solution);
	}
	
done:
	ratlibgrid_set_clear(grid, set1);
	ratlibgrid_set_clear(grid, set2);
	ratlibgrid_set_clear(grid, newset);
	
	if (solution != NULL)
		ratlibpoint_release(solution);
	
	ratlib_memstats();
	
	return 0;
}

/**
 *
 *
 */
inline ratlibsession_t*
ratlibsession_retain (ratlibsession_t *session)
{
	if (unlikely(session == NULL))
		LOG_ERROR_AND_RETURN(NULL, "%s.. null ratlibsession_t\n", __PRETTY_FUNCTION__);
	
	return (ratlibsession_t*)cobject_retain(&session->cobject);
}

/**
 *
 *
 */
inline void
ratlibsession_release (ratlibsession_t *session)
{
	if (unlikely(session == NULL))
		LOG_ERROR_AND_RETURN(, "%s.. null ratlibsession_t\n", __PRETTY_FUNCTION__);
	
	cobject_release(&session->cobject);
}
