/*
 *  acm : an aerial combat simulator for X
 *  Copyright (C) 1991-1998  Riley Rainey
 *
 *  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; version 2 dated June, 1991.
 *
 *  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.
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "../util/error.h"
#include "../util/memory.h"
#include "pm.h"
#include "inventory.h"
#include "planes.h"

#define box_IMPORT
#include "box.h"

#define BB_HDR_SIZE (sizeof(char) + sizeof(char) + sizeof(short))

#define BB_TYPE_SHORT_STATE     0x00
#define BB_TYPE_ADD_OBJECT      0x01
#define BB_TYPE_DELETE_OBJECT   0x02
#define BB_TYPE_END_OF_FRAME    0x03

typedef struct {
	earth_LatLonAlt w;
	VPoint    Sg;
	VPoint    Cg;
	double    heading, pitch, roll;
} _BBShortState;

typedef struct {
	short     type;				/* craft type */
	char      name[64];			/* craft name */
} _BBNewObject;

typedef struct {
	unsigned  char rectype;		/* black box record type */
	unsigned  char table;			/* is it ptbl(0) or mtbl(1)? */
	unsigned  short id;			/* player or missile index */
	union {
		_BBShortState short_state;
		_BBNewObject object;
	} u;
} BBRecord;

static FILE *bbin = 0, *bbout = 0;

static short     rp_map[manifest_MAXPLAYERS], rm_map[manifest_MAXPROJECTILES];
static short     pp_map[manifest_MAXPLAYERS], pm_map[manifest_MAXPROJECTILES];


static void
newBlackBoxCraft(int id, int type, char *name)
{
	register craft *c;
	register int i, max;
	register short *p;

	/*  per PREfix */
	if (type != CT_PLANE && type != CT_DRONE &&
		type != CT_MISSILE && type != CT_CANNON) {
		printf("Invalid craft type passed to newBlackBoxCraft()\n");
	}

	/* Initialize these vars in order to prevent warnings from gcc -Wall: */
	c = NULL;
	max = 0;
	p = NULL;

	switch (type) {

	case CT_PLANE:
	case CT_DRONE:
		if ((i = planes_newPlane(name)) >= 0) {
			c = &ptbl[i];
			c->type = type;
			c->flags = FL_BLACK_BOX;
			memory_strcpy(c->name, sizeof(c->name), name);
			pp_map[id] = i;
		}
		else {
			fprintf(stderr, "No room in player table to add another black box object.\n");
		}
		return;
/*NOTREACHED */ break;

	case CT_MISSILE:
	case CT_CANNON:
		c = mtbl;
		max = manifest_MAXPROJECTILES;
		p = rm_map;
		break;

	default: error_internal("type=%d", type);
	}

	for (i = 0; i < max; ++i, ++c) {
		if (c->type == CT_FREE) {
			c->type = type;
			c->flags = FL_BLACK_BOX;
			c->cinfo = inventory_craftTypeSearchByZoneAndName(NULL, name);
			p[id] = i;
		}
	}
}

void
box_startRecording(void)
{
	int       i;

	if ((bbout = fopen("./black_box_output", "w")) == (FILE *) NULL) {
		fprintf(stderr, "unable to open black box recording file\n");
	}
	for (i = 0; i < manifest_MAXPLAYERS; ++i) {
		rp_map[i] = -1;
	}
	for (i = 0; i < manifest_MAXPROJECTILES; ++i) {
		rm_map[i] = -1;
	}
}

void
box_endRecording(void)
{
	if ( bbout == NULL )  return;
	fclose(bbout);
	bbout = (FILE *) NULL;
}

void
box_startPlayback(void)
{
	int       i;

	if ((bbin = fopen("./black_box_input", "r")) == (FILE *) NULL) {
		fprintf(stderr, "unable to open black box playback file\n");
	}
	for (i = 0; i < manifest_MAXPLAYERS; ++i) {
		pp_map[i] = -1;
	}
	for (i = 0; i < manifest_MAXPROJECTILES; ++i) {
		pm_map[i] = -1;
	}
}

