/*  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_vector.h"

CAO_vector_s *newVector(int size, char type)
{
	CAO_vector_s *newV = (CAO_vector_s *) malloc(sizeof(CAO_vector_s));

	newV->size = size;
	newV->type = type;
	newV->value = (CAO_REF *) malloc(size * sizeof(CAO_REF));

	return newV;
}

CAO_RES CAO_vector_decl(CAO_vector * n, int size, const char type[],
						void *indices[])
{
	int jump;
	return _CAO_vector_decl(n, size, type, indices, &jump);
}

CAO_RES _CAO_vector_decl(CAO_vector * n, int size, const char type[],
						 void *indices[], int *jump)
{

	int i;
	CAO_RES res = CAO_OK;
	CAO_vector_s *_v = newVector(size, type[0]);

	for (i = 0; ((i < size) && (res == CAO_OK)); i++)
		res = _CAO_global_decl(&(_v->value[i]), type, indices, jump);

	*n = _v;

	return res;
}

CAO_RES CAO_vector_dispose(CAO_vector v)
{
	int i;
	CAO_RES res = CAO_OK;
	CAO_vector_s *_v = (CAO_vector_s *) v;

	for (i = 0; ((i < _v->size) && (res == CAO_OK)); i++)
		res = CAO_global_dispose(_v->value[i], _v->type);

	free(_v->value);
	free(_v);

	return res;
}

CAO_RES CAO_vector_const_init(CAO_vector v, void *value)
{
	int i;
	CAO_vector_s *_v = (CAO_vector_s *) v;

	for (i = 0; (i < _v->size); i++)
		CAO_global_const_init(_v->value[i], value, _v->type);

	return CAO_OK;
}

CAO_RES CAO_vector_init(CAO_vector v, void *value[])
{
	int jval = 0;
	return _CAO_vector_init(v, value, &jval);
}

CAO_RES _CAO_vector_init(CAO_vector v, void *value[], int *jval)
{
	// jval é parâmetro de output
	int i, offset = 0;
	CAO_vector_s *_v = (CAO_vector_s *) v;

	for (i = 0; (i < _v->size); i++)
	{
		_CAO_global_init(_v->value[i], value + offset, jval, _v->type);
		offset += *jval;
	}
	*jval = offset;
	return CAO_OK;
}

CAO_RES CAO_vector_assign(CAO_vector r, CAO_vector v)
{

	CAO_vector_s *_r = (CAO_vector_s *) r;
	CAO_vector_s *_v = (CAO_vector_s *) v;

	int i, s;

	if ((_r->size == _v->size) && (_r->type == _v->type))
		for (s = _r->size, i = 0; (i < s); i++)
			CAO_global_assign(_r->value[i], _v->value[i], _r->type);
	else
		return CAO_ERR;

	return CAO_OK;
}

CAO_RES CAO_vector_clone(CAO_vector * r, CAO_vector v)
{
	CAO_vector_s *_v = (CAO_vector_s *) v;
	CAO_vector_s *_r = newVector(_v->size, _v->type);

	int i;
	for (i = 0; i < _v->size; i++)
		CAO_global_clone(&(_r->value[i]), _v->value[i], _v->type);
	*r = _r;
	return CAO_OK;
}

CAO_bool _CAO_vector_equal(CAO_vector vi, CAO_vector vj)
{
	CAO_vector_s *_i = (CAO_vector_s *) vi;
	CAO_vector_s *_j = (CAO_vector_s *) vj;

	int i = 0, s = _i->size;
	CAO_bool r = true;
	while (r && (i < s))
	{
		r = _CAO_global_equal(_i->value[i], _j->value[i], _i->type);
		i++;
	}

	return r;
}

CAO_RES CAO_vector_rot_up(CAO_vector r, CAO_vector v, CAO_rint n)
{
	// o que é suposto acontecer se r == v?
	CAO_vector_s *_r = (CAO_vector_s *) r;
	CAO_vector_s *_v = (CAO_vector_s *) v;
	int i, j, s = _r->size;

	i = 0;
	j = n;
	while (i < s)
	{
		j = j % s;
		CAO_global_assign(_r->value[j++], _v->value[i++], _r->type);
	}
	return CAO_OK;
}

CAO_RES CAO_vector_rot_down(CAO_vector r, CAO_vector v, CAO_rint n)
{
	CAO_vector_s *_r = (CAO_vector_s *) r;
	CAO_vector_s *_v = (CAO_vector_s *) v;
	int i, j, s = _r->size;

	i = 0;
	j = n;
	while (i < s)
	{
		j = j % s;
		CAO_global_assign(_r->value[i++], _v->value[j++], _r->type);
	}
	return CAO_OK;
}

CAO_RES CAO_vector_select(CAO_REF r, CAO_vector v, CAO_rint i)
{
	CAO_vector_s *_v = (CAO_vector_s *) v;

	if ((i < _v->size) && (i >= 0))
	{
		CAO_global_assign(r, _v->value[i], _v->type);
	}
	else
		return CAO_ERR;
	return CAO_OK;
}

CAO_REF CAO_vector_ref(CAO_vector v, CAO_rint i)
{
	char type;
	return _CAO_vector_ref(v, i, &type);
}

CAO_REF _CAO_vector_ref(CAO_vector v, CAO_rint i, char *t)
{
	CAO_vector_s *_v = (CAO_vector_s *) v;
	if ((i < _v->size) && (i >= 0))
	{
		*t = _v->type;
	}
	else
	{
		return NULL;
	}
	return (_v->value[i]);
}

CAO_RES CAO_vector_range_select(CAO_vector r, CAO_vector v, CAO_rint i,
								CAO_rint j)
{
	CAO_vector_s *_r = (CAO_vector_s *) r;
	CAO_vector_s *_v = (CAO_vector_s *) v;
	int k, size;

	size = j - i + 1;
	k = 0;
	while (k < size)
		CAO_global_assign(_r->value[k++], _v->value[i++], _r->type);
	return CAO_OK;
}

CAO_RES CAO_vector_range_set(CAO_vector r, CAO_vector v, CAO_rint i, CAO_rint j)
{
	CAO_vector_s *_r = (CAO_vector_s *) r;
	CAO_vector_s *_v = (CAO_vector_s *) v;
	int k, size;

	size = j - i + 1;
	k = 0;
	while (k < size)
		CAO_global_assign(_r->value[i++], _v->value[k++], _r->type);
	return CAO_OK;
}

CAO_RES CAO_vector_concat(CAO_vector r, CAO_vector a, CAO_vector b)
{
	CAO_vector_s *_r = (CAO_vector_s *) r;
	CAO_vector_s *_a = (CAO_vector_s *) a;
	CAO_vector_s *_b = (CAO_vector_s *) b;

	int _sa = _a->size;
	int _sb = _b->size;

	int i, j;

	i = 0;
	j = 0;
	while (i < _sa)
		CAO_global_assign(_r->value[j++], _a->value[i++], _r->type);
	i = 0;
	while (i < _sb)
		CAO_global_assign(_r->value[j++], _b->value[i++], _r->type);
	return CAO_OK;
}

CAO_RES CAO_vector_dump(CAO_vector v)
{
	CAO_vector_s *_v = (CAO_vector_s *) v;

	int s = (_v->size), i;

	std::cout << "vector[" << s << "] = \n";
	for (i = 0; (i < s); i++)
	{
		CAO_global_dump(_v->value[i], _v->type);
		std::cout << " , ";
	}
	std::cout << "\n end of vector[" << s << "] = \n";
	return CAO_OK;
}

CAO_RES CAO_vector_cast_vector(CAO_vector d, CAO_vector s)
{
	CAO_vector_s *_s = (CAO_vector_s *) d, *_d = (CAO_vector_s *) d;
	int i;
	CAO_RES res = CAO_OK;

	for (i = 0; ((res == CAO_OK) && (i < _s->size)); i++)
		res = CAO_global_cast(_d->value[i], _d->type, _s->value[i], _s->type);

	return res;
}

CAO_RES CAO_vector_cast_matrix(CAO_matrix m, CAO_vector v)
{
	CAO_vector_s *_v = (CAO_vector_s *) v;
	CAO_matrix_s *_m = (CAO_matrix_s *) m;
	int i;
	CAO_RES res = CAO_OK;

	for (i = 0; ((res == CAO_OK) && (i < _v->size)); i++)
		res = CAO_global_cast(_m->value[i], _m->type, _v->value[i], _v->type);
	return res;
}

CAO_RES CAO_matrix_cast_vector(CAO_vector v, CAO_matrix m)
{
	CAO_vector_s *_v = (CAO_vector_s *) v;
	CAO_matrix_s *_m = (CAO_matrix_s *) m;
	int i;
	CAO_RES res = CAO_OK;
	for (i = 0; ((res == CAO_OK) && (i < _v->size)); i++)
		res = CAO_global_cast(_v->value[i], _v->type, _m->value[i], _m->type);

	return res;
}
