#ifdef _WIN32
#include <windows.h>
#endif
#include <math.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <vector>
#include "objects.h"
#include "linalg.cpp"

using namespace std;
vector<int> lists;

float spline(float x)
{
      float X=fabs(x);

          if(X>2.0f)return 0;
          if(X>1.0f)return (2.0f-X)*(2.0f-X)*(2.0f-X)/6.0f;
          return 2.0f/3.0f-X*X+.5*X*X*X;
}


struct stepinfo
{
	float radius;
	float zstep;
	float xrotangle;
	float yrotangle;
	float noise;
	
};


stepinfo calcspline(vector<stepinfo>  &ctrlpoints,float time)
{
	stepinfo *pprev,*prev,*next,*nnext;
	int itime,isize;
	stepinfo result;
	float ppweight,pweight,nweight,nnweight;
	itime=(int)time;
	isize=ctrlpoints.size()-1;
	pprev=&ctrlpoints[min(max(0,itime-1),isize)];
	 prev=&ctrlpoints[min(max(0,itime  ),isize)];
	 next=&ctrlpoints[min(max(0,itime+1),isize)];
	nnext=&ctrlpoints[min(max(0,itime+2),isize)];
	ppweight=spline(time-itime+1.0f);
	 pweight=spline(time-itime+0.0f);
	 nweight=spline(time-itime-1.0f);
	nnweight=spline(time-itime-2.0f);

    result.radius=pprev->radius*ppweight+
		           prev->radius*pweight+
				   next->radius*nweight+
				  nnext->radius*nnweight;

	result.zstep=pprev->zstep*ppweight+
		          prev->zstep*pweight+
				  next->zstep*nweight+
				 nnext->zstep*nnweight;

	result.xrotangle=pprev->xrotangle*ppweight+
		              prev->xrotangle*pweight+
				      next->xrotangle*nweight+
				     nnext->xrotangle*nnweight;

	result.yrotangle=pprev->yrotangle*ppweight+
		              prev->yrotangle*pweight+
				      next->yrotangle*nweight+
				     nnext->yrotangle*nnweight;

	result.noise=pprev->noise*ppweight+
		              prev->noise*pweight+
				      next->noise*nweight+
				     nnext->noise*nnweight;


	return result;



}



template <int i,int j> struct psThing 
{
    vec3 col;
    vec3 coo[i][j];
    vec3 nor[i][j];
    vec3 der[i][j];
    vec3 tex[i][j];
    vec3 offs[i];
    mat3 mats[i];
    
    psThing(){};
    
    void psBuildThing(mat3 &mbeg,vec3 &bbeg,
    	              vector<stepinfo>  &ctrlpoints,int begin)
    {
        vec3 rvec;
        mat3 currmatrix;
        vec3 currvector;
        stepinfo currpoint;
        int x,y;
        float X,Y,PY,R,size;
		
        vec3 a,b,c,d,n;

        size=ctrlpoints.size();
        currvector=bbeg;
        currmatrix=mbeg;

        for( x=0;x<i;x++)
        {
            
            X=1.0f+(size-3.0f)*x/(float)(i-1.0f);
            currpoint=calcspline(ctrlpoints,X);
            offs[x]=currvector;
            mats[x]=currmatrix;

            for( y=0;y<j;y++)
            {
              Y=y/(float)(j-3.0f); 
        	  PY=PI*Y*2.0f;
			  R=currpoint.radius+currpoint.noise*sin(6*PY)*sin(x/(float)(i-1.0f)*PI*2*10);
              //R*=1.6;
			
			  rvec=vec3(R*cos(PY),R*sin(PY),0.0f)*currmatrix+currvector;
			  coo[x][y]=rvec;
			  tex[x][y]=vec(x*.041f,1.0f*(y-1.0f)/(j-3.0f));
			 
			
			}


            currmatrix=
				setrx(currpoint.yrotangle)*setrz(currpoint.xrotangle)*setry(0)*currmatrix;
			currvector=currvector+vec3(0.0f,0.0f,1.2f*currpoint.zstep)*currmatrix;
			
		}

		for(x=0;x<i;x++)
		for(y=1;y<j-1;y++)
		{
			

			a=coo[min(x+1,i-1)][y];
			b=coo[max(x-1,0)][y];
			c=coo[x][y+1];
			d=coo[x][y-1];
			a=a-b;
			c=c-d;
			c.normalize();
			der[x][y]=c;
			n=(c)&(a);
			n.normalize();
			nor[x][y]=n;
		}

		

		int newlist=glGenLists(1);
	    lists.push_back(newlist);
        glNewList(newlist,GL_COMPILE);
		glColor3fv(col());
		for( x=begin;x<i-1;x++)
		{

			glBegin(GL_QUAD_STRIP);
			for(y=1;y<j-1;y++)
			{
			
				glNormal3fv(nor[x][y]());
				glTexCoord2fv(tex[x][y]());
				glVertex3fv(coo[x][y]());
				glNormal3fv(nor[x+1][y]());
				glTexCoord2fv(tex[x+1][y]());
				glVertex3fv(coo[x+1][y]());
			}
			glEnd();
		} 
		
		glEndList();
     




	}
};


