#include <windows.h>
#include <stdio.h>

#include <math.h>

#include "utility.h"
#include "opengl.h"
#include "window.h"
#include "station.h"

opengl_class::opengl_class( void )
{
    hrc = NULL;

    ZeroMemory( &pfd, sizeof( pfd ) );

    pfd.nSize = sizeof( pfd );
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
    
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = 24;
    pfd.cDepthBits = 16;
    pfd.iLayerType = PFD_MAIN_PLANE;
    
      
    x_ang = 0;   
    y_ang = 0;
       
    zoom = -6;
}


void opengl_class::setup_view( void )
{
          
  	glViewport(0,0,window_link->display_x,window_link->display_y);						// Reset The Current Viewport

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

	gluPerspective(45.0f,(GLfloat)window_link->display_x/(GLfloat)window_link->display_y,0.1f,100.0f);

	glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix
	glLoadIdentity();
       	              
}




void opengl_class::frame(  )
{     

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	
    
	glLoadIdentity();					// Reset The View

	glTranslatef(0.0f,0.0f,zoom);				// Move Left And Into The Screen

	glRotatef(x_ang, 1.0f, 0.0f, 0.0f);	
	glRotatef(y_ang, 0.0f, 1.0f, 0.0f);			
      		
    glCallList(world_grid);
 
    //plot_stations();
 
    HDC hdc = GetDC(window_link->hwnd);

    SwapBuffers(hdc);

    char tmp[400];
    sprintf(tmp, "FPS: %4d  x_ang: %5.2f  y_ang: %5.3f zoom: %5.2f", window_link->fps,
            x_ang, y_ang,zoom);

    //TextOut(hdc, 0,0, tmp, strlen(tmp));  
    
    ReleaseDC(window_link->hwnd, hdc);
}



void opengl_class::start( window_class *link_source )
{
    window_link = link_source;
    
	log("GL start");
  
  	HDC hdc = GetDC(window_link->hwnd);

    int format = ChoosePixelFormat( hdc, &pfd );
    
    if (format == 0)
    {
        MessageBox(0, "Choose pixel format", "", 0);
        return;    
    }
    
    char tmp[400];
    sprintf(tmp, "memdc: %d, format: %d", hdc, format);
    log(tmp);   
    
    if ( SetPixelFormat(hdc, format, &pfd ) == 0)
     {
        char tmp[400];
        sprintf(tmp, "SetPixelFormat Fail: %d", GetLastError());

        MessageBox(0, "SetPixelFormat", "", 0);
        log(tmp);
        return;    
    }
    
    hrc = wglCreateContext( hdc );
    wglMakeCurrent( hdc, hrc );   
 
   	glShadeModel(GL_SMOOTH);							// Enable Smooth Shading
	glClearColor(0.0f, 0.0f, 0.0f, 0.5f);				// Black Background
	glClearDepth(1.0f);									// Depth Buffer Setup
	glEnable(GL_DEPTH_TEST);							// Enables Depth Testing
	glDepthFunc(GL_LEQUAL);								// The Type Of Depth Testing To Do
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	//


    setup_view();
    setup_data();
      
}



void opengl_class::stop( )
{
    log("GL stop");

    wglMakeCurrent( NULL, NULL );
    wglDeleteContext( hrc );


   // ReleaseDC( window_link->hwnd, window_link->memdc );
}






void  get_cart(geo_pos pos,  float &x, float &y, float &z)
{
    x = pos.elv*cos(pos.lat * M_PI/180.0) * cos(pos.lon * M_PI/180.0);
    z = pos.elv*cos(pos.lat * M_PI/180.0) * sin(pos.lon * M_PI/180.0);
    y = pos.elv*sin(pos.lat * M_PI/180.);   

//   v = axis / (Math.Sqrt (1 - ecc * (Math.Pow (Math.Sin(lat), 2))));
//   x = (v + height) * Math.Cos(lat) * Math.Cos(lon);
//   y = (v + height) * Math.Cos(lat) * Math.Sin(lon);
//   z = ((1 - ecc) * v + height) * Math.Sin(lat);                 
}
 
 
 
void add_lat_lon_point( float lat, float lon, float elv)
{
    geo_pos p;
    
    
    p.lat = lat;
    p.lon = lon;
    p.elv = elv;

    float x, y, z;
    get_cart(p,  x, y, z);
    
    glVertex3f(x, y, z); 
    
}
 
           

