/*
**  wt -- a 3d game engine
**
**  Copyright (C) 1994 by Chris Laurel
**  email:  claurel@mr.net
**  snail mail:  Chris Laurel, 5700 W Lake St #208,  St. Louis Park, MN  55416
**
**  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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/* This module is mostly code by Russ Nelson (nelson@crynwr.com) */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <limits.h>
#include <math.h>
#include <tcl/tcl.h>

#include "wt.h"
#include "error.h"
#include "fixed.h"
#include "wtmem.h"
#include "list.h"
#include "table.h"
#include "framebuf.h"
#include "texture.h"
#include "world.h"
#include "tclupdate.h"
#include "tclworld.h"



#define REGION_FLOOR_HEIGHT     0
#define REGION_CEILING_HEIGHT   1
#define REGION_FLOOR_TEXTURE    2
#define REGION_CEILING_TEXTURE  3


static int Tcl_GetFixed(Tcl_Interp *interp, char *s, fixed *f)
{
     double d;

     if (Tcl_GetDouble(interp, s, &d) != TCL_OK)
	  return TCL_ERROR;

     *f = FLOAT_TO_FIXED(d);

     return TCL_OK;
}


int parse_vertex(ClientData clientData, Tcl_Interp *interp,
		 int argc, char *argv[])
{
     Vertex v;
     World *w = (World *) clientData;


     if (argc != 3) {
	  interp->result = "wrong # args";
	  return TCL_ERROR;
     }
     if (Tcl_GetFixed(interp, argv[1], &v.x) != TCL_OK) {
	  interp->result = "number expected";
	  return TCL_ERROR;
     }
     if (Tcl_GetFixed(interp, argv[2], &v.y) != TCL_OK) {
	  interp->result = "number expected";
	  return TCL_ERROR;
     }

     sprintf(interp->result, "%d", add_vertex(w, &v));

     return TCL_OK;
}


int parse_region(ClientData clientData, Tcl_Interp *interp,
		  int argc, char *argv[])
{
     int texture_index;
     Region r;
     World *w = (World *) clientData;


     if (argc != 5) {
	  interp->result = "wrong # args";
	  return TCL_ERROR;
     }
     if (Tcl_GetFixed(interp, argv[1], &r.floor) != TCL_OK) {
	  interp->result = "number expected";
	  return TCL_ERROR;
     }
     if (Tcl_GetFixed(interp, argv[2], &r.ceiling) != TCL_OK) {
	  interp->result = "number expected";
	  return TCL_ERROR;
     }

     /* floor texture */
     if (Tcl_GetInt(interp, argv[3], &texture_index) != TCL_OK) {
	  interp->result = "number expected";
	  return TCL_ERROR;
     }
     if (texture_index < 0 || texture_index >= TABLE_SIZE(w->textures)) {
	  interp->result = "non-existent texture";
	  return TCL_ERROR;
     } else {
	  r.floor_tex = WORLD_TEXTURE(w, texture_index);
     }

     /* ceiling texture */
     if (Tcl_GetInt(interp, argv[4], &texture_index) != TCL_OK) {
	  interp->result = "number expected";
	  return TCL_ERROR;
     }
     if (texture_index < 0)
	  r.ceiling_tex = NULL;
     else {
	  if (texture_index < 0 || texture_index >= TABLE_SIZE(w->textures)) {
	       interp->result = "non-existent texture";
	       return TCL_ERROR;
	  } else
	       r.ceiling_tex = WORLD_TEXTURE(w, texture_index);
     }

     sprintf(interp->result, "%d", add_region(w, &r));

     return TCL_OK;
}


