/*--------------------------------------------------------------------------
 * File: lws.c
 * Written by: Fredrik Kling, 1997-05-25
 * Description: Lightwave Scene (LWS) loader.
 *
 * Updates:
 * -- Date -- | ----- Name ----- |-- Did what....
 * 1997-08-01 | Fredrik Kling    | Parentobject support...
 * 1997-06-26 | Fredrik Kling    | Kamera och utökad support, gjorde om keyframer läsaren...
 * 1997-06-16 | Fredrik Kling    | Keyframer data importeras korrekt....
 * 1997-05-25 | Fredrik Kling    | Implementation
 *
 *
 *
 * Todo:
 *
 -------------------------------------------------------------------------------*/

#include "system/xstddef.h"
#include "system/xstdio.h"
#include "system/xstdlib.h"
#include "system/xstring.h"
#include "system/xmath.h"
#include "vmath/vmath.h"
#include "formats/v3d.h"
#include "formats/v3o.h"
#include "formats/lws.h"
#include "formats/lwo.h"
#include "objects/light.h"
#include "objects/object.h"
#include "objects/camera.h"
#include "misc/spline.h"
#include "misc/parser.h"


#define L_DEBUG 1

#if defined(__WATCOMC__)
#define strcasecmp stricmp
#endif
/*
<color value>:          1 - Blue
                        2 - Green
                        3 - Light Blue
                        4 - Red
                        5 - Purple
                        6 - Orange
                        7 - Gray
*/
#define COL_BLUE    1
#define COL_GREEN   2
#define COL_LBLUE   3
#define COL_RED     4
#define COL_PURPLE  5
#define COL_ORANGE  6
#define COL_GRAY    7

/*
<refresh value>:        0 - No Refresh
                        1 - Bounding Box
                        2 - Points Only
                        3 - Every 4th Polygon
                        4 - Full Polygon (Default)
*/
#define REF_NO    0
#define REF_BBOX  1
#define REF_POINT 2
#define REF_4THP  3
#define REF_FULL  4

typedef enum
{
	ERR_NOERR,
	ERR_NOFILE,
	ERR_NOMEM,
	ERR_CUSTOM,
} ERROR;

static ERROR error = ERR_NOERR;
static char errstr[256];
//static char *lws_geterror(void);

void getstr( FILE *f, char *str)
{
	fgets( str, 256, f);
	str[ strlen(str) - 1] = '\0';
}