void add_geo_point( geo_pos p )
{
    float x, y, z;
    get_cart(p,  x, y, z);
    
    glVertex3f(x, y, z); 
    
}


void color_temp(float tmp, float &r, float &g, float &b)
{
    
    double range = max_temp - min_temp;
    
//    Get a scale form 0 to 2PI
    
    
    float val = ((tmp - min_temp) / range) * (M_PI/2.0);
    
    if (val < 0) val = 0; 
    if (val > M_PI/2.0) val = M_PI/2.0;


    
    g = sin(val*2);//0;//0.5 + 0.5*sin(val+M_PI/6);

    b = cos(val);
    r = sin(val);
       
    
   


    if (r > 1) r = 1;
    if (g > 1) g = 1;
    if (b > 1) b = 1;

    if (r < 0) r = 0;
    if (g < 0) g = 0;
    if (b < 0) b = 0;    


    
}

void opengl_class::plot_stations ( void )
{
    float s = 0.1;
      
    glColor3f(1.0,0.0,0.0);

    for (int i = 0; i < num_stations; i++)
    {
        
        if (stations[i].data_valid)
        {
        glBegin(GL_POLYGON);

        float r, g, b;

        color_temp((float)stations[i].data.temp,r, g, b);

        glColor3f(r, g, b);


        add_lat_lon_point(stations[i].pos.lat +s, stations[i].pos.lon, 1.001);
        add_lat_lon_point(stations[i].pos.lat - s, stations[i].pos.lon -s, 1.001);
        add_lat_lon_point(stations[i].pos.lat - s, stations[i].pos.lon +s, 1.001);
        
        glEnd();        
        
        
        float x, y, z;
        get_cart(stations[i].pos, x, y, z);

    }


//int font;

//        glFontBegin(&font);
//        glScalef(8.0, 8.0, 8.0);
//        glTranslatef(30, 30, 0);
//        glFontTextOut("Test", 5, 5, 0);
//        glFontEnd();
//        glFlush();

      
        
        
        
    }        
}



typedef struct dist_index
{
 unsigned int index;    
float dist;
};



int dist_compare (const void * a, const void * b)
{
    if( (*(dist_index*)a).dist < (*(dist_index*)b).dist) return(-1);
    if( (*(dist_index*)a).dist > (*(dist_index*)b).dist) return(1);

    return(0);
}

/* 

   Create the triangle using Delaunay triangulation

*/
void generate_mesh ( void )
{
    console("Generating mesh...");

    dist_index * dist_data  = (dist_index *) malloc(num_stations * sizeof(dist_index));

    for (int i = 0; i < num_stations; i++)
        if(stations[i].data_valid)
        {
            float bx, by, bz;
        
            get_cart(stations[i].pos,  bx, by, bz);
/*
            // get distances    
            for (int j = 0; j < num_stations; j++)    
            {
                float px, py, pz;
        
                get_cart(stations[j].pos,  px, py, pz);
            
                float dist = (px-bx)*(px-bx) +  (py-by)*(py-by) +  (pz-bz)*(pz-bz); 


              //  console("pos :%f  %f x%f  y%f  z%f \n", stations[j].pos.lat, stations[j].pos.lon, 
                //        px,py,pz);


                    dist_data[j].index = j;
                if (stations[j].data_valid)
                {
                    dist_data[j].dist = dist;

            }
            else 
                    dist_data[j].dist = 100000;

            
            
            
            }

        // sort distances

        qsort (dist_data, num_stations, sizeof(dist_index), dist_compare);
    
        for (int j = 0; j < 10; j++)
        {
         //   console("index %d  dist: %f\n", dist_data[j].index, dist_data[j].dist);
        } 
    
    */
    
    double ep = 0.5;
    
        float cr, cg, cb;
    
        glBegin(GL_TRIANGLES);
  	
        //color_temp(stations[dist_data[0].index].data.temp,cr, cg, cb);

        color_temp(stations[i].data.temp,cr, cg, cb);

   	    glColor3f(cr, cg, cb);
  
  
   geo_pos tmp;
   
  
  tmp = stations[i].pos;
  
  
  
    tmp.lat = stations[i].pos.lat;
    tmp.lon = stations[i].pos.lon - ep;
  
  
     get_cart(tmp,  bx, by, bz);
  
      glVertex3f(bx, by, bz); 
  
  
        //add_geo_point( stations[dist_data[0].index].pos );

//        color_temp(stations[dist_data[2].index].data.temp,cr, cg, cb);
  

   	    glColor3f(cr, cg, cb);


  tmp.lat = stations[i].pos.lat + ep;
  tmp.lon = stations[i].pos.lon;
  
  
     get_cart(tmp,  bx, by, bz);
  



          color_temp(stations[i].data.temp,cr, cg, cb);
      glVertex3f(bx, by, bz); 

  
//      add_geo_point( stations[dist_data[2].index].pos );

       // color_temp(stations[dist_data[1].index].data.temp,cr, cg, cb);




  tmp.lat = stations[i].pos.lat - ep;
  tmp.lon = stations[i].pos.lon;
  
  
     get_cart(tmp,  bx, by, bz);
 
      glVertex3f(bx, by, bz); 
  

        color_temp(stations[i].data.temp,cr, cg, cb);
   	    glColor3f(cr, cg, cb);


//      glVertex3f(bx+ep, by+ep, bz); 
  
//      add_geo_point( stations[dist_data[1].index].pos );

       glEnd();
    
    }

    free(dist_data);
    
    console("done\n");
    
    
}

