/*
 *  Update items under the control of black box playback
 */

void
box_input(void)
{
	register int i;
	BBRecord  rec;
	craft    *c;

	if (bbin) {
		while (fread((char *) &rec, BB_HDR_SIZE, 1, bbin) == 1) {

			c = (rec.table == 0) ? &ptbl[pp_map[rec.id]] :
				&mtbl[pm_map[-(int) rec.id]];

			switch (rec.rectype) {

			case BB_TYPE_SHORT_STATE:
				fread((char *) &rec.u.short_state,
					  sizeof(rec.u.short_state), 1, bbin);
				if (pp_map[rec.id] == -1)
					break;
				c->prevSg = c->Sg;
				c->w = rec.u.short_state.w;
				c->Sg = rec.u.short_state.Sg;
				c->Cg = rec.u.short_state.Cg;
				c->curRoll = rec.u.short_state.roll;
				c->curPitch = rec.u.short_state.pitch;
				c->curHeading = rec.u.short_state.heading;
				break;

			case BB_TYPE_ADD_OBJECT:
				fread((char *) &rec.u.object,
					  sizeof(rec.u.object), 1, bbin);
				newBlackBoxCraft(rec.id, rec.u.object.type,
								 rec.u.object.name);
				break;

			case BB_TYPE_DELETE_OBJECT:
				pp_map[rec.id] = -1;
				c->kill(c, "(FIXME)");
				break;

			case BB_TYPE_END_OF_FRAME:
				return;

			default:
				fprintf(stderr, "unknown rectype in\
 black box recording: %d\n", rec.rectype);
				fclose(bbin);
				bbin = (FILE *) NULL;
				break;
			}
		}
		fclose(bbin);
		bbin = (FILE *) NULL;
		for (i = 0; i < manifest_MAXPLAYERS; ++i) {
			if (pp_map[i] != CT_FREE)
				ptbl[pp_map[i]].kill(&ptbl[pp_map[i]], "(FIXME)");
		}
	}
}

/*
 *  Write out black box records
 */

void
box_output(void)
{
	register int i;
	register craft *c;
	BBRecord  rec;

	if (bbout) {
		for (i = 0, c = ptbl; i < manifest_MAXPLAYERS; ++i, ++c) {

			if (c->type != CT_FREE) {

				if (rp_map[i] == -1) {
					rp_map[i] = i;
					rec.rectype = BB_TYPE_ADD_OBJECT;
					rec.table = 0;
					rec.id = i;
					rec.u.object.type = c->type;
					memory_strcpy(rec.u.object.name, sizeof(rec.u.object.name),
						   c->cinfo->name);
					fwrite((char *) &rec, BB_HDR_SIZE, 1, bbout);
					fwrite((char *) &rec.u.object,
						   sizeof(rec.u.object), 1, bbout);
				}
				rec.rectype = BB_TYPE_SHORT_STATE;
				rec.table = 0;
				rec.id = i;
				rec.u.short_state.w = c->w;
				rec.u.short_state.Sg = c->Sg;
				rec.u.short_state.Cg = c->Cg;
				rec.u.short_state.roll = c->curRoll;
				rec.u.short_state.pitch = c->curPitch;
				rec.u.short_state.heading = c->curHeading;
				fwrite((char *) &rec, BB_HDR_SIZE, 1, bbout);
				fwrite((char *) &rec.u.object,
					   sizeof(rec.u.short_state), 1, bbout);
			}
		}
		for (i = 0; i < manifest_MAXPROJECTILES; ++i) {
			rm_map[i] = -1;
		}

		rec.rectype = BB_TYPE_END_OF_FRAME;
		rec.id = 0;
		fwrite((char *) &rec, BB_HDR_SIZE, 1, bbout);
	}
}

void
box_killPlayer(int id)
{
	BBRecord  rec;

	if (bbout) {
		rec.rectype = BB_TYPE_DELETE_OBJECT;
		rec.id = id;
		fwrite((char *) &rec, BB_HDR_SIZE, 1, bbout);
	}
}