static int readanim (PARSE *p, OBJECT *obj)
{
  char tok[256];
	KEY key;
	KEY *n;
  int infoch,nofkf,i,linval,j;
  //float tension,bias,cont,xs,ys,zs;

  parse_get (p,tok);	// Fetch "("
	parse_get (p,tok);	// Fetch "unnamed"
  if (stricmp (tok,"unnamed"))
	{
		//xerror ("lws_load: In object, expected: 'unnamed' but found '%s'.\n",tok);
		error = ERR_CUSTOM;
		sprintf (errstr,"Expected: 'unnamed' but found '%s'",tok);
		return 0;
	}
	parse_get (p,tok);  // Fetch ")"

  // Read number of info-channels and number of keyframes...

  infoch = parse_geti (p);
  nofkf = parse_geti (p);

	#ifdef L_DEBUG
		printf ("  þ Objectmotion (channels,keys): %i,%i\n",infoch,nofkf);
	#endif

  // Create an animation section here!!!
  // Information channels assumed to be 9...
	obj->anim = spline_createanim (infoch);   // infoch should be 9....

	for (i=0;i<nofkf;i++)
	{
		float heading,pitch,bank;

		for (j=0;j<infoch;j++) key.value[j] = parse_getf (p);
		// Fix degrees to correct SAGA values...

		heading = (SINESIZE * key.value[ANIM_ANG_X]) / 360.0;
		pitch = (SINESIZE * key.value[ANIM_ANG_Y]) / 360.0;
		bank = (SINESIZE * key.value[ANIM_ANG_Z]) / 360.0;

		key.value[ANIM_ANG_X] = pitch;
		key.value[ANIM_ANG_Y] = heading;
		key.value[ANIM_ANG_Z] = bank;

		// Correct for lightwave axis
		key.value[ANIM_POS_Y] = -key.value[ANIM_POS_Y];

		key.frame = parse_geti (p);  // Warum nicht parse_getf ?
		linval = parse_geti (p);	// Unused!!
		key.tension = parse_getf (p);
		key.bias = parse_getf (p);
		key.contunity = parse_getf (p);

		// Add a key...
		n = spline_addkey (obj->anim,key.frame);

// It better work for me...  otherwise * URK *.. It didnt work..  POINTER stuff...
//		*n = key;
		for (j=0;j<infoch;j++) n->value[j]=key.value[j];
		n->tension = key.tension;
		n->bias = key.bias;
		n->contunity = key.contunity;
	}
	return 1;
}
static int doobject (PARSE *p,V3D *v3d, int i)
{
  int cmd;
  char tok[256];
  char fname[128];
  OBJECT *obj;
  int refv,cv,ltype,so,lc;
  //float xs,ys,zs,tension,bias,cont;
	float xf,yf,zf;


	printf ("þ Reading object information\n");
  // Fetch filename from lightwave...
  parse_safeget (p,fname);
  // Create an object, preset to v3o type...
	obj=object_create();
  obj->type=T_V3O;

	if (!i)
	{
  	// Load lwo file...
		printf ("  þ Reading File:'%s'\n",fname);
		if ((obj->v3o=lwo_load (fname))==NULL)
		{
			//xerror ("[!] Error: Unable to load: %s\n");
			error = ERR_NOFILE;
			return 0;
		}
	} else {
						#ifdef L_DEBUG
							printf ("  þ Reading Nullobject\n");
						#endif
						obj->type=T_NULL;
				 }


  while (cmd=parse_case(parse_get(p,tok),"ShowObject ObjectMotion EndBehavior ShadowOptions LockedChannels ParentObject GoalObject HLimits PLimits BLimits IKAnchor DisplacementMap ClipMap DistantDissolve PivotPoint MetaMorph"))
  switch (cmd)
  {

    case 1 :  // Fetch refresh value and colorvalue...
      refv=parse_geti (p);
      cv=parse_geti (p);
			#ifdef L_DEBUG
				printf ("  þ Refresh and Colorvalue: %i,%i\n",refv,cv);
			#endif
      break;

    case 2 :  readanim (p,obj); break;

    case 3: // Fetch loop behavior
			#ifdef L_DEBUG
				printf ("  þ Loop behavior\n");
			#endif
      ltype=parse_geti (p);
      break;

    case 4: // ShadowOptions
			#ifdef L_DEBUG
				printf ("  þ Shadow options..  ending object..\n");
			#endif
      so=parse_geti (p);
      v3d_obj2scene (v3d,obj);
      return 1;   // Exit function if shadowoptions reached...
			break;
	    // Unsupported featuers.
		case 5:	// Locked channels
			lc = parse_geti (p);
			break;
    case 6: // Parent object
			obj->num_parent=parse_geti (p);
			#ifdef L_DEBUG
				printf ("  þ Parent object: %i\n",obj->num_parent);
			#endif
			break;
    case 7:	// Goal object
			parse_geti (p);
			break;
    case 8: // Hlimits
				parse_getf (p);
				parse_getf (p);
				break;
    case 9: // Plimits
				parse_getf (p);
				parse_getf (p);
				break;
    case 10: // BLimits
				parse_getf (p);
				parse_getf (p);
				break;
    case 11:	// IKAnchor
				parse_geti (p);
				break;
    case 12:	// DisplacementsMap
    case 13:	// ClipMap
		case 14:	// DistantDissolve
				break;
		case 15:	// PivotPoint, translation of object...
				xf = parse_getf (p);	//x,y,z
				yf = parse_getf (p);
				zf = parse_getf (p);
				if (obj->type == T_V3O)
					v3o_translate (obj->v3o,xf,yf,zf);
				break;
		case 16: // MetaMoprh
				parse_getf (p);
				break;
    default:
						// Unsupported chunks gives an error...  not really good... but...
						error = ERR_CUSTOM;
					 	strcpy (errstr,"[LWS] Unsupported or unknown chunk");
					 	return 0;
  }
	// If we get this far something went wrong...   since we leave on exit functions...  Hopefully...
	error = ERR_CUSTOM;
	strcpy (errstr,"[LWS] Unsupported or unknown chunk");
	return 0;
}
static int dolight (PARSE *p,V3D *v3d)
{
  int cmd;
  OBJECT *obj;
  char tok[256];
  int rv,cv,ltype,red,green,blue,lt,so;
	float cs,foff;

	printf ("þ Reading Light\n");

	obj = object_create();
  obj->vlgh = vlgh_create ();
	obj->type=T_VLGH;

	// Shadowtype? <- finns inte med i lightwave doc'en
	// Parse everything...
  while (cmd=parse_case (parse_get (p,tok),"LightName ShowLight LightMotion EndBehavior LightColor LgtIntensity LightType ShadowCasting AddLight ShadowType TargetObject LightFalloff LightRange"))
  {
		printf ("dolight: %s\n",tok);
		switch (cmd)
    {
        case 1 :  // Name..  skip..
          parse_get (p,tok);
					#ifdef L_DEBUG
						printf ("  þ Light name: %s\n",tok);
					#endif
          break;
        case 2 :  // Refresh value and colorvalue...
          rv=parse_geti (p);
          cv=parse_geti (p);
					#ifdef L_DEBUG
						printf ("  þ Refresh & Color value: %i,%i\n",rv,cv);
					#endif
          break;
        case 3 : // Keyframer data....
						readanim (p,obj);
						break;
      case 4: // Endbehavior
        ltype=parse_geti (p);
				#ifdef L_DEBUG
					printf ("  þ Endbehavior: %i\n",ltype);
				#endif
        break;
      case 5: // Lightcolor
        red=obj->vlgh->color.r = parse_geti (p);
        green=obj->vlgh->color.g = parse_geti (p);
        blue=obj->vlgh->color.b = parse_geti (p);
				#ifdef L_DEBUG
					printf ("  þ Lightcolor: (%i,%i,%i)\n",red,green,blue);
				#endif
        break;
      case 6: // LgtIntensity
				#ifdef L_DEBUG
					printf ("  þ Lightintens..\n");
				#endif
				cs=parse_getf (p);
				obj->vlgh->intens=cs;
        break;
      case 7: // Lighttype
				#ifdef L_DEBUG
					printf ("  þ Lighttype...\n");
				#endif
        lt = parse_geti(p);

        switch (lt)
        {
          case 0:
          case 1:// obj->vlgh->type=T_OMNI;
          case 2://
          break;
        }
        break;
      case 8: //Shadowcasting...
        lt = parse_geti (p);
				#ifdef L_DEBUG
					printf ("  þ Shadowcasting: %i\n",lt);
				#endif

        v3d_obj2scene (v3d,obj);    // Add light to global lightlist
				return 1;
        break;
			case 9: // Addlight
				#ifdef L_DEBUG
					printf ("  þ Adding light to scene\n");
				#endif
				break;
			case 10: // Shadowoption
				#ifdef L_DEBUG
					printf ("  þ Shadowtype\n");
				#endif
				so = parse_geti (p);
				v3d_obj2scene (v3d,obj);
				return 1;
				break;
			case 11: // TargetObject
				parse_geti (p);
				break;
			case 12: // Lightfalloff
				#ifdef L_DEBUG
					printf ("  þ Falloff...\n");
				#endif
				foff=parse_getf (p);
				if (foff!=1)
				{
					obj->vlgh->flags|=VLGHFLAG_DISTANCE;
					obj->vlgh->falloff=1.0-foff;
				}
				break;
			case 13: // LightRange
				parse_getf (p);
				break;
    }
  }
	// If we get this far something went wrong...   since we leave on exit functions...  Hopefully...
	error = ERR_CUSTOM;
	strcpy (errstr,"[LIGHT] Unsupported or unknown chunk");
	return 0;
}
static void docamera (PARSE *p, V3D *v3d)
{
  int cmd;
  OBJECT *dum;
  char tok[256];
	CAMERA *cam;
	//int target_o;

	// Fetch color and refresh values...  Unused...

	parse_geti (p);
	parse_geti (p);

	#ifdef L_DEBUG
		printf ("þ Reading camera\n");
	#endif

	cam = camera_create ();
	// Place the camera in the scene
	v3d->activecamera = cam;

  while (cmd=parse_case (parse_get (p,tok),"CameraMotion EndBehavior TargetObject DepthofField LockedChannels"))
  {
    switch (cmd)
    {
			case 1 : // CameraMotion
				dum = object_create ();
				readanim (p,dum);
				cam->anim = dum->anim;
				xfree (dum);
				break;
			case 2: // EndBehavior
				parse_geti (p);
				break;
			case 3: // TargetObject
				cam->num_target = parse_geti (p);
				printf ("  + Target: %i\n",cam->num_target);
				return;
				break;
			case 4: // DepthofField
				parse_geti (p);
				return;
				break;
			case 5: // LockedChannels
				printf ("  + Locked channels\n");
				parse_geti (p);
				break;
			default:
				break;
		}
	}
}
V3D *lws_load (char *fn)
{
  PARSE *p;
  V3D *v3d;
  int cmd;
  int version;
	int ff,lf,fs;
	DWORD buffsize;
	float xf;
  char tok[512];
	char *buffer;


  buffer = fload( fn, &buffsize);
  if(buffer == NULL)
	{
		error = ERR_NOFILE;
		return( NULL);
	}

  p = parse_init(buffer);

  parse_this (p,"LWSC");
  v3d = v3d_create ();

  // Version
  version = parse_geti (p);
  if (version>1)
	{
		//xerror ("lws_load: Wrong version number: %i.\n",version);
		error = ERR_CUSTOM;
		sprintf (errstr,"Wrong version number: %i",version);
		return NULL;
	}
  printf ("Version: %i\n",version);


  while (cmd=parse_case(parse_get(p,tok),"LoadObject AddLight AmbientColor FirstFrame LastFrame FrameStep FramesPerSecond AddNullObject ShowCamera"))
  {
    switch (cmd)
    {
      case 1: // Loadobject
							if (!doobject(p,v3d,0))
						  {
								printf ("%s\n",lws_geterror ());
								return NULL;
								break;
							}
							break;
      case 2: // Addlight
							if (!dolight(p,v3d))
							{
								printf ("%s\n",lws_geterror ());
								return NULL;
								break;
							}
							break;
			case 3: // Ambient light...
							v3d -> ambient.r = parse_geti (p) / 4;
							v3d -> ambient.g = parse_geti (p) / 4;
							v3d -> ambient.b = parse_geti (p) / 4;
							parse_this (p,"AmbIntensity");
							xf = parse_getf (p);
							v3d -> ambient.r *= xf;
							v3d -> ambient.g *= xf;
							v3d -> ambient.b *= xf;
							break;
			case 4: ff = parse_geti (p); break;
			case 5: lf = parse_geti (p); break;
			case 6: fs = parse_geti (p); break;
			case 7: // Fps
							v3d->fps = parse_getf (p);
							break;
			case 8: // AddNullObject
							if (!doobject (p,v3d,1))
							{
								printf ("%s\n",lws_geterror ());
								break;
							}
							break;
			case 9: // Showcamera
							docamera (p,v3d);
							break;
      default: break; //xerror("That's not yet implemented!\n");
    }
  }

  xfree (buffer);
  parse_exit (p);

	return (v3d);
}
/*################################################################
	# lws - geterror
	#################################################################*/
char *lws_geterror(void)
{
	switch( error)
	{
		case ERR_NOERR: return( "[LWS] No error");
		case ERR_NOFILE: return( "[LWS] Unable to open file");
		case ERR_NOMEM: return( "[LWS] Unable to allocate memory");
		case ERR_CUSTOM:
		{
			char str[256];
			strcpy( str, errstr);
			sprintf( errstr, "[LWS] %s", str);
			return( errstr);
		}
		default: return( "[LWS] Unknown error!");
	}
	return ("[LWS] No error");
}