int parse_wall(ClientData clientData, Tcl_Interp *interp,
	       int argc, char *argv[])
{
     Wall wall;
     int texture_index;
     int front_region, back_region;
     int vertex1, vertex2;
     fixed wall_length;
     World *w = (World *)clientData;


     if (argc != 10) {
	  interp->result = "wrong # args";
	  return TCL_ERROR;
     }
     /* vertices */
     if (Tcl_GetInt(interp, argv[1], &vertex1) != TCL_OK) {
	  interp->result = "number expected";
	  return TCL_ERROR;
     }
     if (Tcl_GetInt(interp, argv[2], &vertex2) != TCL_OK) {
	  interp->result = "number expected";
	  return TCL_ERROR;
     }
     if (vertex1 < 0 || vertex1 >= TABLE_SIZE(w->vertices)) {
	  interp->result = "invalid vertex number";
	  return TCL_ERROR;
     }
     if (vertex2 < 0 || vertex2 >= TABLE_SIZE(w->vertices)) {
	  interp->result = "invalid vertex number";
	  return TCL_ERROR;
     }
     wall.vertex1 = &WORLD_VERTEX(w, vertex1);
     wall.vertex2 = &WORLD_VERTEX(w, vertex2);

     /* texture */
     if (Tcl_GetInt(interp, argv[3], &texture_index) != TCL_OK) {
	  interp->result = "number expected";
	  return TCL_ERROR;
     }
     if (texture_index < 0 || texture_index >= TABLE_SIZE(w->textures)) {
	  interp->result = "non-existent texture";
	  return TCL_ERROR;
     } else
	  wall.surface_texture = WORLD_TEXTURE(w, texture_index);

     /* front and back regions */
     if (Tcl_GetInt(interp, argv[4], &front_region) != TCL_OK) {
	  interp->result = "number expected";
	  return TCL_ERROR;
     }
     if (Tcl_GetInt(interp, argv[5], &back_region) != TCL_OK) {
	  interp->result = "number expected";
	  return TCL_ERROR;
     }
     if (front_region < 0 || front_region >= TABLE_SIZE(w->regions)) {
	  interp->result = "non-existent region";
	  return TCL_ERROR;
     }
     if (back_region < 0 || back_region >= TABLE_SIZE(w->regions)) {
	  interp->result = "non-existent region";
	  return TCL_ERROR;
     }
     wall.front = &WORLD_REGION(w, front_region);
     wall.back = &WORLD_REGION(w, back_region);

     /* Texture phase and scale.  This code is somewhat more complicated than
     **   you'd expect, since the texture scale must be normalized to the
     **   wall length.
     */
     if (Tcl_GetFixed(interp, argv[6], &wall.xscale) != TCL_OK) {
	  interp->result = "number expected";
	  return TCL_ERROR;
     }
     if (Tcl_GetFixed(interp, argv[7], &wall.yscale) != TCL_OK) {
	  interp->result = "number expected";
	  return TCL_ERROR;
     }
     if (Tcl_GetFixed(interp, argv[8], &wall.xphase) != TCL_OK) {
	  interp->result = "number expected";
	  return TCL_ERROR;
     }
     if (Tcl_GetFixed(interp, argv[9], &wall.yphase) != TCL_OK) {
	  interp->result = "number expected";
	  return TCL_ERROR;
     }
     wall_length =
	  FLOAT_TO_FIXED(sqrt(FIXED_TO_FLOAT(wall.vertex2->x -
					     wall.vertex1->x) *
			      FIXED_TO_FLOAT(wall.vertex2->x -
					     wall.vertex1->x) +
			      FIXED_TO_FLOAT(wall.vertex2->y -
					     wall.vertex1->y) *
			      FIXED_TO_FLOAT(wall.vertex2->y -
					     wall.vertex1->y)));
     wall.yscale = fixmul(wall.yscale,
			  INT_TO_FIXED(wall.surface_texture->height));
     wall.xscale = fixmul(fixmul(wall.xscale,
				 INT_TO_FIXED(wall.surface_texture->width)),
			  wall_length);
     wall.xphase = FIXED_SCALE(wall.xphase, wall.surface_texture->width);
     wall.yphase = FIXED_SCALE(wall.yphase, wall.surface_texture->height);

     wall.sky = False;

     sprintf(interp->result, "%d", add_wall(w, &wall));

     return TCL_OK;
}


int parse_texture(ClientData clientData, Tcl_Interp *interp,
		  int argc, char *argv[])
{
     char texture_path[PATH_MAX];
     Texture *t;
     World *w = (World *) clientData;

     if (argc != 2) {
	  interp->result = "wrong # args";
	  return TCL_ERROR;
     }

     sprintf(texture_path, "%s/%s", TEXTURE_PATH, argv[1]);
     t = read_texture_file(texture_path);

     sprintf(interp->result, "%d", add_texture(w, t));

     return TCL_OK;
}


int parse_skywall(ClientData clientData, Tcl_Interp *interp,
		  int argc, char *argv[])
{
     int wall_index;
     int scale;
     World *w = (World *) clientData;
     Wall *wall;


     if (argc != 3) {
	  interp->result = "wrong # args";
	  return TCL_ERROR;
     }

     if (Tcl_GetInt(interp, argv[1], &wall_index) != TCL_OK) {
	  interp->result = "wall number expected";
	  return TCL_ERROR;
     }

     if (Tcl_GetInt(interp, argv[2], &scale) != TCL_OK) {
	  interp->result = "integer expected";
	  return TCL_ERROR;
     }

     if (wall_index < 0 || wall_index >= TABLE_SIZE(w->walls)) {
	  interp->result = "non-existent wall";
	  return TCL_ERROR;
     }

     wall = &WORLD_WALL(w, wall_index);
     wall->sky = True;
     wall->xscale = INT_TO_FIXED(wall->surface_texture->width * scale);

     return TCL_OK;
}


