#include "globals.h"

/*

		 http://www.lighthouse3d.com/opengl/terrain/index.php?tgasource
 		http://www.alsprogrammingresource.com/docs/particles/Docs/html/tga_8cpp-source.html
NOTA : L'option capture d'ecran est en commentaire pour eviter l'utilisation d'opengl

 */
#ifndef TGA_H
#define TGA_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

 enum
 {
 TGA_ERROR_FILE_OPEN,
 TGA_ERROR_READING_FILE,
 TGA_ERROR_INDEXED_COLOR,
 TGA_ERROR_MEMORY,
 TGA_ERROR_COMPRESSED_FILE,
 TGA_OK
 };

 int tgaSave(    char            *filename,
                 short int       width,
                 short int       height,
                 unsigned char   pixelDepth,
                 unsigned char   *imageData
             );

 int tgaSaveSeries
 (
 char *filename,
 short int width,
 short int height,
 unsigned char pixelDepth,
 unsigned char *imageData
 );

 typedef struct
 {
     int status;
     unsigned char type, pixelDepth;
     short int width, height;
     unsigned char *imageData;
 }
 tgaInfo;


  // this variable is used for image series
  static int savedImages=0;

  // load the image header fields. We only keep those that matter!
  void tgaLoadHeader(FILE *file, tgaInfo *info) {

          unsigned char cGarbage;
          short int iGarbage;

          fread(&cGarbage, sizeof(unsigned char), 1, file);
          fread(&cGarbage, sizeof(unsigned char), 1, file);

  // type must be 2 or 3
          fread(&info->type, sizeof(unsigned char), 1, file);

          fread(&iGarbage, sizeof(short int), 1, file);
          fread(&iGarbage, sizeof(short int), 1, file);
          fread(&cGarbage, sizeof(unsigned char), 1, file);
          fread(&iGarbage, sizeof(short int), 1, file);
          fread(&iGarbage, sizeof(short int), 1, file);

          fread(&info->width, sizeof(short int), 1, file);
          fread(&info->height, sizeof(short int), 1, file);
          fread(&info->pixelDepth, sizeof(unsigned char), 1, file);

          fread(&cGarbage, sizeof(unsigned char), 1, file);
  }

  // loads the image pixels. You shouldn't call this function
  // directly
  void tgaLoadImageData(FILE *file, tgaInfo *info) {

          int mode,total,i;
          unsigned char aux;

  // mode equal the number of components for each pixel
          mode = info->pixelDepth / 8;
  // total is the number of bytes we'll have to read
          total = info->height * info->width * mode;

          fread(info->imageData,sizeof(unsigned char),total,file);

  // mode=3 or 4 implies that the image is RGB(A). However TGA
  // stores it as BGR(A) so we'll have to swap R and B.
          if (mode >= 3)
                  for (i=0; i < total; i+= mode) {
                          aux = info->imageData[i];
                          info->imageData[i] = info->imageData[i+2];
                          info->imageData[i+2] = aux;
                  }
  }

  // this is the function to call when we want to load
  // an image
  tgaInfo * tgaLoad(char *filename) {

          FILE *file;
          tgaInfo *info;
          int mode,total;

  // allocate memory for the info struct and check!
          info = (tgaInfo *)malloc(sizeof(tgaInfo));
          if (info == NULL)
                  return(NULL);


  // open the file for reading (binary mode)
          file = fopen(filename, "rb");
          if (file == NULL) {
                  info->status = TGA_ERROR_FILE_OPEN;
                  return(info);
          }

  // load the header
          tgaLoadHeader(file,info);

  // check for errors when loading the header
          if (ferror(file)) {
                  info->status = TGA_ERROR_READING_FILE;
                  fclose(file);
                  return(info);
          }

  // check if the image is color indexed
          if (info->type == 1) {
                  info->status = TGA_ERROR_INDEXED_COLOR;
                  fclose(file);
                  return(info);
          }
  // check for other types (compressed images)
          if ((info->type != 2) && (info->type !=3)) {
                  info->status = TGA_ERROR_COMPRESSED_FILE;
                  fclose(file);
                  return(info);
          }

  // mode equals the number of image components
          mode = info->pixelDepth / 8;
  // total is the number of bytes to read
          total = info->height * info->width * mode;
  // allocate memory for image pixels
          info->imageData = (unsigned char *)malloc(sizeof(unsigned char) *
                                                                  total);

  // check to make sure we have the memory required
          if (info->imageData == NULL) {
                  info->status = TGA_ERROR_MEMORY;
                  fclose(file);
                  return(info);
          }
  // finally load the image pixels
          tgaLoadImageData(file,info);

  // check for errors when reading the pixels
          if (ferror(file)) {
                  info->status = TGA_ERROR_READING_FILE;
                  fclose(file);
                  return(info);
          }
          fclose(file);
          info->status = TGA_OK;
          return(info);
  }

  // converts RGB to greyscale
  void tgaRGBtogreyscale(tgaInfo *info) {

          int mode,i,j;

          unsigned char *newImageData;

  // if the image is already greyscale do nothing
          if (info->pixelDepth == 8)
                  return;

  // compute the number of actual components
          mode = info->pixelDepth / 8;

  // allocate an array for the new image data
          newImageData = (unsigned char *)malloc(sizeof(unsigned char) *
                                                  info->height * info->width);
          if (newImageData == NULL) {
                  return;
          }

  // convert pixels: greyscale = o.30 * R + 0.59 * G + 0.11 * B
          for (i = 0,j = 0; j < info->width * info->height; i +=mode, j++)
                  newImageData[j] =
                          (unsigned char)(0.30 * info->imageData[i] +
                                          0.59 * info->imageData[i+1] +
                                          0.11 * info->imageData[i+2]);


  //free old image data
          free(info->imageData);

  // reassign pixelDepth and type according to the new image type
          info->pixelDepth = 8;
          info->type = 3;
  // reassing imageData to the new array.
          info->imageData = newImageData;
  }
