//-----------------------------------------------------------------------------
//
// kfountain - Partical Fountain Screen Saver for KDE 2
//
// Copyright (c)  Ian Reinhart Geiser 2001
//
// KConfig code and KScreenSaver "Setup..." improvements by
// Nick Betcher <nbetcher@usinternet.com> 2001
//
#include <stdlib.h>
#include <qlabel.h>
#include <qlayout.h>
#include <kapplication.h>
#include <klocale.h>
#include <kconfig.h>
#include <kcolordialog.h>
#include <kbuttonbox.h>
#include <kcolorbutton.h>
#include <kglobal.h>
#include "fountain.h"
#include "fountain.moc"
#ifdef Q_WS_MACX
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#else
#include <GL/glu.h>
#include <GL/gl.h>
#endif
#include <qimage.h>
#include <kdebug.h>
#include <qpainter.h>
#include <qradiobutton.h>
#include <qspinbox.h>
#include <kstandarddirs.h>
#include <math.h>
#include <kmessagebox.h>
// libkscreensaver interface
extern "C"
{
	KDE_EXPORT const char *kss_applicationName = "kfountain.kss";
	KDE_EXPORT const char *kss_description = I18N_NOOP( "Particle Fountain Screen Saver" );
	KDE_EXPORT const char *kss_version = "2.2.0";

	KDE_EXPORT KScreenSaver *kss_create( WId id )
	{
		return new KFountainSaver( id );
	}

	KDE_EXPORT QDialog *kss_setup()
	{
		return new KFountainSetup();
	}
}

//-----------------------------------------------------------------------------
// dialog to setup screen saver parameters
//
KFountainSetup::KFountainSetup( QWidget *parent, const char *name )
	: SetupUi( parent, name, TRUE )
{
	readSettings();

	//QLabel *label;
	//QPushButton *button;

	//setCaption( i18n("Setup Particle Fountain") );

	//QVBoxLayout *tl = new QVBoxLayout(this, 10);
	//QHBoxLayout *tl1 = new QHBoxLayout;
	//tl->addLayout(tl1);

	//QVBoxLayout *tl11 = new QVBoxLayout(5);
	//tl1->addLayout(tl11);

	//label = new QLabel( i18n("No options here yet...:"), this );
	///tl11->addWidget(label);;

	//preview = new QWidget( this );
	preview->setFixedSize( 220, 170 );
	preview->setBackgroundColor( black );
	preview->show();    // otherwise saver does not get correct size
	saver = new KFountainSaver( preview->winId() );
	//tl1->addWidget(preview);

	//KButtonBox *bbox = new KButtonBox(this);
	//bbox->addStretch(1);
;
	connect( PushButton1, SIGNAL( clicked() ), SLOT( slotOkPressed() ) );
	connect( PushButton2, SIGNAL( clicked() ), SLOT( reject() ) );
	connect( PushButton3, SIGNAL( clicked() ), SLOT( aboutPressed() ) );
	connect(  SpinBox1, SIGNAL( valueChanged(int)), saver, SLOT( updateSize(int)));
	connect( RadioButton1, SIGNAL( toggled(bool)), saver, SLOT( doStars(bool)));

}

// read settings from config file
void KFountainSetup::readSettings()
{
	KConfig config("kssfountainrc", false, false);

	config.setGroup( "Settings" );
	QString boolval = config.readEntry( "Stars", "false" );
	if (boolval == "true") {
		RadioButton1->setDown(true);
		RadioButton1_2->setDown(false);
	} else {
		if (boolval == "false")
		{
			RadioButton1->setDown(false);
			RadioButton1_2->setDown(true);
		}
	}

	QString starammount = config.readEntry("StarSize", "75");
	SpinBox1->setValue(starammount.toInt());

}

// Ok pressed - save settings and exit
void KFountainSetup::slotOkPressed()
{
	KConfig config("kssfountainrc", false, false);
	config.setGroup( "Settings" );

	if (RadioButton1->isOn() == true)
	{
		config.writeEntry( "Stars", "true" );
	} else {
		if (RadioButton1_2->isOn() == true)
		{
			config.writeEntry( "Stars", "false" );
		}
	}
	config.writeEntry( "StarSize", QString::number(SpinBox1->value()) );

	config.sync();

	accept();
}

