/* Texture Paint - a GIMP plugin
 *
 * Copyright (C) 1998  Uwe Maurer <uwe_maurer@t-online.de>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include "texturepaint.h"
#include "model.h"
#include "q1pal.h"

#define MDL_VERSION	6

/*
	MDL (Quake I) Model
*/

typedef struct
{
	gint32 ident;
	gint32 version;
	vec3_t scale;
	vec3_t origin;
	gfloat radius;
	vec3_t offsets;
	gint32 num_skins;
	gint32 skin_width;
	gint32 skin_height;
	gint32 num_verts;
	gint32 num_tris;
	gint32 num_frames;
	gint32 sync_type;
	gint32 flags;
	gfloat size;
} Model1Header;

typedef struct
{
	gint32 onseam;
	gint32 s;
	gint32 t;
} stvert_t;

typedef  struct
{
	gint32 facesfront;
	gint32 v[3];
} mdl_triangle_t;

typedef struct
{
	unsigned char p[3];
	unsigned char  normal;
} vertex_t;

typedef struct
{
	vertex_t min,max;
	char name[16];
} frame_t;

static void destroy(Model *mdl)
{
	g_free(mdl->tri);
	g_free(mdl->vertex);
	g_free(mdl->frames);
	g_free(mdl);
}

static void draw(Model *mdl,int frame,int nextframe,float interp)
{
	int i,j,v;
	vec3_t *vertex;
	
	gfloat x,y,z;

	
	frame= (frame % mdl->numframes)*mdl->num_verts;
	nextframe= (nextframe % mdl->numframes)*mdl->num_verts;

	vertex=mdl->vertex;
	

	glBegin(GL_TRIANGLES);

	for (i=0;i<mdl->num_tris;i++)
	{
		for (j=0;j<3;j++)
		{
			v=mdl->tri[i].v[j];	
			
			x= vertex[frame+v].x +
				(vertex[nextframe+v].x-vertex[frame+v].x)*interp;			
			y= vertex[frame+v].y +
				(vertex[nextframe+v].y-vertex[frame+v].y)*interp;			
			z= vertex[frame+v].z +
				(vertex[nextframe+v].z-vertex[frame+v].z)*interp;			
			glTexCoord2fv(mdl->tri[i].tex[j]);
			glVertex3f(x,y,z);
		}
	}

	glEnd();
}