/*
  // takes a screen shot and saves it to a TGA image
  int tgaGrabScreenSeries(char *filename, int xmin,int ymin, int xmax, int ymax) {

          int w, h;
          unsigned char *imageData;

  // compute width and heidth of the image
          w = xmax - xmin;
          h = ymax - ymin;

  // allocate memory for the pixels
          imageData = (unsigned char *)malloc(sizeof(unsigned char) * w * h * 4);

  // read the pixels from the frame buffer
          glReadPixels(xmin,ymin,xmax,ymax,GL_RGBA,GL_UNSIGNED_BYTE, (void *)imageData);

  // save the image
          return (tgaSaveSeries(filename,(short int)w,(short int)h,32,imageData));
  }

*/


  // saves an array of pixels as a TGA image
  int tgaSave(    char            *filename,
                  short int       width,
                  short int       height,
                  unsigned char   pixelDepth,
                  unsigned char   *imageData) {

          unsigned char cGarbage = 0, type,mode,aux;
          short int iGarbage = 0;
          int i;
          FILE *file;

  // open file and check for errors
          file = fopen(filename, "wb");
          if (file == NULL) {
                  return(TGA_ERROR_FILE_OPEN);
          }

  // compute image type: 2 for RGB(A), 3 for greyscale
          mode = (unsigned char)(pixelDepth / 8);
          if ((pixelDepth == 24) || (pixelDepth == 32))
                  type = 2;
          else
                  type = 3;

  // write the header
          fwrite(&cGarbage, sizeof(unsigned char), 1, file);
          fwrite(&cGarbage, sizeof(unsigned char), 1, file);

          fwrite(&type, sizeof(unsigned char), 1, file);

          fwrite(&iGarbage, sizeof(short int), 1, file);
          fwrite(&iGarbage, sizeof(short int), 1, file);
          fwrite(&cGarbage, sizeof(unsigned char), 1, file);
          fwrite(&iGarbage, sizeof(short int), 1, file);
          fwrite(&iGarbage, sizeof(short int), 1, file);

          fwrite(&width, sizeof(short int), 1, file);
          fwrite(&height, sizeof(short int), 1, file);
          fwrite(&pixelDepth, sizeof(unsigned char), 1, file);

          fwrite(&cGarbage, sizeof(unsigned char), 1, file);

  // convert the image data from RGB(a) to BGR(A)
          if (mode >= 3)
          for (i=0; i < width * height * mode ; i+= mode) {
                  aux = imageData[i];
                  imageData[i] = imageData[i+2];
                  imageData[i+2] = aux;
          }

  // save the image data
          fwrite(imageData, sizeof(unsigned char),
                          width * height * mode, file);
          fclose(file);
  // release the memory
         // free(imageData);

          return(TGA_OK);
  }

  // saves a series of files with names "filenameX.tga"
  int tgaSaveSeries(char          *filename,
                           short int              width,
                           short int              height,
                           unsigned char  pixelDepth,
                           unsigned char  *imageData) {

          char *newFilename;
          int status;

  // compute the new filename by adding the
  // series number and the extension
          newFilename = (char *)malloc(sizeof(char) * strlen(filename)+8);

          sprintf(newFilename,"%s%d.tga",filename,savedImages);

  // save the image
          status = tgaSave(newFilename,width,height,pixelDepth,imageData);

  //increase the counter
          savedImages++;
          return(status);
  }


  // releases the memory used for the image
  void tgaDestroy(tgaInfo *info) {

          if (info != NULL) {
                  free(info->imageData);
                  free(info);
          }
  }

