/*
 * Mesa 3-D graphics library
 * Version:  6.3
 *
 * Copyright (C) 2005  Brian Paul   All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/**
 * \file slang_storage.c
 * slang variable storage
 * \author Michal Krol
 */

#include "imports.h"
#include "slang_utility.h"
#include "slang_storage.h"
#include "slang_assemble.h"

/* slang_storage_array */

void slang_storage_array_construct (slang_storage_array *arr)
{
	arr->type = slang_stor_aggregate;
	arr->aggregate = NULL;
	arr->length = 0;
}

void slang_storage_array_destruct (slang_storage_array *arr)
{
	if (arr->aggregate != NULL)
	{
		slang_storage_aggregate_destruct (arr->aggregate);
		slang_alloc_free (arr->aggregate);
	}
}

/* slang_storage_aggregate */

void slang_storage_aggregate_construct (slang_storage_aggregate *agg)
{
	agg->arrays = NULL;
	agg->count = 0;
}

void slang_storage_aggregate_destruct (slang_storage_aggregate *agg)
{
	unsigned int i;
	for (i = 0; i < agg->count; i++)
		slang_storage_array_destruct (agg->arrays + i);
	slang_alloc_free (agg->arrays);
}

static slang_storage_array *slang_storage_aggregate_push_new (slang_storage_aggregate *agg)
{
	slang_storage_array *arr = NULL;
	agg->arrays = (slang_storage_array *) slang_alloc_realloc (agg->arrays, agg->count * sizeof (
		slang_storage_array), (agg->count + 1) * sizeof (slang_storage_array));
	if (agg->arrays != NULL)
	{
		arr = agg->arrays + agg->count;
		slang_storage_array_construct (arr);
		agg->count++;
	}
	return arr;
}

/* _slang_aggregate_variable() */

static int aggregate_vector (slang_storage_aggregate *agg, slang_storage_type basic_type,
	unsigned int row_count)
{
	slang_storage_array *arr = slang_storage_aggregate_push_new (agg);
	if (arr == NULL)
		return 0;
	arr->type = basic_type;
	arr->length = row_count;
	return 1;
}

static int aggregate_matrix (slang_storage_aggregate *agg, slang_storage_type basic_type,
	unsigned int dimension)
{
	slang_storage_array *arr = slang_storage_aggregate_push_new (agg);
	if (arr == NULL)
		return 0;
	arr->type = slang_stor_aggregate;
	arr->length = dimension;
	arr->aggregate = (slang_storage_aggregate *) slang_alloc_malloc (sizeof (
		slang_storage_aggregate));
	if (arr->aggregate == NULL)
		return 0;
	slang_storage_aggregate_construct (arr->aggregate);
	if (!aggregate_vector (arr->aggregate, basic_type, dimension))
		return 0;
	return 1;
}

static int aggregate_variables (slang_storage_aggregate *agg, const slang_variable_scope *vars,
	slang_function_scope *funcs, slang_struct_scope *structs)
{
	unsigned int i;
	for (i = 0; i < vars->num_variables; i++)
		if (!_slang_aggregate_variable (agg, &vars->variables[i].type.specifier,
			vars->variables[i].array_size, funcs, structs))
			return 0;
	return 1;
}