Model *Model1Load(char *name,FILE *fp)
{
	Model1Header header;
	Model *mdl;
	int i,k,f;
	gint32 dummy;
	guchar *texture;
	gint size;
	gint32 image_id,layer_id;
	GimpPixelRgn rgn;
	GimpDrawable *drawable;
	gint w,h;
	stvert_t *stvert;
	mdl_triangle_t *triangle;
	vertex_t *vertex;
	frame_t frame;
	gfloat x,y,z;
	gint v;
	gfloat xmin,xmax,ymax,ymin,zmax,zmin,scale;
	char filename[300];

	fread(&header,sizeof(header),1,fp);	
	if (strncmp((char *)&header.ident,"IDPO",4)!=0) return NULL;
	if (header.version!=MDL_VERSION) return NULL;
	mdl=g_malloc(sizeof(*mdl));
	memset(mdl,0,sizeof(*mdl));

	mdl->num_tris=header.num_tris;
	mdl->num_verts=header.num_verts;
		
	w=header.skin_width;
	h=header.skin_height;
	size=w*h;
	texture=g_malloc(size);

	image_id=-1;

	for (i=0;i<header.num_skins;i++)
	{
		fread(&dummy,sizeof(dummy),1,fp);
		fread(texture,size,1,fp);	
		image_id=gimp_image_new(w,h,GIMP_INDEXED);

		if (i==0)
		{
			strcpy(filename,name);		
		}
		else
		{
			sprintf(filename,"%s%i",name,i);
		}
		gimp_image_set_filename(image_id,filename);

		layer_id=gimp_layer_new(image_id,"skin",w,h,GIMP_INDEXED_IMAGE,100,GIMP_NORMAL_MODE);
		gimp_image_add_layer(image_id,layer_id,1);

		gimp_image_set_cmap(image_id,q1pal,256);
		drawable=gimp_drawable_get(layer_id);

		gimp_pixel_rgn_init(&rgn,drawable,0,0,w,h,TRUE,FALSE);
		gimp_pixel_rgn_set_rect(&rgn,texture,0,0,w,h);

		gimp_drawable_detach(drawable);
		gimp_display_new(image_id);	
		gimp_image_clean_all(image_id);
	}	
	gimp_displays_flush();
	g_free(texture);
	update_images_menu(image_id);	

	size=header.num_verts*sizeof(stvert_t);
	stvert=g_malloc(size);
	fread(stvert,size,1,fp);

	size=header.num_tris*sizeof(mdl_triangle_t);
	triangle=g_malloc(size);
	fread(triangle,size,1,fp);

	mdl->tri=g_malloc(header.num_tris*sizeof(triangle_t));
	mdl->vertex=g_malloc(header.num_frames*header.num_verts*sizeof(mdl->vertex[0]));
	
	size=header.num_verts*sizeof(vertex_t);
	vertex=g_malloc(size);

	mdl->frames=g_malloc(header.num_frames*sizeof(mdl->frames[0]));
	mdl->numframes=header.num_frames;

	xmax=-G_MAXFLOAT;
	ymax=-G_MAXFLOAT;
	zmax=-G_MAXFLOAT;
	xmin=+G_MAXFLOAT;
	ymin=+G_MAXFLOAT;
	zmin=+G_MAXFLOAT;

	for (f=k=0;f<header.num_frames;f++)
	{
		fread(&dummy,sizeof(dummy),1,fp);
		fread(&frame,sizeof(frame),1,fp);

		strncpy(mdl->frames[f].name,frame.name,16);

		fread(vertex,size,1,fp);

		for (i=0;i<header.num_verts;i++,k++)
		{
			x=(gfloat)vertex[i].p[0] * header.scale.x 
				+ header.origin.x;
			y=(gfloat)vertex[i].p[1] * header.scale.y
				+ header.origin.y;
			z=(gfloat)vertex[i].p[2] * header.scale.z 
				+ header.origin.z;

			mdl->vertex[k].x=x;
			mdl->vertex[k].y=y;
			mdl->vertex[k].z=z;

			if (x>xmax) xmax=x; 
			if (x<xmin) xmin=x; 
			if (y>ymax) ymax=y; 
			if (y<ymin) ymin=y; 
			if (z>zmax) zmax=z; 
			if (z<zmin) zmin=z; 
		}
	}

	x=(xmax-xmin)/2;
	y=(ymax-ymin)/2;
	z=(zmax-zmin)/2;

	scale=x;
	if (y>scale) scale=y;
	if (z>scale) scale=z;

	if (scale) scale=1/scale; else scale=1;	

	x=(xmax+xmin)/2;
	y=(ymax+ymin)/2;
	z=(zmax+zmin)/2;

	
	
	for (i=0;i<header.num_verts*header.num_frames;i++)
	{
			mdl->vertex[i].x=(mdl->vertex[i].x-x)*scale;
			mdl->vertex[i].y=(mdl->vertex[i].y-y)*scale;
			mdl->vertex[i].z=(mdl->vertex[i].z-z)*scale;
	}

	for (i=0;i<header.num_tris;i++)
	{
		for (k=0;k<3;k++)
		{
			v=triangle[i].v[k];
			x=(gfloat)stvert[v].s/header.skin_width;
			y=(gfloat)stvert[v].t/header.skin_height;

			if (!triangle[i].facesfront && stvert[v].onseam)
			{
				x+=0.5;
			}
			mdl->tri[i].v[k]=v;
			mdl->tri[i].tex[k][0]=x;
			mdl->tri[i].tex[k][1]=y;
		}
	}

	g_free(vertex);
	g_free(stvert);
	g_free(triangle);

	mdl->destroy=destroy;
	mdl->draw=draw;

	return mdl;
}