/*
 * AffineTransform.cpp
 *
 *  Created on: Nov 10, 2008
 *      Author: alexander
 */

#include "AffineTransform.h"
#include "GBuffer.h"
#include "math.h"

AffineTransform::AffineTransform() {

}

AffineTransform AffineTransform::getRotation(double alpha)
{
	double c = cos(alpha);
	double s = sin(alpha);
	AffineTransform toReturn;

	toReturn.data[0] = c;
	toReturn.data[1] = s;
	toReturn.data[2] = 0;

	toReturn.data[3] = -s;
	toReturn.data[4] = c;
	toReturn.data[5] = 0;

	toReturn.data[6] = 0;
	toReturn.data[7] = 0;
	toReturn.data[8] = 1;

	return toReturn;
}

AffineTransform AffineTransform::getShift(double dx, double dy)
{
	AffineTransform toReturn;

	toReturn.data[0] = 1;
	toReturn.data[1] = 0;
	toReturn.data[2] = dx;

	toReturn.data[3] = 0;
	toReturn.data[4] = 1;
	toReturn.data[5] = dy;

	toReturn.data[6] = 0;
	toReturn.data[7] = 0;
	toReturn.data[8] = 1;

	return toReturn;
}

AffineTransform AffineTransform::getScale(double sx, double sy)
{
	AffineTransform toReturn;

	toReturn.data[0] = sx;
	toReturn.data[1] = 0;
	toReturn.data[2] = 0;

	toReturn.data[3] = 0;
	toReturn.data[4] = sy;
	toReturn.data[5] = 0;

	toReturn.data[6] = 0;
	toReturn.data[7] = 0;
	toReturn.data[8] = 1;

	return toReturn;
}



/* Yea! Guys some cool black c++ magic now! */
AffineTransform AffineTransform::multiplyBy(AffineTransform &other)
{
	AffineTransform toReturn;
	double *z = toReturn.data;
	double *x = data;
	double *y = other.data;

	(*z) = (*x++) * (*y); (*z) += (*x++) * (*(y + 3)); (*z) += (*x) * (*(y + 6)); x -= 2; y++;      z++;
	(*z) = (*x++) * (*y); (*z) += (*x++) * (*(y + 3)); (*z) += (*x) * (*(y + 6)); x -= 2; y++;      z++;
	(*z) = (*x++) * (*y); (*z) += (*x++) * (*(y + 3)); (*z) += (*x) * (*(y + 6)); x++   ; y -= 2;	z++;

	(*z) = (*x++) * (*y); (*z) += (*x++) * (*(y + 3)); (*z) += (*x) * (*(y + 6)); x -= 2; y++;      z++;
	(*z) = (*x++) * (*y); (*z) += (*x++) * (*(y + 3)); (*z) += (*x) * (*(y + 6)); x -= 2; y++;      z++;
	(*z) = (*x++) * (*y); (*z) += (*x++) * (*(y + 3)); (*z) += (*x) * (*(y + 6)); x++ ; y -= 2;     z++;

	(*z) = (*x++) * (*y); (*z) += (*x++) * (*(y + 3)); (*z) += (*x) * (*(y + 6)); x -= 2; y++;      z++;
 	(*z) = (*x++) * (*y); (*z) += (*x++) * (*(y + 3)); (*z) += (*x) * (*(y + 6)); x -= 2; y++;      z++;
	(*z) = (*x++) * (*y); (*z) += (*x++) * (*(y + 3)); (*z) += (*x) * (*(y + 6)); x++ ; y -= 2;     z++;


	return toReturn;
}



// Try bilinear application
GBuffer *AffineTransform::apply (GBuffer *input)
{
	GBuffer *toReturn = new GBuffer(input->h, input->w);
	int h = input->h;
	int w = input->w;
	int i,j;
	double x, y;

	GBuffer::Telement * start = toReturn->data;

	for (i = 0; i < h; i++)
		for (j = 0; j < w; j++)
		{
			x = data[0] * j + data[1] * i + data[2];
			y = data[3] * j + data[4] * i + data[5];

			if (x < 0 || x > w - 1  || y < 0 || y > h - 1)
			{
				start[i * w + j] = 0x80;
				continue;
			}

			int a = input->data[(int)(y) * w  +     (int)(x)];
			int b = input->data[(int)(y) * w  +     (int)(x + 1)];
			int c = input->data[(int)(y + 1) * w  + (int)(x)];
			int d = input->data[(int)(y + 1) * w  + (int)(x + 1)];
			double k1 = x - (int)(x);
			double k2 = y - (int)(y);

			int result = (a * (1 - k1) + k1 * b) * (1 - k2) +
						 (c * (1 - k1) + k1 * d) * k2;
			start[i * w + j] = result;
		}

	return toReturn;
}

GBuffer *AffineTransform::apply (GBuffer *input, int newh, int neww)
{
	GBuffer *toReturn = new GBuffer(newh, neww);
	int h = newh;
	int w = neww;
	int i,j;
	double x, y;

	GBuffer::Telement * start = toReturn->data;

	for (i = 0; i < h; i++)
		for (j = 0; j < w; j++)
		{
			x = data[0] * j + data[1] * i + data[2];
			y = data[3] * j + data[4] * i + data[5];

			if (x < 0 || x > input->w - 1  || y < 0 || y > input->h - 1)
			{
				start[i * w + j] = 0x80;
				continue;
			}

			int a = input->data[(int)(y) * input->w  +     (int)(x)];
			int b = input->data[(int)(y) * input->w  +     (int)(x + 1)];
			int c = input->data[(int)(y + 1) * input->w  + (int)(x)];
			int d = input->data[(int)(y + 1) * input->w  + (int)(x + 1)];
			double k1 = x - (int)(x);
			double k2 = y - (int)(y);

			int result = (a * (1 - k1) + k1 * b) * (1 - k2) +
						 (c * (1 - k1) + k1 * d) * k2;
			start[i * w + j] = result;
		}

	return toReturn;
}

AffineTransform::~AffineTransform() {
	// TODO Auto-generated destructor stub
}