int _slang_aggregate_variable (slang_storage_aggregate *agg, slang_type_specifier *spec,
	slang_operation *array_size, slang_function_scope *funcs, slang_struct_scope *structs)
{
	switch (spec->type)
	{
	case slang_spec_bool:
		return aggregate_vector (agg, slang_stor_bool, 1);
	case slang_spec_bvec2:
		return aggregate_vector (agg, slang_stor_bool, 2);
	case slang_spec_bvec3:
		return aggregate_vector (agg, slang_stor_bool, 3);
	case slang_spec_bvec4:
		return aggregate_vector (agg, slang_stor_bool, 4);
	case slang_spec_int:
		return aggregate_vector (agg, slang_stor_int, 1);
	case slang_spec_ivec2:
		return aggregate_vector (agg, slang_stor_int, 2);
	case slang_spec_ivec3:
		return aggregate_vector (agg, slang_stor_int, 3);
	case slang_spec_ivec4:
		return aggregate_vector (agg, slang_stor_int, 4);
	case slang_spec_float:
		return aggregate_vector (agg, slang_stor_float, 1);
	case slang_spec_vec2:
		return aggregate_vector (agg, slang_stor_float, 2);
	case slang_spec_vec3:
		return aggregate_vector (agg, slang_stor_float, 3);
	case slang_spec_vec4:
		return aggregate_vector (agg, slang_stor_float, 4);
	case slang_spec_mat2:
		return aggregate_matrix (agg, slang_stor_float, 2);
	case slang_spec_mat3:
		return aggregate_matrix (agg, slang_stor_float, 3);
	case slang_spec_mat4:
		return aggregate_matrix (agg, slang_stor_float, 4);
	case slang_spec_sampler1D:
	case slang_spec_sampler2D:
	case slang_spec_sampler3D:
	case slang_spec_samplerCube:
	case slang_spec_sampler1DShadow:
	case slang_spec_sampler2DShadow:
		return aggregate_vector (agg, slang_stor_int, 1);
	case slang_spec_struct:
		return aggregate_variables (agg, spec->_struct->fields, funcs, structs);
	case slang_spec_array:
		{
			slang_storage_array *arr;
			slang_assembly_file file;
			slang_assembly_flow_control flow;
			slang_assembly_name_space space;
			slang_assembly_local_info info;
			slang_assembly_stack_info stk;

			arr = slang_storage_aggregate_push_new (agg);
			if (arr == NULL)
				return 0;
			arr->type = slang_stor_aggregate;
			arr->aggregate = (slang_storage_aggregate *) slang_alloc_malloc (sizeof (
				slang_storage_aggregate));
			if (arr->aggregate == NULL)
				return 0;
			slang_storage_aggregate_construct (arr->aggregate);
			if (!_slang_aggregate_variable (arr->aggregate, spec->_array, NULL, funcs, structs))
				return 0;
			slang_assembly_file_construct (&file);
			space.funcs = funcs;
			space.structs = structs;
			/* XXX: vars! */
			space.vars = NULL;
			if (!_slang_assemble_operation (&file, array_size, 0, &flow, &space, &info, &stk))
			{
				slang_assembly_file_destruct (&file);
				return 0;
			}
			/* TODO: evaluate array size */
			slang_assembly_file_destruct (&file);
			arr->length = 256;
		}
		return 1;
	default:
		return 0;
	}
}

/* _slang_sizeof_aggregate() */

unsigned int _slang_sizeof_aggregate (const slang_storage_aggregate *agg)
{
	unsigned int i, size = 0;
	for (i = 0; i < agg->count; i++)
	{
		unsigned int element_size;
		if (agg->arrays[i].type == slang_stor_aggregate)
			element_size = _slang_sizeof_aggregate (agg->arrays[i].aggregate);
		else
			element_size = sizeof (GLfloat);
		size += element_size * agg->arrays[i].length;
	}
	return size;
}

/* _slang_flatten_aggregate () */

int _slang_flatten_aggregate (slang_storage_aggregate *flat, const slang_storage_aggregate *agg)
{
	unsigned int i;
	for (i = 0; i < agg->count; i++)
	{
		unsigned int j;
		for (j = 0; j < agg->arrays[i].length; j++)
		{
			if (agg->arrays[i].type == slang_stor_aggregate)
			{
				if (!_slang_flatten_aggregate (flat, agg->arrays[i].aggregate))
					return 0;
			}
			else
			{
				slang_storage_array *arr;
				arr = slang_storage_aggregate_push_new (flat);
				if (arr == NULL)
					return 0;
				arr->type = agg->arrays[i].type;
				arr->length = 1;
			}
		}
	}
	return 1;
}