#include "model_radius.h"

int Q_filelength (FILE *f)
{
	int		pos;
	int		end;

	pos = ftell (f);
	fseek (f, 0, SEEK_END);
	end = ftell (f);
	fseek (f, pos, SEEK_SET);

	return end;
}

int Frame_ModifyRadius(int frame_num, md3Frame_t *frame, float newRadius) {
   if(frame) {
      printf("  frame %d, %s: old radius: %f, ", frame_num, frame->name, frame->radius);
      frame->radius = newRadius;
      printf("new radius: %f\n", frame->radius);
      
      return (frame->radius == newRadius);
   }
   printf("ERROR: invalid frame pointer in Frame_ModifyRadius\n");
   return 0;   
}

int Frames_Next(md3Model_t *model, int frame_num) {
   if(model) {
      /*
      int i, j, k, l, m;
      
      // read bounds bytes
      char *dest = (char*)&(model->frames[frame_num].bounds);
      for(i = 0; i < sizeof(vec3_t) * 2; i++) {
         dest[i] = model->buffer[i];
      }

      // read localOrigin bytes
      dest = (char*)&(model->frames[frame_num].localOrigin);
      for(j = i; j < i + sizeof(vec3_t); j++) {
         dest[j] = model->buffer[j];
      }

      // read radius bytes
      dest = (char*)&(model->frames[frame_num].radius);
      for(k = j; k < j + sizeof(float); k++) {
         dest[k] = model->buffer[k];
      }

      // read name bytes
      dest = (char*)&(model->frames[frame_num].name);
      for(l = k, m = k; l < k + 16; l++) {
         printf("%c", model->buffer[m]);
         if(model->buffer[m] != '\0') {
            dest[m] = model->buffer[m];
            m++;
         } else {
            break;
         }
      }
      dest[m] = '\0';
      
      model->buffer += m;
      
      //strcpy(dest, &(model->buffer[k+1]));
      //model->buffer += k + strlen(dest);
      */

      /*
      fread(&(model->frames->bounds), sizeof(vec3_t), 2, fp);
      fread(&(model->frames->localOrigin), sizeof(vec3_t), 1, fp);
      fread(&(model->frames->radius), sizeof(float), 1, fp);
      while(getc(fp) != '\0') {
         model->frames->name = fp;
      }
      */

      fread(&(model->frames[frame_num]), sizeof(md3Frame_t), 1, model->input);

      return 1;
   }
   printf("ERROR: invalid model pointer in Frames_Next\n");
   return 0;
}

int Frames_Modify(md3Model_t *model, md3Frame_t *newFrame) {
   if(model && newFrame) {
      int i;
      for(i = 0; i < model->header.numFrames; i++) {
         Frame_ModifyRadius( i, &(model->frames[i]), newFrame->radius);
         // other modifications
         //model->frames += sizeof(md3Frame_t);
         //Frames_Next(model, i);
      }
      return 1;
   }
   printf("ERROR: invalid model or frame pointers in Frames_Modify\n");
   return 0;
}

int Model_Init(md3Model_t *model) {
   if(model) {
      model->buffer = NULL;
      
      model->frames = NULL;
      model->tags = NULL;
      model->surfaces = NULL;

      model->input = NULL;
      model->output = NULL;

      return 1;
   }
   printf("ERROR: invalid model pointer in Model_Init\n");
   return 0;
}

int Model_Destroy(md3Model_t *model) {
   if(model) {
      if(model->buffer) 
         free(model->buffer);
      
      if(model->frames)
         free(model->frames);
      if(model->tags)
         free(model->tags);
      if(model->surfaces)
         free(model->surfaces);

      if(model->input)
         fclose(model->input);
      if(model->output)
         fclose(model->output);

      return 1;
   }
   return 0;
}

