/*  CAO Compiler
    Copyright (C) 2014 Cryptography and Information Security Group, HASLab - INESC TEC and Universidade do Minho

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "CAO_struct.h"

CAO_struct_s *newStruct(int size)
{
	CAO_struct_s *newS;
	newS = (CAO_struct_s *) malloc(sizeof(CAO_struct_s));
	newS->size = size;
	newS->types = (char *)malloc(size * sizeof(char));
	newS->fields = (CAO_REF *) malloc(size * sizeof(CAO_REF));
	return newS;
}

CAO_RES CAO_struct_decl(CAO_struct * n, int size, const char type[],
						void *indices[])
{
	int jump;
	return _CAO_struct_decl(n, size, type, indices, &jump);
}

CAO_RES _CAO_struct_decl(CAO_struct * n, int size, const char type[],
						 void *indices[], int *jump)
{
	int i, offset = 0;
	CAO_RES res = CAO_OK;
	CAO_struct_s *_s = newStruct(size);

	for (i = 0; ((i < size) && (res == CAO_OK)); i++)
	{
		_s->types[i] = type[offset];
		res =
			_CAO_global_decl(&(_s->fields[i]), type + offset, indices + offset,
							 jump);
		offset += (*jump);
	}
	*n = _s;
	*jump = offset;

	return res;
}

CAO_RES CAO_struct_dispose(CAO_struct s)
{
	int i;
	CAO_RES res = CAO_OK;
	CAO_struct_s *_s = (CAO_struct_s *) s;

	for (i = 0; ((i < _s->size) && (res == CAO_OK)); i++)
		res = CAO_global_dispose(_s->fields[i], _s->types[i]);
	free(_s->types);
	free(_s->fields);
	free(_s);
	return res;
}

CAO_RES CAO_struct_const_init(CAO_struct s, void *value)
{
	int i;
	CAO_struct_s *_s = (CAO_struct_s *) s;

	for (i = 0; i < _s->size; i++)
		CAO_global_const_init(_s->fields[i], value, _s->types[i]);
	return CAO_OK;
}

CAO_RES CAO_struct_init(CAO_struct s, void *value[])
{
	int jval = 0;
	return _CAO_struct_init(s, value, &jval);
}

CAO_RES _CAO_struct_init(CAO_struct s, void *value[], int *jval)
{
	// jval é parâmetro de saída
	int i, offValue = 0;
	CAO_struct_s *_s = (CAO_struct_s *) s;

	for (i = 0; i < _s->size; i++)
	{
		_CAO_global_init(_s->fields[i], value + offValue, jval, _s->types[i]);
		offValue += *jval;
	}
	*jval = offValue;
	return CAO_OK;
}

CAO_RES CAO_struct_assign(CAO_struct r, CAO_struct s)
{

	CAO_struct_s *_r = (CAO_struct_s *) r;
	CAO_struct_s *_s = (CAO_struct_s *) s;

	int i;

	for (i = 0; i < _r->size; i++)
	{
		CAO_global_assign(_r->fields[i], _s->fields[i], _r->types[i]);
	}
	return CAO_OK;
}

CAO_RES CAO_struct_clone(CAO_struct * r, CAO_struct s)
{

	CAO_struct_s *_s = (CAO_struct_s *) s;
	CAO_struct_s *_r = newStruct(_s->size);

	int i;

	for (i = 0; (i < _s->size); i++)
	{
		_r->types[i] = _s->types[i];
		CAO_global_clone(&(_r->fields[i]), _s->fields[i], _s->types[i]);
	}
	*r = _r;
	return CAO_OK;
}

CAO_bool _CAO_struct_equal(CAO_struct si, CAO_struct sj)
{
	CAO_struct_s *_si = (CAO_struct_s *) si;
	CAO_struct_s *_sj = (CAO_struct_s *) sj;

	int i = 0, size = _si->size;
	CAO_bool r = true;
	while (r && (i < size))
	{
		r = _CAO_global_equal(_si->fields[i], _sj->fields[i], _si->types[i]);
		i++;
	}
	return r;
}

CAO_RES CAO_struct_select(CAO_REF r, CAO_struct s, CAO_rint i)
{
	CAO_struct_s *_s = (CAO_struct_s *) s;

	CAO_global_assign(r, _s->fields[i], _s->types[i]);
	return CAO_OK;
}

CAO_REF CAO_struct_ref(CAO_struct s, CAO_rint i)
{
	char type;
	return _CAO_struct_ref(s, i, &type);
}

CAO_REF _CAO_struct_ref(CAO_struct s, CAO_rint i, char *t)
{
	CAO_struct_s *_s = (CAO_struct_s *) s;
	*t = _s->types[i];
	return (_s->fields[i]);
}

CAO_RES CAO_struct_dump(CAO_struct s)
{
	CAO_struct_s *_s = (CAO_struct_s *) s;

	int f = (_s->size), i;

	std::cout << "struct with" << f << "fields \n";
	for (i = 0; (i < f); i++)
	{
		CAO_global_dump(_s->fields[i], _s->types[i]);
		std::cout << "\n";
	}
	std::cout << "end of struct with" << f << "fields \n";
	return CAO_OK;
}
