#include "VTransferFunction.h"
#include "VTransferFunctionNode.h"
#include "V2Ddraw.h"
#include <vector>
#include <algorithm>
#include <iostream>
#include "vmath.h"
#include <math.h>

using namespace std;

VTransferFunction::VTransferFunction(int x, int y,int width, int height,float* alpha)
{
	this->x = x;
	this->y = y;
	this->width = width;
	this->height = height;
	this->alpha = alpha;
	this->dragging = false;
	this->chooser = new VColorChooser(alpha,x,y,width,height);
	this->currentnode = 0;
	this->tex = 0;
	VTransferFunctionNode * begin = new VTransferFunctionNode(this->x,this->y+this->height);
	float * color = begin->getColor();
	color[0] = color[1] = color[2] = 0.0f;
	begin->setChoosed(false);
	this->nodes.push_back(begin);
	VTransferFunctionNode * end = new VTransferFunctionNode(this->x+this->width,this->y+this->height);
	color = end->getColor();
	color[0] = color[1] = color[2] = 0.0f;
	end->setChoosed(false);
	this->nodes.push_back(end);
	this->length = 4096;
	colors = new float[4*length];
}

void VTransferFunction::draw() {
	//draw surrounding box
	V2Ddraw::drawRect(this->x,this->y,this->width,this->height);
	//draw nodes
	stdx::sort(nodes.begin(),nodes.end());
	int startx = this->x;
	int starty = this->y+this->height;
	for(ptr_vector<VTransferFunctionNode>::iterator it=nodes.begin(); it!=nodes.end(); it++) {
		glBegin(GL_LINES);
			glVertex2f(startx,starty);
			glVertex2f((*it).getx(),(*it).gety());
		glEnd();
		startx = (*it).getx();
		starty = (*it).gety();
		(*it).draw();
	}
	/*glBegin(GL_LINES);
		glVertex2f(startx,starty);
		glVertex2f(this->x+this->width,this->y);
	glEnd();*/
	//draw colorbox
	float * c = this->nodes.front().getColor();
	//cout << c[0] << " " << c[1] << " " << c[2] << endl;
	float previous_color[3];
	previous_color[0] = c[0];
	previous_color[1] = c[1];
	previous_color[2] = c[2];
	startx = this->x;
	int y1 = this->y+this->height+5;
	int y2 = this->y+this->height+25;
	for(ptr_vector<VTransferFunctionNode>::iterator it=nodes.begin(); it!=nodes.end(); it++) {
		if(it!=nodes.begin()) {
			float * current_color = (*it).getColor();
			int xn = (*it).getx();
			glBegin(GL_QUADS);
				glColor4f(previous_color[0],previous_color[1],previous_color[2],*(this->alpha));
				glVertex2f(startx,y1);
				glColor4f(current_color[0],current_color[1],current_color[2],*(this->alpha));
				glVertex2f(xn,y1);
				glColor4f(current_color[0],current_color[1],current_color[2],*(this->alpha));
				glVertex2f(xn,y2);
				glColor4f(previous_color[0],previous_color[1],previous_color[2],*(this->alpha));
				glVertex2f(startx,y2);
			glEnd();
			startx = xn;
			previous_color[0] = current_color[0];
			previous_color[1] = current_color[1];
			previous_color[2] = current_color[2];
		}
	}
	
	if(this->chooser->isActive()) {
		this->chooser->draw();
	}
}

void VTransferFunction::pressed(int x, int y) {
	//select node
	for(ptr_vector<VTransferFunctionNode>::iterator it=nodes.begin(); it!=nodes.end(); it++) {
		if((*it).isInside(x,y)) {
			this->currentnode = &(*it);
		}
	}
}