int Model_Read(char *filename, md3Model_t *model) {
   model->buffer = NULL;
   if(filename && model) {
      model->input = fopen(filename, "rb");
   
      if(model->input) {
         // read in header
         fread(&(model->header), sizeof(md3Header_t), 1, model->input);

         printf("version: %d\n", model->header.version);
         
         // FIXME: do we even need this anymore?
         // allocate buffer for file
         model->input_len = Q_filelength(model->input);
         model->buffer = (char*)malloc(model->input_len);
         fread(model->buffer, sizeof(char), model->input_len, model->input);
         
         // seek to the beginning of the file
         fseek(model->input, 0L, SEEK_SET);

         // allocate frames
         model->frames = (md3Frame_t*)malloc(sizeof(md3Frame_t) * model->header.numFrames);
         // seek to the frames offset and read them
         fseek(model->input, model->header.ofsFrames, SEEK_SET);
         fread(model->frames, sizeof(md3Frame_t), model->header.numFrames, model->input);

         // seek to the beginning of the file
         //fseek(model->input, 0L, SEEK_SET);

         // everything after this is wrong!

         // allocate tags
         model->tags = (md3Tag_t*)malloc(sizeof(md3Tag_t) * (model->header.numFrames * model->header.numTags));
         // seek to tags offset and read them
         fseek(model->input, model->header.ofsTags, SEEK_SET);
         fread(model->tags, sizeof(md3Tag_t), (model->header.numFrames * model->header.numTags), model->input);

         // seek to the beginning of the file
         fseek(model->input, 0L, SEEK_SET);

         /////////
         // new
         /////////
         // allocate surfaces
         
         printf("numSurfaces: %d\n", model->header.numSurfaces);
         
         model->surfaces = (md3Surface_t*)malloc(sizeof(md3Surface_t) * model->header.numSurfaces);
         
         // read first surface, then procedurally after that...
         fseek(model->input, model->header.ofsSurfaces, SEEK_SET);
         fread(model->surfaces, sizeof(md3Surface_t), 1, model->input);
         
         model->stats.numShaders = model->surfaces[0].numShaders;
         model->stats.numTriangles = model->surfaces[0].numTriangles;
         model->stats.numTexcoords = model->surfaces[0].numVerts;
         model->stats.numVerts = model->surfaces[0].numVerts;

         printf("numShaders: %d\n", model->surfaces[0].numShaders);
         printf("numTriangles: %d\n", model->surfaces[0].numTriangles);
         printf("numTexcoords: %d\n", model->surfaces[0].numVerts);
         printf("numVerts: %d\n\n", model->surfaces[0].numVerts);

         int offset = model->header.ofsSurfaces;
         if(model->header.numSurfaces > 1)
         
         for(int i = 1; i < model->header.numSurfaces; i++) {
            //offset += (model->surfaces[i-1].ofsEnd + 1);
            offset += sizeof(md3Surface_t);
            fseek(model->input, offset, SEEK_SET);
            
            fread(&(model->surfaces[i]), sizeof(md3Surface_t), 1, model->input);
         
            model->stats.numShaders += model->surfaces[i].numShaders;
            model->stats.numTriangles += model->surfaces[i].numTriangles;
            model->stats.numTexcoords += model->surfaces[i].numVerts;
            model->stats.numVerts += model->surfaces[i].numVerts;

            printf("numShaders: %d\n", model->surfaces[i].numShaders);
            printf("numTriangles: %d\n", model->surfaces[i].numTriangles);
            printf("numTexcoords: %d\n", model->surfaces[i].numVerts);
            printf("numVerts: %d\n\n", model->surfaces[i].numVerts);
         }
         
         /////////
         // old
         /////////

         /*
         // allocate surfaces
         model->surfaces = (md3Surface_t*)malloc(sizeof(md3Surface_t) * (model->header.numFrames * model->header.numSurfaces));
         // seek to surface offset and read them
         fseek(model->input, model->header.ofsSurfaces, SEEK_SET);
         fread(model->surfaces, sizeof(md3Surface_t), (model->header.numFrames * model->header.numSurfaces), model->input);
         
         // allocate shaders
         model->shaders = (md3Shader_t*)malloc(sizeof(md3Shader_t) * (model->surfaces->numShaders * model->header.numSurfaces));
         fseek(model->input, model->surfaces->ofsShaders, SEEK_SET);
         fread(model->shaders, sizeof(md3Shader_t), (model->surfaces->numShaders * model->header.numSurfaces), model->input);

         
         // allocate triangles
         int numTris = 0;
         for(int i = 0; i < model->header.numSurfaces; i++) {
            //printf("num tris: %d\n", model->surfaces[i].numTriangles);
            numTris += model->surfaces[i].numTriangles;
         }
         //printf("total num tris: %d\n", numTris);
         model->triangles = (md3Triangle_t*)malloc(sizeof(md3Triangle_t) * numTris);
         fseek(model->input, model->surfaces->ofsTriangles, SEEK_SET);
         fread(model->triangles, sizeof(md3Triangle_t), numTris, model->input);
         
         
         // allocate texture coords (st)
         int numSt = 0;
         for(int i = 0; i < model->header.numSurfaces; i++) {
            //printf("num texcoords: %d\n", model->surfaces[i].numVerts);
            numSt += model->surfaces[i].numVerts;
         }
         //printf("total num texcoords: %d\n", numSt);
         model->texcoords = (md3St_t*)malloc(sizeof(md3St_t) * numSt);
         fseek(model->input, model->surfaces->ofsSt, SEEK_SET);
         fread(model->texcoords, sizeof(md3St_t), numSt, model->input);

         // allocate xyz normals (verts)
         int numVerts = 0;
         for(int i = 0; i < model->header.numSurfaces; i++) {
            //printf("num verts: %d\n", model->surfaces[i].numVerts);
            numVerts += model->surfaces[i].numVerts;
         }
         //printf("total num verts: %d\n", numSt);
         model->verts = (md3XyzNormal_t*)malloc(sizeof(md3XyzNormal_t) * numVerts);
         fseek(model->input, model->surfaces->ofsXyzNormals, SEEK_SET);
         fread(model->verts, sizeof(md3XyzNormal_t), numVerts, model->input);
         */

         return 1;
      }
   }
   printf("ERROR: invalid model pointer or filename in Model_Read\n");
   return 0;
}