void rotate(float &x, float &y, float ang)
{
    double xp = x * cos(ang) - y  * sin (ang);
    double yp = y * cos(ang) + x  * sin (ang);
    
    x = xp; y = yp;
}


void make_world( void )
{
    unsigned int lon_segs = 36;

    float lon_step = 360 / (float)lon_segs;

    geo_pos r; r.lat = 90;  r.lon = 0;
    
    
    float cc = 0.9;//((rand() % 1000)+100) / 2000.0;
        
    
    for (int i = 0; i < 18; i++)
    {
        glBegin(GL_QUAD_STRIP);
        
      	glColor3f(cc, cc, cc);

        for (int j = 0; j < lon_segs+1; j++, r.lon += lon_step)
        {
            add_lat_lon_point(r.lat, r.lon, 1);
            add_lat_lon_point(r.lat-10, r.lon, 1);
        }



        
        r.lat -= 10;
        glEnd();

    }

    
}



void opengl_class::setup_data( void )
{
    world_grid = glGenLists(1);	

    glNewList(world_grid, GL_COMPILE);	
  
    make_world();
  
    glColor3f(0,0.2,0.9);


    unsigned int latitude_rings = 20;

    float y = -1;  


    float ring_ang = 0;

    for (int i = 0; i < latitude_rings; i++)
    {
        glBegin(GL_LINE_LOOP);
   	
       	double t = 0;
   	
       	unsigned int points = 100;
   	

        double r = sqrt(1 - y*y);
  	
   	
       	for (int j = 0; j < points; j++)
       	{
            t += 2*M_PI / (double) points;

            float x = r*cos(t);
            float z = r*sin(t);
      
   	
            glVertex3f(x, y, z);

        }
    
    
        y += 2 / (double) latitude_rings;
    
    
        ring_ang +=  M_PI / (double) latitude_rings;

        glEnd( );
    }
    
    
    
       

    double y_ang = 0;
    unsigned int y_ang_slices = 36;


    for (int y_slice = 0; y_slice < y_ang_slices; y_slice++)
    {
        glBegin(GL_LINE_LOOP);
   	
 	
        double x_ang = 0;
        unsigned int x_ang_slices = 100;  	
   	
   	
       	char tmp[400];
       	sprintf(tmp, "y_ang %f", y_ang);
    //   	log(tmp);
   	
   	
       	for (int x_slice = 0; x_slice < x_ang_slices; x_slice++)
       	{
            
       	char tmp[400];
       //	sprintf(tmp, "   x_ang %f", x_ang);
       	//log(tmp);            
            
            
            float r = 1;

            float x = r*cos(x_ang);
            float y = r*sin(x_ang);

            float z = 0;//r*sin(y_ang);

            rotate( x, z, y_ang);


 
         	glColor3f(0,0.4,0.7);
   	
            glVertex3f(x, y, z);
            
            x_ang +=  2*M_PI / (double) x_ang_slices;

        }
    
      
        y_ang +=  2*M_PI / (double) y_ang_slices;
    
        glEnd( );
    
    }


    
   // plot_stations();


    generate_mesh ();

    
    glEndList();	
  
  
  
}