void KFountainSetup::aboutPressed()
{
    KMessageBox::about(this,
        i18n("<h3>Particle Fountain</h3>\n<p>Particle Fountain Screen Saver for KDE</p>\nCopyright (c)  Ian Reinhart Geiser 2001<br>\n\n<p>KConfig code and KScreenSaver \"Setup...\" improvements by Nick Betcher <nbetcher@usinternet.com> 2001</p>"));
}
//-----------------------------------------------------------------------------


KFountainSaver::KFountainSaver( WId id ) : KScreenSaver( id )
{

	kdDebug() << "Blank" << endl;

	timer = new QTimer( this );
    	timer->start( 25, TRUE );
	setBackgroundColor( black );
        erase();
	fountain = new Fountain();
	embed(fountain);
	fountain->show();
	connect( timer, SIGNAL(timeout()), this, SLOT(blank()) );
}

KFountainSaver::~KFountainSaver()
{

}

// read configuration settings from config file
void KFountainSaver::readSettings()
{
// Please remove me

}

void KFountainSaver::blank()
{
	// Play fountain

	fountain->updateGL();
	timer->start( 25, TRUE );

}
Fountain::Fountain( QWidget * parent, const char * name) : QGLWidget (parent,name)
{
	rainbow=true;
	slowdown=2.0f;
	zoom=-40.0f;
	index=0;
	size = 0.75f;
	obj = gluNewQuadric();

// This has to be here because you can't update the fountain until 'fountain' is created!
	KConfig config("kssfountainrc", false, false);
	config.setGroup( "Settings" );
	QString boolval = config.readEntry( "Stars", "false" );
	if (boolval == "true") {
		setStars(true);
	} else {
		if (boolval == "false")
		{
			setStars(false);
		}
	}

	QString starammount = config.readEntry("StarSize", "75");
	float passvalue = (starammount.toInt() / 100.0);
	setSize(passvalue);

}

Fountain::~Fountain()
{
	glDeleteTextures( 1, &texture[0] );
	gluDeleteQuadric(obj);
}