int Model_Write(char *filename, md3Model_t *model) {
   if(filename && model) {
      model->output = fopen(filename, "wb");
      if(model->output) {
         // confirm header offsets?
         ;

         // write header
         fwrite(&(model->header), 1, sizeof(md3Header_t), model->output);

         // write frames
         fseek(model->output, sizeof(md3Header_t), SEEK_SET);
         fwrite(model->frames, 1, model->header.numFrames * sizeof(md3Frame_t), model->output);

         // everything after this is wrong!

         // write tags
         fseek(model->output, model->header.ofsTags, SEEK_SET);
         fwrite(model->tags, 1, sizeof(md3Tag_t) * (model->header.numFrames * model->header.numTags), model->output);

         // write surfaces
         fseek(model->output, model->header.ofsSurfaces, SEEK_SET);
         fwrite(model->surfaces, 1, sizeof(md3Surface_t) * (model->header.numFrames * model->header.numSurfaces), model->output);

         int numShaders = 0, numTris = 0, numSt = 0, numVerts = 0;
         for(int i = 0; i < model->header.numSurfaces; i++) {
            numShaders += model->surfaces[i].numShaders;
            numTris += model->surfaces[i].numTriangles;
            numSt += model->surfaces[i].numVerts;
            numVerts += model->surfaces[i].numVerts;
         }
         
         
         // shaders
         fseek(model->output, model->surfaces[0].ofsShaders, SEEK_SET);
         fwrite(model->shaders, 1, sizeof(md3Shader_t) * numShaders, model->output);

         // triangles
         fseek(model->output, model->surfaces[0].ofsShaders, SEEK_SET);
         fwrite(model->triangles, 1, sizeof(md3Triangle_t) * numTris, model->output);

         
         // texture coords
         fseek(model->output, model->surfaces[0].ofsSt, SEEK_SET);
         fwrite(model->texcoords, 1, sizeof(md3St_t) * numSt, model->output);

         
         // xyznorms (vertices)
         fseek(model->output, model->surfaces[0].ofsXyzNormals, SEEK_SET);
         fwrite(model->verts, 1, sizeof(md3XyzNormal_t) * numVerts * model->header.numFrames, model->output);
         for(int j = 0; j < model->surfaces[0].numVerts; j++) {
            printf("vert: %d, %d, %d: %d\n", model->verts[j].xyz[0], model->verts[j].xyz[1], model->verts[j].xyz[2], model->verts[j].normal);
         }

         // close output file
         fclose(model->output);

         return 1;
      }
      printf("ERROR: couldnt open output file: %s in Model_Write\n", filename);
   }
   printf("ERROR: invalid model pointer or filename in Model_Write\n");
   return 0;
}

int Model_Modify(md3Model_t *model, commandlineArgs_t *args) {
   if(model) {
      md3Frame_t frame;
      frame.radius = args->radius;
      return Frames_Modify(model, &frame);
   }
   printf("ERROR: invalid model pointer Model_Modify\n");
   return 0;
}