#endif




Fl_Window *form;
Fl_Value_Slider  *zenit,*dist,*azimut;
Fl_Light_Button *light_button;
Fl_Button *export_button;
Fl_Button *about_button;
Render_Window *render_window1, *render_window2;
Camera *camera=new Camera;
Fl_Hold_Browser *LBA1mapselect;
Fl_Hold_Browser *LBA2mapselect;
int mousex1,mousey1,mousex2,mousey2,mousedx,mousedy;//420 580

void LBA1mapselect_callback(Fl_Widget* o, void*)
{
//    printf("loading LBA1 map %d\n",((Fl_Browser*)o)->value()-1);
//    delete render_window1->lba_map_gl;
//    delete render_window2->lba_map_gl;
    render_window1->change_map_flag=true;
    render_window2->change_map_flag=true;
    render_window1->LBA2_flag=false;
    render_window2->LBA2_flag=false;
    render_window1->NUM_MAP=(int)(((Fl_Browser*)o)->value()-1);
    render_window2->NUM_MAP=(int)(((Fl_Browser*)o)->value()-1);
//
//    lba_map_gl=new LBA_MAP_GL((int)((Fl_Browser*)o)->value()-1,0);
//    render_window2->lba_map_gl=new LBA_MAP_GL((int)((Fl_Browser*)o)->value()-1,0);

    strcpy(render_window1->map_name,((Fl_Browser*)o)->text(render_window1->NUM_MAP+1));
    strcpy(render_window2->map_name,((Fl_Browser*)o)->text(render_window2->NUM_MAP+1));
    camera->targetx=32;
    camera->targetz=32;
//    puts("map loaded");
}

void LBA2mapselect_callback(Fl_Widget* o, void*)
{
    render_window1->change_map_flag=true;
    render_window2->change_map_flag=true;
    render_window1->LBA2_flag=true;
    render_window2->LBA2_flag=true;
    render_window1->NUM_MAP=(int)(((Fl_Browser*)o)->value());
    render_window2->NUM_MAP=(int)(((Fl_Browser*)o)->value());
    strcpy(render_window1->map_name,((Fl_Browser*)o)->text(render_window1->NUM_MAP));
    strcpy(render_window2->map_name,((Fl_Browser*)o)->text(render_window2->NUM_MAP));
    camera->targetx=32;
    camera->targetz=32;
/*
    printf("loading LBA2 map %d\n",((Fl_Browser*)o)->value()-1);
    puts("deleting previous...");
    delete render_window1->lba_map_gl;
    delete render_window2->lba_map_gl;
    puts("creating new one...");
    render_window1->lba_map_gl=new LBA_MAP_GL((int)((Fl_Browser*)o)->value(),1);
    render_window2->lba_map_gl=new LBA_MAP_GL((int)((Fl_Browser*)o)->value(),1);
    camera->targetx=32;
    camera->targetz=32;
    puts("map loaded");*/
}

void light_button_callback(Fl_Widget* o, void*)
{
    render_window1->has_light=((Fl_Light_Button*)o)->value();
}