void VTransferFunction::released(int x,int y) {
	if(!this->currentnode && !this->dragging && !this->chooser->isActive()) {//new node (klick irgendwohin)
		this->chooser->setActive(true,x,y);
		this->nodes.push_back(new VTransferFunctionNode(x,y));
	}
	else if(this->currentnode && !this->dragging && !this->chooser->isActive()) { //klick auf knoten
		this->chooser->setActive(true,x,y);
		currentnode->setChoosed(true);
	}
	else if(!this->currentnode && !this->dragging && this->chooser->isActive()) { //klick auf colorchooser
		if(this->chooser->isInside(x,y)) {
			for(ptr_vector<VTransferFunctionNode>::iterator it=nodes.begin(); it!=nodes.end(); it++) {
				if((*it).isChoosed()) {
					float * color = new float[3];
					this->chooser->getColor(x,y,color);
					float * color_node = (*it).getColor();
					color_node[0] = color[0];
					color_node[1] = color[1];
					color_node[2] = color[2];
					//cout << color[0] << " " << color[1] << " " << color[2] << endl;
					delete color;
					this->chooser->setActive(false,0,0);
					(*it).setChoosed(false);
				}
			}
		}
		this->renderTexture();
	} else if(this->dragging) {
		this->renderTexture();
	}
	
	this->currentnode = 0;
	this->dragging = false;
}

void VTransferFunction::drag(int x, int y) {
	this->dragging = true;
	stdx::sort(nodes.begin(),nodes.end());
	bool endknoten = (this->currentnode == &this->nodes.front()) || (this->currentnode == &this->nodes.back());
	if(currentnode && !endknoten) {
		currentnode->setx(x);
		currentnode->sety(y);
	} else if(currentnode && endknoten) {
		currentnode->sety(y);
	}
	this->renderTexture();
}

bool VTransferFunction::isInside(int xm, int ym) {
	return xm > this->x && xm < this->x + this->width && ym > this->y && ym < this->y + this->height;
}

GLuint * VTransferFunction::getTexture() {
	return &this->tex;
}

void VTransferFunction::renderTexture() {
	stdx::sort(this->nodes.begin(),this->nodes.end());

	float * c = this->nodes.front().getColor();
	Vector4<float> color(c[0],c[1],c[2],this->nodes.front().getOpacity(this->height, this->y));
	int anfang = 0;
	int posx = this->x;
	for(ptr_vector<VTransferFunctionNode>::iterator it=nodes.begin(); it!=nodes.end(); it++) {
		if(it!=nodes.begin()) {
			float * c = (*it).getColor();
			float op = (*it).getOpacity(this->height, this->y);
			Vector4<float> color2(c[0],c[1],c[2],op);
			double intervall_norm = (double)((*it).getx() - posx)/(double)this->width;
			int intervall = (int)floor((float)(intervall_norm*(float)length)+0.5);
			float f = 0.0f;
			for(int i = anfang; i < anfang+intervall; i++) {
				Vector4<float> interpol = color.lerp(f,color2); //linear interpolation
				colors[4*i+0] = interpol[0];
				colors[4*i+1] = interpol[1];
				colors[4*i+2] = interpol[2];
				colors[4*i+3] = 1-interpol[3];
				f += 1.0f/(float)intervall;
				//cout << i << endl;
			}
			anfang += intervall;
			posx = (*it).getx();
			color = color2;
		}
	}
	c = this->nodes.back().getColor();
	Vector4<float> ende(c[0],c[1],c[2],this->nodes.back().getOpacity(this->height, this->y));
	for(int i = anfang; i< length; i++) {
		colors[4*i+0] = ende[0];
		colors[4*i+1] = ende[1];
		colors[4*i+2] = ende[2];
		colors[4*i+3] = ende[3];
		//cout << i << endl;
	}
	//for(int i=0; i<length; i++)
	//	cout << colors[4*i+0] << " " << colors[4*i+1] << " " << colors[4*i+2] << " " << colors[4*i+3] << endl;
	if(glIsTexture(this->tex)) {
		glDeleteTextures(1,&this->tex);
	}
	glGenTextures(1,&this->tex);
	glBindTexture(GL_TEXTURE_1D, this->tex);
	glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
	glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
	glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, length, 0, GL_RGBA, GL_FLOAT, colors);


}

VTransferFunction::~VTransferFunction(void)
{
	delete this->chooser;
	delete[] this->colors;
}