// over complicated and doesnt work because of the way im reading and the way different exporters structure md3 files
int old_main(int argc, char **argv) {
   commandlineArgs_t args;
   md3Model_t model;
   
   // dont proceed if there are bad parameters
   if(!argv[0] || !argv[1] || !argv[2] || !argv[3]) {
      printf("model_radius 1.0:\n");
      printf(" - changes the radius value for each frame of animation\n\n");
      printf(" USAGE:\n");
      printf("   model_radius <input md3 filename> <output md3 filename> <new radius value>\n\n");
      printf(" EXAMPLE:\n");
      printf("   model_radius stardestroyer.md3 sd.md3 1024\n");
      printf("    - modifies radius and outputs sd.md3\n\n");
      printf("   model_radius tiefighter.md3 same 128\n");
      printf("    - modifies radius and outputs tiefighter.md3\n");
      return 0;
   }

   // read in commandline arguments
   strcpy((char*)&(args.input_name), argv[1]);
   strcpy((char*)&(args.output_name), argv[2]);
   args.radius = atof(argv[3]);

   printf("%s, %s, %f\n", args.input_name, args.output_name, args.radius);

   // initiailize model structure
   Model_Init(&model);

   // input model from file
   Model_Read(args.input_name, &model);
   
   // apply model manipulations based on commandline params
   Model_Modify(&model, &args);
   
   // write modified model to output file
   if(strcmp(args.output_name, "same") == 0) {
      Model_Write(args.input_name, &model);
   } else {
      Model_Write(args.output_name, &model);
   }

   // deallocate and destroy model structure
   Model_Destroy(&model);

   /*

   // open the file
   printf("attempting to open %s\n", args.input_name);

   fp = fopen(args.input_name, "rb");

   // if the file was opened successfully
   if(fp) {
      md3Model_t model;

      printf("opened %s correctly\n", argv[1]);

      // read in header
      fread(&(model.header), sizeof(md3Header_t), 1, fp);

      printf("frames: %d\n", model.header.numFrames);

      if(argv[2]) {
         long file_len;
         char *buffer;
         md3Frame_t *frame;
         float radius = atof(argv[2]);

         if(radius < 0)
            radius = 0;

         printf("  setting new radius value %f\n", radius);

         // allocate buffer for file
         file_len = Q_filelength(fp);
         //buffer = new char[file_len];
         buffer = malloc(file_len);
         fread(buffer, sizeof(char), file_len, fp);

         // skip to first frame
         buffer += model.header.ofsFrames;

         // go through frames, changing the radius value
         frame = NULL;
         for(int i = 0; i < model.header.numFrames; i++) {
            frame = (md3Frame_t*)buffer;

            printf("  frame %d: old radius: %f, ", i, frame->radius);

            frame->radius = radius;

            printf("new radius: %f\n", frame->radius);

            buffer += sizeof(md3Frame_t);
         }

         // cleanup
         md3Frame = NULL;
         free(buffer);
      }

      // cleanup...
      fclose(fp);
   }
   */

   //getch();
}

int main(int argc, char **argv) {
   FILE *fp;
   char *buffer, *ptr;
   int buffer_len;
   char output_filename[MAX_QPATH];
   int i;

   md3Header_t *header;

   if(argv[1] && argv[2] && argv[3]) {
      fp = fopen(argv[1], "rb");
      if(fp) {
         buffer_len = Q_filelength(fp);

         if(buffer_len) {
            buffer = new char[buffer_len];
            fread(buffer, 1, buffer_len, fp);
            fclose(fp);
            
            header = (md3Header_t*)buffer;         
            ptr = buffer;

            for(i = 0, ptr += header->ofsFrames; i < header->numFrames; i++, ptr += sizeof(md3Frame_t)) {
               printf("old radius: %f, ", ((md3Frame_t*)ptr)->radius);
               ((md3Frame_t*)ptr)->radius = atof(argv[3]);
               printf("new radius: %f\n", ((md3Frame_t*)ptr)->radius);
            }

            if(strcmp("same", argv[2]) == 0) {
               strcpy((char *)output_filename, argv[1]);
            } else {
               strcpy((char *)output_filename, argv[2]);
            }

            fp = fopen((const char *)output_filename, "wb");
            printf("new radius: %d\n", buffer_len);
            fwrite((void *)buffer, 1, buffer_len, fp);
            printf("got here\n");
            fclose(fp);

            ptr = NULL;
            delete [] buffer;
         }
      }
   } else {
      // dont proceed if there are bad parameters
      printf("model_radius 1.0:\n");
      printf(" - changes the radius value for each frame of animation\n\n");
      printf(" USAGE:\n");
      printf("   model_radius <input md3 filename> <output md3 filename> <new radius value>\n\n");
      printf(" EXAMPLE:\n");
      printf("   model_radius stardestroyer.md3 sd.md3 1024\n");
      printf("    - modifies radius and outputs sd.md3\n\n");
      printf("   model_radius tiefighter.md3 same 128\n");
      printf("    - modifies radius and outputs tiefighter.md3\n");
   }
   return 0;
}