/*  Cube
 *  Copyright (C) Joakim Kolsjö 2004
 *	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 2 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, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 
 */

#include "gfx_cube.h"
#include <iostream>
#include <cmath>

using namespace std;

namespace gfx
{	
	Cube::Cube()
	{
		
		angle = 0;
		
		tail = new xio::vector2d[100];
		for(int i = 0; i < 100; i++)
		{
			tail[i].x = 0;	
			tail[i].y = 0;
		}
		tail_reset = false;
	}

	void Cube::init(xio::Manager *i, int size)
	{
		xio = i;
		// Define the cube
		for(int i = 0; i < 8; i++)
		{
			x[i] = (float)(size/2 - (size * (((i + 1)/2) % 2)));
			y[i] = (float)(size/2 - (size * ((i / 2) % 2)));
			z[i] = (float)(size/2 - (size * ((i / 4) % 2)));
		}
		
		height = width = size*3;
		s = SDL_CreateRGBSurface(SDL_SWSURFACE, height, width, 32, 0, 0, 0, 0);
		
	}
		
	xio::surface *Cube::draw(float move, int change, bool wirecube,
				bool tail, bool edges)
	{	
		wq = wirecube;
		tl = tail;
		ed = edges;
		
		SDL_FillRect(s, NULL, 0xff00ff);
		
		render(angle, 360-angle, 0);
		
		angle += move * change;
	
		if(angle >= 360)
			angle=0;
	
		return s;
	}


	void Cube::drawLine(float x1, float y1, float x2, float y2)
	{
		double hl = fabs(x2-x1);
		double vl = fabs(y2-y1);
		double length = (hl>vl) ? hl : vl;
		float deltax = ((x2-x1) / (float)length);
		float deltay = ((y2-y1) / (float)length);
				
		// Push tail positions back
		for(int i = 100-1; i > 0; i--)
		{
			tail[i].x = tail[i-1].x;
			tail[i].y = tail[i-1].y;
		}
		
		for(int i = 0; i< (int)length; i++)
		{
			unsigned long x = (int)(x1 += deltax);
			unsigned long y = (int)(y1 += deltay);
			if(i == 0) {
				tail[0].x = x;
				tail[0].y = y;
				if(ed)
					xio->video->setPixel(s, x, y, 255, 255, 255);
				
				if(tl)
				{
					// Draw edges with tails
					for(int i = 0; i < 100; i++)
					{
						// Make shure we don't go over 255
						if(i < 28)
						{
							//xio->video->drawPixel(
							xio->video->setPixel(s, (int)tail[i].x, (int)tail[i].y,
								(int)((100-i)*2.55),
								(int)((100-i)*2.55), 
								(int)((100-i)*2.55));
						}
						// Decrease colors
						else {
							//xio->video->drawPixel(
							xio->video->setPixel(s, (int)tail[i].x, (int)tail[i].y,
								(int)((100-i)*2.55+30),
								(int)((100-i)*2.55+60), 
								(int)((100-i)*2.55+70));
						}
					}
				}
			}
			else
				if(wq)
					xio->video->setPixel(s, x, y, 255, 255, 255);
		}		
	}
	
	void Cube::render(float xa, float ya, float za)
	{
		float mat[4][4];					// Determine rotation matrix
		
		// Transform angle in degrees (0-360) to radians (0-2*PI)
		float xradian = (xa * 3.1416f)/180;
		float yradian = (ya * 3.1416f)/180;
		float zradian = (za * 3.1416f)/180;
		
		// Sines
		float sx = (float)sin(xradian);
		float sy = (float)sin(yradian);
		float sz = (float)sin(zradian);
			  
		// Cosinuses
		float cx = (float)cos(xradian);
		float cy = (float)cos(yradian);
		float cz = (float)cos(zradian);
		
		mat[0][0] = (cx * cz) + (sx * sy * sz);
		mat[1][0] = (-cx * sz) + (cz * sx * sy);
		mat[2][0] = cy * sx;
		
		mat[0][1] = (cy * sz);
		mat[1][1] = (cy * cz);
		mat[2][1] = -sy;
		
		mat[0][2] = (-cz * sx) + (cx * sy * sz);
		mat[1][2] = (sx * sz) + (cx * cz * sy);
		mat[2][2] = cx * cy;
		
		int i;
		// Rotate and apply perspective
		for(i = 0; i < 8; i++)
		{
			rx[i] = (x[i] * mat[0][0]) + (y[i] * mat[1][0]) + (z[i] * mat[2][0]);
			ry[i] = (x[i] * mat[0][1]) + (y[i] * mat[1][1]) + (z[i] * mat[2][1]);
			rz[i] = (x[i] * mat[0][2]) + (y[i] * mat[1][2]) + (z[i] * mat[2][2]) +300;
			scrx[i] = ((rx[i] * 500)/rz[i]) + width/2;// + 320;
			scry[i] = ((ry[i] * 500)/rz[i]) + height/2;// + 240;
		}
		// Actual drawing
		for(i = 0; i < 4; i++)
		{
			drawLine(scrx[i], scry[i], scrx[i+4], scry[i+4]);
			drawLine(scrx[i], scry[i], scrx[(i+1)%4], scry[(i+1)%4]);
			drawLine(scrx[i+4], scry[i+4], scrx[((i+1)%4)+4], scry[((i+1)%4)+4]);
		}
	}

}