/** load the particle file */
bool Fountain::loadParticle()
{
    /* Status indicator */
    bool Status = TRUE;
	QImage buf;

    kdDebug() << "Loading: " << locate("data", "kscreensaver/particle.png") << endl;
 if (buf.load( locate("data", "kscreensaver/particle.png") ) )

        {
		tex = convertToGLFormat(buf);  // flipped 32bit RGBA
		kdDebug() << "Texture loaded: " << tex.numBytes () << endl;
	}
	else
	{
		QImage dummy( 32, 32, 32 );
  		dummy.fill( Qt::white.rgb() );
        	buf = dummy;
		tex = convertToGLFormat( buf );
	}

            /* Set the status to true */
            //Status = TRUE;
	glGenTextures(1, &texture[0]);   /* create three textures */
	glBindTexture(GL_TEXTURE_2D, texture[0]);
	/* use linear filtering */
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	/* actually generate the texture */
	glTexImage2D(GL_TEXTURE_2D, 0, 4, tex.width(), tex.height(), 0,
	GL_RGBA, GL_UNSIGNED_BYTE, tex.bits());



    return Status;
}
/** setup the GL enviroment */
void Fountain::initializeGL ()
{

	kdDebug() << "InitGL" << endl;
	GLfloat colors[12][3]=
	{{1.0f,0.5f,0.5f},{1.0f,0.75f,0.5f},{1.0f,1.0f,0.5f},{0.75f,1.0f,0.5f},
	{0.5f,1.0f,0.5f},{0.5f,1.0f,0.75f},{0.5f,1.0f,1.0f},{0.5f,0.75f,1.0f},
	{0.5f,0.5f,1.0f},{0.75f,0.5f,1.0f},{1.0f,0.5f,1.0f},{1.0f,0.5f,0.75f}};

	if (loadParticle())						// Jump To Texture Loading Routine
	{
    /* Enable smooth shading */
    glShadeModel( GL_SMOOTH );

    /* Set the background black */
    glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );

    /* Depth buffer setup */
    glClearDepth( 1.0f );

    /* Enables Depth Testing */
    glDisable( GL_DEPTH_TEST );

    /* Enable Blending */
    glEnable( GL_BLEND );
    /* Type Of Blending To Perform */
    glBlendFunc( GL_SRC_ALPHA, GL_ONE );


    /* Really Nice Perspective Calculations */
    glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
    /* Really Nice Point Smoothing */
    glHint( GL_POINT_SMOOTH_HINT, GL_NICEST );

    /* Enable Texture Mapping */
    glEnable( GL_TEXTURE_2D );
    /* Select Our Texture */
    glBindTexture( GL_TEXTURE_2D, texture[0] );

		for (loop=0;loop<MAX_PARTICLES;loop++)				// Initials All The Textures
		{
			particle[loop].active=true;				// Make All The Particles Active
			particle[loop].life=1.0f;				// Give All The Particles Full Life
			particle[loop].fade=float(KApplication::random()%100)/1000.0f+0.003f;	// Random Fade Speed
			particle[loop].r=colors[(loop+1)/(MAX_PARTICLES/12)][0];	// Select Red Rainbow Color
			particle[loop].g=colors[(loop+1)/(MAX_PARTICLES/12)][1];	// Select Green Rainbow Color
			particle[loop].b=colors[(loop+1)/(MAX_PARTICLES/12)][2];	// Select Blue Rainbow Color
			particle[loop].xi=float((KApplication::random()%50)-26.0f)*10.0f;	// Random Speed On X Axis
			particle[loop].yi=float((KApplication::random()%50)-25.0f)*10.0f;	// Random Speed On Y Axis
			particle[loop].zi=float((KApplication::random()%50)-25.0f)*10.0f;	// Random Speed On Z Axis
			particle[loop].xg=0.0f;					// Set Horizontal Pull To Zero
			particle[loop].yg=-0.8f;				// Set Vertical Pull Downward
			particle[loop].zg=0.0f;					// Set Pull On Z Axis To Zero
			particle[loop].size=size;				// Set particle size.
		}
	}
	else
		exit(0);
}
/** resize the gl view */
void Fountain::resizeGL ( int width, int height )
{
	kdDebug() << "ResizeGL " << width << "," <<height<< endl;
	if (height==0)							// Prevent A Divide By Zero By
	{
		height=1;						// Making Height Equal One
	}

	glViewport(0,0,width,height);					// Reset The Current Viewport

	glMatrixMode(GL_PROJECTION);					// Select The Projection Matrix
	glLoadIdentity();						// Reset The Projection Matrix

	// Calculate The Aspect Ratio Of The Window
	gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,200.0f);

	glMatrixMode(GL_MODELVIEW);					// Select The Modelview Matrix
	glLoadIdentity();
}
/** paint the GL view */
void Fountain::paintGL ()
{
	//kdDebug() << "PaintGL" << endl;

	GLfloat colors[12][3]=
	{{1.0f,0.5f,0.5f},{1.0f,0.75f,0.5f},{1.0f,1.0f,0.5f},{0.75f,1.0f,0.5f},
	{0.5f,1.0f,0.5f},{0.5f,1.0f,0.75f},{0.5f,1.0f,1.0f},{0.5f,0.75f,1.0f},
	{0.5f,0.5f,1.0f},{0.75f,0.5f,1.0f},{1.0f,0.5f,1.0f},{1.0f,0.5f,0.75f}};
	col = ( ++col ) % 12;
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);		// Clear Screen And Depth Buffer

	glLoadIdentity();
						// Reset The ModelView Matrix
	transIndex++;
	glTranslatef( GLfloat(5.0*sin(4*3.14*transIndex/360)), GLfloat(4.0*cos(2*3.14*transIndex/360)), 0.0 );
	xspeed = GLfloat(100.0*cos(3*3.14*transIndex/360)+100);
	yspeed = GLfloat(100.0*sin(3*3.14*transIndex/360)+100);
	//slowdown = GLfloat(4.0*sin(2*3.14*transIndex/360)+4.01);

	for (loop=0;loop<MAX_PARTICLES;loop++)				// Loop Through All The Particles
	{
		if (particle[loop].active)				// If The Particle Is Active
		{
			float x=particle[loop].x;			// Grab Our Particle X Position
			float y=particle[loop].y;			// Grab Our Particle Y Position
			float z=particle[loop].z+zoom;			// Particle Z Pos + Zoom
    /* Select Our Texture */

                    /* Draw The Particle Using Our RGB Values,
                     * Fade The Particle Based On It's Life
                     */

                    glColor4f( particle[loop].r,
                               particle[loop].g,
                               particle[loop].b,
                               particle[loop].life );

                    /* Build Quad From A Triangle Strip */
		  if( !stars )
		    glBegin( GL_TRIANGLE_STRIP );
		  else
		    glBegin( GL_TRIANGLE_FAN );
                      /* Top Right */
                      glTexCoord2d( 1, 1 );
                      glVertex3f( x + particle[loop].size, y + particle[loop].size, z );
                      /* Top Left */
                      glTexCoord2d( 0, 1 );
                      glVertex3f( x - particle[loop].size, y + particle[loop].size, z );
                      /* Bottom Right */
                      glTexCoord2d( 1, 0 );
                      glVertex3f( x + particle[loop].size, y - particle[loop].size, z );
                      /* Bottom Left */
                      glTexCoord2d( 0, 0 );
                      glVertex3f( x - particle[loop].size, y - particle[loop].size, z );
                    glEnd( );

			particle[loop].x+=particle[loop].xi/(slowdown*1000);// Move On The X Axis By X Speed
			particle[loop].y+=particle[loop].yi/(slowdown*1000);// Move On The Y Axis By Y Speed
			particle[loop].z+=particle[loop].zi/(slowdown*1000);// Move On The Z Axis By Z Speed

			particle[loop].xi+=particle[loop].xg;			// Take Pull On X Axis Into Account
			particle[loop].yi+=particle[loop].yg;			// Take Pull On Y Axis Into Account
			particle[loop].zi+=particle[loop].zg;			// Take Pull On Z Axis Into Account
			particle[loop].life-=particle[loop].fade;		// Reduce Particles Life By 'Fade'

			if (particle[loop].life<0.0f)					// If Particle Is Burned Out
			{
				particle[loop].life=2.0f;				// Give It New Life
				particle[loop].fade=float(KApplication::random()%100)/1000.0f+0.003f;	// Random Fade Value
				particle[loop].x=0.0f;					// Center On X Axis
				particle[loop].y=0.0f;					// Center On Y Axis
				particle[loop].z=0.0f;					// Center On Z Axis
				particle[loop].xi=xspeed+float((KApplication::random()%60)-32.0f);	// X Axis Speed And Direction
				particle[loop].yi=yspeed+float((KApplication::random()%60)-30.0f);	// Y Axis Speed And Direction
				particle[loop].zi=float((KApplication::random()%60)-30.0f);		// Z Axis Speed And Direction
				particle[loop].r=colors[col][0];			// Select Red From Color Table
				particle[loop].g=colors[col][1];			// Select Green From Color Table
				particle[loop].b=colors[col][2];			// Select Blue From Color Table
				particle[loop].size=size;
				if ((1+(random()%20)) == 10)
				{
				// Explode
					particle[loop].active=true;				// Make All The Particles Active
					particle[loop].life=1.0f;				// Give All The Particles Full Life
					particle[loop].fade=float(KApplication::random()%100)/1000.0f+0.003f;	// Random Fade Speed
					particle[loop].r=colors[(loop+1)/(MAX_PARTICLES/12)][0];	// Select Red Rainbow Color
					particle[loop].g=colors[(loop+1)/(MAX_PARTICLES/12)][1];	// Select Green Rainbow Color
					particle[loop].b=colors[(loop+1)/(MAX_PARTICLES/12)][2];	// Select Blue Rainbow Color
					particle[loop].xi=float((KApplication::random()%50)-26.0f)*10.0f;	// Random Speed On X Axis
					particle[loop].yi=float((KApplication::random()%50)-25.0f)*10.0f;	// Random Speed On Y Axis
					particle[loop].zi=float((KApplication::random()%50)-25.0f)*10.0f;	// Random Speed On Z Axis
					particle[loop].xg=0.0f;					// Set Horizontal Pull To Zero
					particle[loop].yg=-0.8f;				// Set Vertical Pull Downward
					particle[loop].zg=0.0f;					// Set Pull On Z Axis To Zero
					particle[loop].size=size;				// Set particle size.
				}
			}
			// Lets stir some things up
			index += 0.001;
			particle[loop].yg =2.0*sin(2*3.14*transIndex/360);
			particle[loop].xg =2.0*cos(2*3.14*transIndex/360);
			particle[loop].zg =4.0+(4.0*cos(2*3.14*transIndex/360));

		}
	}

	glFlush();
}
void Fountain::setSize( float newSize )
{
	size = newSize;
}
void Fountain::setStars( bool doStars )
{
	stars = doStars;
}

void KFountainSaver::updateSize(int newSize)
{
	fountain->setSize(newSize/100);
}
void KFountainSaver::doStars(bool starState)
{
	fountain->setStars(starState);
}