void about_button_callback(Fl_Widget* o, void*)
{
    fl_message(
"Perspective LBA Map Viewer\n\
david38 - The Magic Ball Network\n\
http://www.magicball.net\n\
From an original idea of RobG\n\n\
Controls : \n\
Click & Drag the mouse in either of two views to control the camera\n\
Left button is to navigate\n\
Right button is to zoom\n\
When mouse cursor is inside a view, you can also use arrow keys to navigate\n\n\
Project is built with Mingw compiler, using the IDE Code::Blocks\n\
It uses FLTK and OPENGL libraries\n\n");
}

void export_button_callback(Fl_Widget* o, void*)
{

        char current_directory[1000];
        char file_tga[1000];
        char file_obj[1000];
        char file_mtl[1000];
        sprintf(file_obj,"%s.obj",render_window1->map_name);
        sprintf(file_mtl,"%s.mtl",render_window1->map_name);
        sprintf(file_tga,"%s.tga",render_window1->map_name);
        getcwd(current_directory,1000);

    FILE *f=fopen(file_obj,"wb");
    fprintf(f,"o lba_map_mesh\n");
    fprintf(f,"mtllib %s\n",file_mtl);
    fprintf(f,"g default\n");
    for(int i=0;i<render_window1->lba_map_gl->lba_face.size();i++)
    {
        fprintf(f,"v %f %f %f\n",   render_window1->lba_map_gl->lba_face[i].v[0],
                                    render_window1->lba_map_gl->lba_face[i].v[1],
                                    render_window1->lba_map_gl->lba_face[i].v[2]);

        fprintf(f,"v %f %f %f\n",   render_window1->lba_map_gl->lba_face[i].v[3],
                                    render_window1->lba_map_gl->lba_face[i].v[4],
                                    render_window1->lba_map_gl->lba_face[i].v[5]);

        fprintf(f,"v %f %f %f\n",   render_window1->lba_map_gl->lba_face[i].v[6],
                                    render_window1->lba_map_gl->lba_face[i].v[7],
                                    render_window1->lba_map_gl->lba_face[i].v[8]);
    }

    for(int i=0;i<render_window1->lba_map_gl->lba_face.size();i++)
    {
        fprintf(f,"vt %f %f\n", render_window1->lba_map_gl->lba_face[i].vt[0],
                                render_window1->lba_map_gl->lba_face[i].vt[1]);

        fprintf(f,"vt %f %f\n", render_window1->lba_map_gl->lba_face[i].vt[2],
                                render_window1->lba_map_gl->lba_face[i].vt[3]);

        fprintf(f,"vt %f %f\n", render_window1->lba_map_gl->lba_face[i].vt[4],
                                render_window1->lba_map_gl->lba_face[i].vt[5]);
    }

    for(int i=0;i<render_window1->lba_map_gl->lba_face.size();i++)
    {
        fprintf(f,"vn %f %f %f\n",  render_window1->lba_map_gl->lba_face[i].vn[0],
                                    render_window1->lba_map_gl->lba_face[i].vn[1],
                                    render_window1->lba_map_gl->lba_face[i].vn[2]);

        fprintf(f,"vn %f %f %f\n",  render_window1->lba_map_gl->lba_face[i].vn[0],
                                    render_window1->lba_map_gl->lba_face[i].vn[1],
                                    render_window1->lba_map_gl->lba_face[i].vn[2]);

        fprintf(f,"vn %f %f %f\n",  render_window1->lba_map_gl->lba_face[i].vn[0],
                                    render_window1->lba_map_gl->lba_face[i].vn[1],
                                    render_window1->lba_map_gl->lba_face[i].vn[2]);
    }
    fprintf(f,"usemtl lba_map_texture\n");
    fprintf(f,"s 1\n");
    for(int i=0;i<render_window1->lba_map_gl->lba_face.size();i++)
        fprintf(f,"f %d/%d/%d %d/%d/%d %d/%d/%d\n",i*3+1,i*3+1,i*3+1,i*3+2,i*3+2,i*3+2,i*3+3,i*3+3,i*3+3);

fclose(f);
f=fopen(file_mtl,"wb");
fprintf(f,"newmtl lba_map_texture\n");
fprintf(f,"Ka 0 0 0\n");
fprintf(f,"Kd 1 1 1\n");
fprintf(f,"Ks 0 0 0\n");
fprintf(f,"Ni 1\n");
fprintf(f,"Ns 400\n");
fprintf(f,"Tf 1 1 1\n");
fprintf(f,"d 1\n");
fprintf(f,"map_Kd %s",file_tga);
fclose(f);
    tgaSave(file_tga,2048,2048,32,render_window1->lba_map_gl->texture_map);



    fl_message("Map has been exorted to :\n%s\\%s.obj\n%s\\%s.mtl\n%s\\%s.tga",current_directory,render_window1->map_name,current_directory,render_window1->map_name,current_directory,render_window1->map_name);


//    render_window1->lba_map_gl
    //render_window1->has_light=((Fl_Light_Button*)o)->value();
}


