/*
 * $Id: multires.c 13015 2007-12-27 07:27:03Z nicholasbishop $
 *
 * ***** BEGIN GPL LICENSE BLOCK *****
 *
 * 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, USA.
 *
 * The Original Code is Copyright (C) 2006 by Nicholas Bishop
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL LICENSE BLOCK *****
 *
 * Implements the multiresolution modeling tools.
 *
 * multires.h
 *
 */

#include "MEM_guardedalloc.h"

#include "BIF_gl.h"
#include "BIF_glutil.h"
#include "BIF_mywindow.h"
#include "BIF_screen.h"
#include "BIF_space.h"
#include "BIF_radialcontrol.h"

#include "BKE_global.h"
#include "BLI_arithb.h"

#include "mydevice.h"
#include "transform.h"

#include "math.h"

/* Prints the value being edited in the view header */
static void radialcontrol_header(const RadialControl *rc)
{
	if(rc) {
		char str[512];
		const char *name= "";

		if(rc->mode == RADIALCONTROL_SIZE)
			name= "Size";
		else if(rc->mode == RADIALCONTROL_STRENGTH)
			name= "Strength";
		else if(rc->mode == RADIALCONTROL_ROTATION)
			name= "Angle";

		sprintf(str, "%s: %d", name, (int)(rc->new_value));
		headerprint(str);
	}
}

/* Creates, initializes, and returns the control */
RadialControl *radialcontrol_start(const int mode, RadialControlCallback callback,
				   const int original_value, const int max_value,
				   const unsigned int tex)
{
	RadialControl *rc= MEM_callocN(sizeof(RadialControl), "radial control");
	short mouse[2];
		
	getmouseco_areawin(mouse);
	rc->origloc[0]= mouse[0];
	rc->origloc[1]= mouse[1];
		
	if(mode == RADIALCONTROL_SIZE)
		rc->origloc[0]-= original_value;
	else if(mode == RADIALCONTROL_STRENGTH)
		rc->origloc[0]-= 200 - 2*original_value;
	else if(mode == RADIALCONTROL_ROTATION) {
		rc->origloc[0]-= 200 * cos(original_value * M_PI / 180.0);
		rc->origloc[1]-= 200 * sin(original_value * M_PI / 180.0);
	}
		
	rc->callback = callback;
	rc->original_value = original_value;
	rc->max_value = max_value;

	rc->tex = tex;

	/* NumInput is used for keyboard input */
	rc->num = MEM_callocN(sizeof(NumInput), "radialcontrol numinput");
	rc->num->idx_max= 0;

	rc->mode= mode;
	radialcontrol_header(rc);
	
	allqueue(REDRAWVIEW3D, 0);

	return rc;
}


static void radialcontrol_end(RadialControl *rc)
{
	if(rc) {
		rc->callback(rc->mode, rc->new_value);
		BIF_undo_push("Brush property set");

		/* Free everything */
		glDeleteTextures(1, (GLuint*)(&rc->tex));
		MEM_freeN(rc->num);
		MEM_freeN(rc);

		allqueue(REDRAWVIEW3D, 0);
		allqueue(REDRAWBUTSEDIT, 0);
		allqueue(REDRAWHEADERS, 0);
	}
}

void radialcontrol_do_events(RadialControl *rc, unsigned short event)
{
	short mouse[2];
	short tmp[2];
	float dist;
	char valset= 0;

	if(!rc)
		return;
	
	handleNumInput(rc->num, event);
	
	if(hasNumInput(rc->num)) {
		float val;
		applyNumInput(rc->num, &val);
		rc->new_value = val;
		valset= 1;
		allqueue(REDRAWVIEW3D, 0);
	}
	
	switch(event) {
	case MOUSEX:
	case MOUSEY:
		if(!hasNumInput(rc->num)) {
			char ctrl= G.qual & LR_CTRLKEY;
			getmouseco_areawin(mouse);
			tmp[0]= rc->origloc[0]-mouse[0];
			tmp[1]= rc->origloc[1]-mouse[1];
			dist= sqrt(tmp[0]*tmp[0]+tmp[1]*tmp[1]);
			if(rc->mode == RADIALCONTROL_SIZE)
				rc->new_value = dist;
			else if(rc->mode == RADIALCONTROL_STRENGTH) {
				float fin= (200.0f - dist) * 0.5f;
				rc->new_value= fin>=0 ? fin : 0;
			} else if(rc->mode == RADIALCONTROL_ROTATION)
				rc->new_value= ((int)(atan2(tmp[1], tmp[0]) * (180.0 / M_PI)) + 180);

			if(ctrl)
				rc->new_value= (rc->new_value + 5) / 10*10;	

			valset= 1;
			allqueue(REDRAWVIEW3D, 0);
		}
		break;
	case ESCKEY:
	case RIGHTMOUSE:
		rc->new_value = rc->original_value;
	case LEFTMOUSE:
		while(get_mbut()==L_MOUSE);
	case RETKEY:
	case PADENTER:
		radialcontrol_end(rc);
		return;
	default:
		break;
	};
	
	if(valset) {
		if(rc->new_value > rc->max_value)
			rc->new_value = rc->max_value;
	}
	
	radialcontrol_header(rc);
}

static void rot_line(const short o[2], const float ang)
{
	sdrawXORline(o[0], o[1], o[0] + 200*cos(ang), o[1] + 200*sin(ang));
}

void radialcontrol_draw(RadialControl *rc)
{
	short r1=200, r2=200, r3=200;
	float angle = 0;

	if(rc && rc->mode) {
		if(rc->mode == RADIALCONTROL_SIZE) {
			r1= rc->new_value;
			r2= rc->original_value;
			r3= r1;
		} else if(rc->mode == RADIALCONTROL_STRENGTH) {
			r1= 200 - rc->new_value * 2;
			r2= 200;
			r3= 200;
		} else if(rc->mode == RADIALCONTROL_ROTATION) {
			r1= r2= 200;
			r3= 200;
			angle = rc->new_value;
		}
		
		/* Draw brush with texture */
		glPushMatrix();
		glTranslatef(rc->origloc[0], rc->origloc[1], 0);
		glRotatef(angle, 0, 0, 1);

		if(rc->tex) {
			const float str = rc->mode == RADIALCONTROL_STRENGTH ? (rc->new_value / 200.0 + 0.5) : 1;

			glEnable(GL_BLEND);
			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

			glBindTexture(GL_TEXTURE_2D, rc->tex);

			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

			glEnable(GL_TEXTURE_2D);
			glBegin(GL_QUADS);
			glColor4f(0,0,0, str);
			glTexCoord2f(0,0);
			glVertex2f(-r3, -r3);
			glTexCoord2f(1,0);
			glVertex2f(r3, -r3);
			glTexCoord2f(1,1);
			glVertex2f(r3, r3);
			glTexCoord2f(0,1);
			glVertex2f(-r3, r3);
			glEnd();
			glDisable(GL_TEXTURE_2D);
		}
			
		glPopMatrix();

		if(r1 != r2)
			fdrawXORcirc(rc->origloc[0], rc->origloc[1], r1);
		fdrawXORcirc(rc->origloc[0], rc->origloc[1], r2);
			
		if(rc->mode == RADIALCONTROL_ROTATION) {
			float ang1= rc->original_value * (M_PI/180.0f);
			float ang2= rc->new_value * (M_PI/180.0f);

			if(rc->new_value > 359)
				ang2 = 0;

			rot_line(rc->origloc, ang1);
			if(ang1 != ang2)
				rot_line(rc->origloc, ang2);
		}
	}
}