void build_objects()
{
	
	psThing<80,40> spiral;
	psThing<20,20> banch;
	int i,j;
	mat3 mbeg;
	float mul=1.8f;
	vec3 bbeg=vec(1,10,-80);
	stepinfo spoints[]=
	{
		{mul*0.0f,2.5f,0.0f,0.0f,0.0f},
		{mul*2.0f,2.5f,0.0f,-0.0f,0.0f},
		{mul*10.0f,2.5f,0.0f,-0.001f,0.9f},
		{mul*9.0f,2.5f,0.00f,-0.051f,0.9f},
		{mul*8.0f,2.5f,0.00f,-0.081f,0.9f},
		{mul*7.0f,1.8f,0.00f,0.0f,0.6f},
		{mul*6.0f,1.8f,0.00f,0.091f,0.6f},
		{mul*5.0f,1.8f,0.00f,0.091f,0.6f},
		{mul*5.0f,1.5f,0.00f,0.091f,0.6f},
		{mul*4.0f,1.5f,0.00f, 0.151f,0.3f},
		{mul*3.0f,1.5f,0.00f ,0.151f,0.3f},
		{mul*3.0f,1.5f,0.00f, 0.181f,0.1f},
		{mul*2.0f,1.0f,0.00f, 0.381f,0.0f},
		{mul*0.0f,1.0f,0.0f, 0.35f},
		{mul*-1.0f,1.9f,0.0f,0.15f}
	};

	stepinfo npoints[]=
	{
		{mul*3.0f,2.7f,0.0f,0.2f,0.0f},
		{mul*3.0f,2.7f,0.0f, 0.2f,0.0f},
		{mul*3.0f,2.7f,0.0f,-0.0f,0.0f},
		{mul*2.5f,2.5f,0.0f,-0.1f,0.0f},
		{mul*2.0f,2.5f,0.0f,-0.15f,0.0f},
		{mul*1.7f,1.8f,0.0f,0.0f,0.0f},
		{mul*1.5f,1.8f,0.0f,0.19f,0.0f},
		{mul*1.0f,1.8f,0.0f,0.19f,0.0f},
		{mul*0.0f,1.5f,0.0f,0.19f,0.0f},
		{mul*-1.0f,1.5f,0.0f, 0.15f,0.0f}
	};

	stepinfo npoints2[]=
	{
		{mul*3.0f,2.7f,0.0f,0.0f,0.0f},
		{mul*3.0f,2.7f,0.0f, 0.0f,0.0f},
		{mul*3.0f,2.7f,0.0f,-0.0f,0.0f},
		{mul*2.5f,2.5f,0.0f,-0.1f,0.0f},
		{mul*2.0f,2.5f,0.0f,-0.1f,0.0f},
		{mul*1.7f,1.8f,0.0f,-0.1f,0.0f},
		{mul*1.5f,1.8f,0.0f,-0.1f,0.0f},
		{mul*1.0f,1.8f,0.0f,-0.19f,0.0f},
		{mul*0.0f,1.5f,0.0f,-0.19f,0.0f},
		{mul*-1.0f,1.5f,0.0f, -0.15f,0.0f}
	};
	
	vector<stepinfo> bpoints;

	spiral.col=vec3(1.0f,1.0f,1.0f);
	banch.col=vec3(1.0f,0.4f,0.4f);

	mbeg=setrx(0.8f);
	bbeg=vec(0.0f,0.0f,-70.0f);
	bpoints=vector<stepinfo>(spoints,spoints+15);
	spiral.psBuildThing(mbeg,bbeg,bpoints,4);

	mbeg=setrx(4.95f)*setrz(0.0f)*spiral.mats[37];
	bbeg=spiral.offs[47];
	bpoints=vector<stepinfo>(npoints2,npoints2+10);
    banch.psBuildThing(mbeg,bbeg,bpoints,0);

	mbeg=setrx(4.75f)*setrz(-1.0f)*spiral.mats[37];
	bbeg=spiral.offs[37];
	bpoints=vector<stepinfo>(npoints,npoints+10);

	banch.psBuildThing(mbeg,bbeg,bpoints,0);
	mbeg=setrx(4.75f)*setrz(1.)*spiral.mats[37];
	bbeg=spiral.offs[37];
    banch.psBuildThing(mbeg,bbeg,bpoints,0);

	mbeg=setrx(0.8f)*setrz((float)PI*2.0f/3.0f);
	bbeg=vec(0,0,-70);
	bpoints=vector<stepinfo>(spoints,spoints+15);
	spiral.psBuildThing(mbeg,bbeg,bpoints,4);

	mbeg=setrx(4.95f)*setrz(0.0f)*spiral.mats[37];
	bbeg=spiral.offs[47];
	bpoints=vector<stepinfo>(npoints2,npoints2+10);
    banch.psBuildThing(mbeg,bbeg,bpoints,0);
    
	mbeg=setrx(4.75f)*setrz(-1.0f)*spiral.mats[37];
	bbeg=spiral.offs[37];
	bpoints=vector<stepinfo>(npoints,npoints+10);
	banch.psBuildThing(mbeg,bbeg,bpoints,0);
	
	mbeg=setrx(4.75f)*setrz(1.0f)*spiral.mats[37];
	bbeg=spiral.offs[37];
	banch.psBuildThing(mbeg,bbeg,bpoints,0);
    
	
	mbeg=setrx(0.8f)*setrz((float)PI*4.0f/3.0f);
	bbeg=vec(0,0,-70);
	bpoints=vector<stepinfo>(spoints,spoints+15);
	spiral.psBuildThing(mbeg,bbeg,bpoints,4);

	mbeg=setrx(4.95f)*setrz(0)*spiral.mats[37];
	bbeg=spiral.offs[47];
	bpoints=vector<stepinfo>(npoints2,npoints2+10);
    banch.psBuildThing(mbeg,bbeg,bpoints,0);
	
	mbeg=setrx(4.75f)*setrz(-1.0f)*spiral.mats[37];
	bbeg=spiral.offs[37];
	bpoints=vector<stepinfo>(npoints,npoints+10);
	banch.psBuildThing(mbeg,bbeg,bpoints,0);
	
	mbeg=setrx(4.75f)*setrz(1.0f)*spiral.mats[37];
	bbeg=spiral.offs[37];
    banch.psBuildThing(mbeg,bbeg,bpoints,0);
		

    //build backgroubd quad
	int newlist=glGenLists(1);
	lists.push_back(newlist);
    glNewList(newlist,GL_COMPILE);
    glLoadIdentity();
	glTranslatef(0,0,-280);
	glColor3f(1,1,1);

	for( i=-60;i<60;i++)
	{
		glBegin(GL_QUAD_STRIP);

			
				glNormal3f(0,0,0.1f);
				for(j=-40;j<41;j++)
				{
					glTexCoord2f(i/30.0f,j/30.0f);
					glVertex2f(i*2.9,j*2.6);
					glTexCoord2f((i+1.0f)/30.0f,j/30.0f);
					glVertex2f((i+1)*2.9,j*2.6);
				}
				glEnd();
			}

	  glEndList();
}


void paint_objects()
{
	for(unsigned int i=0;i<lists.size();i++)
	{
		glCallList(lists[i]);
	}
}