int main(int argc, char** argv)
{
//  glutInit(&argc, argv); // this line removed for FLTK

  camera->distance=50;
  camera->fov=45;
  camera->targetx=32;
  camera->targetz=32;
  camera->size=80.;

  form = new Fl_Window(1000,700,"test");
  dist = new Fl_Value_Slider(430,70,40,260,"PRSP");
  zenit = new Fl_Value_Slider(480,70,40,260,"ZNIT");
  azimut = new Fl_Value_Slider(530,70,40,260,"AZMT");


  light_button = new Fl_Light_Button(420,20,160,30,"Light/Shadow");
  export_button = new Fl_Button(420,350,160,30,"OBJ Export");
  about_button = new Fl_Button(420,390,160,30,"Help/About");
  LBA1mapselect=new Fl_Hold_Browser(20,440,470,240);
  LBA2mapselect=new Fl_Hold_Browser(510,440,470,240);
  light_button->callback(light_button_callback);
  export_button->callback(export_button_callback);
  about_button->callback(about_button_callback);


  FILE *f=fopen("data//map//lba1//names.txt","rb");
    for(int i=0;i<134;i++)
    {
    char name[100];
    int cursor=0;
    char c;
    while(1)
    {
        c=fgetc(f);
        if(c==13)
        {
            name[cursor]=0;
            fgetc(f);
            break;
        }
        else
        {
            name[cursor]=c;
            cursor++;
        }
    }
        LBA1mapselect->add(name);
    }
    LBA1mapselect->callback(LBA1mapselect_callback);
    LBA2mapselect->callback(LBA2mapselect_callback);

    fclose(f);
    f=fopen("data//map//lba2//names.txt","rb");
    for(int i=0;i<148;i++)
    {
        char name[100];
        int cursor=0;
        char c;
        while(1)
        {
            c=fgetc(f);
            if(c==13)
            {
                name[cursor]=0;
                fgetc(f);
                break;
            }
            else
            {
                name[cursor]=c;
                cursor++;
            }
        }
        LBA2mapselect->add(name);
    }
    LBA2mapselect->position(0);

    fclose(f);
    render_window1=new Render_Window(camera,20,20,400,400,"");
    render_window2=new Render_Window(camera,580,20,400,400,"");
    render_window1->has_texture=true;
    render_window2->has_texture=false;
    render_window1->has_light=false;
    render_window2->has_light=true;




    form->end();
    dist->scrollvalue(100,0,1,449);
//    zenit->scrollvalue(30,0,10,70);
//    azimut->scrollvalue(0,0,-45,90);

    zenit->scrollvalue(34,0,-180,360);
    azimut->scrollvalue(0,0,-180,360);

    form->label("Perspective LBA Map Viewer");
    form->show(argc,argv);

    render_window2->show();
    while (Fl::wait())
    {
    puts("main loop");
    mousex1=mousex2;
    mousey1=mousey2;
    mousex2=Fl::event_x();
    mousey2=Fl::event_y();
    mousedx=mousex2-mousex1;
    mousedy=mousey2-mousey1;
    camera->mouse_dx=mousedx;
    camera->mouse_dy=mousedy;
    camera->distance=dist->value();
    camera->fov=atan(camera->size/2./camera->distance)*180./M_PI;
    camera->zenit=zenit->value();
    camera->azimut=azimut->value();
};



  return 0;
}