int parse_set_region(ClientData clientData, Tcl_Interp *interp,
		     int argc, char *argv[])
{
     int region_index;
     int field;
     fixed fixval;
     int intval;
     World *w = (World *) clientData;


     if (argc != 4) {
	  interp->result = "wrong # args";
	  return TCL_ERROR;
     }
     if (Tcl_GetInt(interp, argv[1], &region_index) != TCL_OK) {
	  interp->result = "number expected";
	  return TCL_ERROR;
     }
     if (region_index < 0 || region_index >= TABLE_SIZE(w->regions)) {
	  interp->result = "non-existent region";
	  return TCL_ERROR;
     }

     if (Tcl_GetInt(interp, argv[2], &field) != TCL_OK) {
	  interp->result = "number expected";
	  return TCL_ERROR;
     }

     switch (field) {
	case REGION_FLOOR_HEIGHT:
	  if (Tcl_GetFixed(interp, argv[3], &fixval) != TCL_OK) {
	       interp->result = "number expected";
	       return TCL_ERROR;
	  }
	  WORLD_REGION(w, region_index).floor = fixval;
	  break;

	case REGION_CEILING_HEIGHT:
	  if (Tcl_GetFixed(interp, argv[3], &fixval) != TCL_OK) {
	       interp->result = "number expected";
	       return TCL_ERROR;
	  }
	  WORLD_REGION(w, region_index).ceiling = fixval;
	  break;

	case REGION_FLOOR_TEXTURE:
	  if (Tcl_GetInt(interp, argv[3], &intval) != TCL_OK) {
	       interp->result = "integer index expected";
	       return TCL_ERROR;
	  }
	  if (intval < 0 || intval >= TABLE_SIZE(w->textures))
	       interp->result = "not a valid texture";
	  WORLD_REGION(w, region_index).floor_tex = WORLD_TEXTURE(w, intval);
     
	  break;
	  
	case REGION_CEILING_TEXTURE:
	  if (Tcl_GetInt(interp, argv[3], &intval) != TCL_OK) {
	       interp->result = "integer index expected";
	       return TCL_ERROR;
	  }
	  if (intval < 0 || intval >= TABLE_SIZE(w->textures))
	       interp->result = "not a valid texture";
	  WORLD_REGION(w, region_index).ceiling_tex = WORLD_TEXTURE(w, intval);

	  break;

	default:
	  interp->result = "bad field number";
	  return TCL_ERROR;
     }

     return TCL_OK;
}


int parse_get_region(ClientData clientData, Tcl_Interp *interp,
		     int argc, char *argv[])
{
     int region_index;
     int field;
     World *w = (World *) clientData;


     if (argc != 3) {
	  interp->result = "wrong # args";
	  return TCL_ERROR;
     }

     if (Tcl_GetInt(interp, argv[1], &region_index) != TCL_OK) {
	  interp->result = "number expected";
	  return TCL_ERROR;
     }
     if (region_index < 0 || region_index >= TABLE_SIZE(w->regions)) {
	  interp->result = "non-existent region";
	  return TCL_ERROR;
     }

     if (Tcl_GetInt(interp, argv[2], &field) != TCL_OK) {
	  interp->result = "number expected";
	  return TCL_ERROR;
     }

     switch (field) {
	case REGION_FLOOR_HEIGHT:
	  sprintf(interp->result, "%f",
		  FIXED_TO_FLOAT(WORLD_REGION(w, region_index).floor));
	  break;

	case REGION_CEILING_HEIGHT:
	  sprintf(interp->result, "%f",
		  FIXED_TO_FLOAT(WORLD_REGION(w, region_index).ceiling));
	  break;

	default:
	  interp->result = "bad field number";
	  return TCL_ERROR;
     }

     return TCL_OK;
}


int parse_move_vertex(ClientData clientData, Tcl_Interp *interp,
		      int argc, char *argv[])
{
     int vertex_index;
     fixed x, y;
     World *w = (World *) clientData;


     if (argc != 4) {
	  interp->result = "wrong # args";
	  return TCL_ERROR;
     }

     if (Tcl_GetInt(interp, argv[1], &vertex_index) != TCL_OK) {
	  interp->result = "vertex index expected";
	  return TCL_ERROR;
     }

     if (vertex_index < 0 || vertex_index >= TABLE_SIZE(w->vertices)) {
	  interp->result = "non-existent vertex";
	  return TCL_ERROR;
     }

     if (Tcl_GetFixed(interp, argv[2], &x) != TCL_OK ||
	 Tcl_GetFixed(interp, argv[3], &y) != TCL_OK) {
	  interp->result = "number expected";
	  return TCL_ERROR;
     }

     WORLD_VERTEX(w, vertex_index).x = x;
     WORLD_VERTEX(w, vertex_index).y = y;

     return TCL_OK;
}


int parse_addupdate(ClientData clientData, Tcl_Interp *interp,
		    int argc, char *argv[])
{
     Updater *u = (Updater *) clientData;
     int event_num;


     if (argc != 2) {
	  interp->result = "wrong # args";
	  return TCL_ERROR;
     }

     event_num = updater_add_event(u, argv[1]);
     sprintf(interp->result, "%d", event_num);

     return TCL_OK;
}
