/* savebsp.m */

#include "idbsp.h"

/*
   id              secstore_i;
   id              mapvertexstore_i;
   id              subsecstore_i;
   id              maplinestore_i;
   id              nodestore_i;
   id              mapthingstore_i;
   id              ldefstore_i;
   id              sdefstore_i;
 */

STORAGE            *secstore_i;
STORAGE            *mapvertexstore_i;
STORAGE            *subsecstore_i;
STORAGE            *maplinestore_i;
STORAGE            *nodestore_i;
STORAGE            *mapthingstore_i;
STORAGE            *ldefstore_i;
STORAGE            *sdefstore_i;

/*
   ===============================================================================

   the output functions byte swap and write lumps

   ===============================================================================
 */


/*
   ================
   =
   = WriteStorage
   =
   ================
 */

/*void WriteStorage (char *name, id store, int esize) */
void                WriteStorage(char *name, STORAGE * store, int esize)
{
	int                 count,
	                    len;

/*      count = [store count]; */
	count = store -> count;
	len = esize * count;
/*      [wad_i addName: name data:[store elementAt:0] size:len]; */
	addName(name, store -> data, len);
	printf("%s (%i): %i\n", name, count, len);
}


/*
   =================
   =
   = OutputSectors
   =
   =================
 */

void                OutputSectors(void)
{
	int                 i,
	                    count;
	mapsector_t        *p;

/*
   count = [secstore_i count];
   p = [secstore_i elementAt:0];
 */
	count = secstore_i -> count;
	p = secstore_i -> data;

/*
   for (i=0 ; i<count ; i++, p++)
   {
   p->floorheight = SHORT(p->floorheight);
   p->ceilingheight = SHORT(p->ceilingheight);
   p->lightlevel = SHORT(p->lightlevel);
   p->special = SHORT(p->special);
   p->tag = SHORT(p->tag);
   }
 */

	WriteStorage("sectors", secstore_i, sizeof(mapsector_t));
}


/*
   =================
   =
   = OutputSegs
   =
   =================
 */

void                OutputSegs(void)
{
	int                 i,
	                    count;
	mapseg_t           *p;

/*
   count = [maplinestore_i count];
   p = [maplinestore_i elementAt:0];
 */
	count = maplinestore_i -> count;
	p = maplinestore_i -> data;
/*
   for (i=0 ; i<count ; i++, p++)
   {
   p->v1 = SHORT(p->v1);
   p->v2 = SHORT(p->v2);
   p->angle = SHORT(p->angle);
   p->linedef = SHORT(p->linedef);
   p->side = SHORT(p->side);
   p->offset = SHORT(p->offset);
   }
 */
	WriteStorage("segs", maplinestore_i, sizeof(mapseg_t));
}


/*
   =================
   =
   = OutputSubsectors
   =
   =================
 */

void                OutputSubsectors(void)
{
	int                 i,
	                    count;
	mapsubsector_t     *p;

/*
   count = [subsecstore_i count];
   p = [subsecstore_i elementAt:0];
 */
	count = subsecstore_i -> count;
	p = subsecstore_i -> data;
/*
   for (i=0 ; i<count ; i++, p++)
   {
   p->numsegs = SHORT(p->numsegs);
   p->firstseg = SHORT(p->firstseg);
   }
 */
	WriteStorage("ssectors", subsecstore_i, sizeof(mapsubsector_t));
}


/*
   =================
   =
   = OutputVertexes
   =
   =================
 */

void                OutputVertexes(void)
{
	int                 i,
	                    count;
	mapvertex_t        *p;

/*
   count = [mapvertexstore_i count];
   p = [mapvertexstore_i elementAt:0];
 */
	count = mapvertexstore_i -> count;
	p = mapvertexstore_i -> data;
/*
   for (i=0 ; i<count ; i++, p++)
   {
   p->x = SHORT(p->x);
   p->y = SHORT(p->y);
   }
 */
	WriteStorage("vertexes", mapvertexstore_i, sizeof(mapvertex_t));
}


/*
   =================
   =
   = OutputThings
   =
   =================
 */

void                OutputThings(void)
{
	int                 i,
	                    count;
	mapthing_t         *p;

/*
   count = [mapthingstore_i count];
   p = [mapthingstore_i elementAt:0];
 */
	count = mapthingstore_i -> count;
	p = mapthingstore_i -> data;
/*
   for (i=0 ; i<count ; i++, p++)
   {
   p->x = SHORT(p->x);
   p->y = SHORT(p->y);
   p->angle = SHORT(p->angle);
   p->type = SHORT(p->type);
   p->options = SHORT(p->options);
   }
 */
	WriteStorage("things", mapthingstore_i, sizeof(mapthing_t));
}


/*
   =================
   =
   = OutputLineDefs
   =
   =================
 */

void                OutputLineDefs(void)
{
	int                 i,
	                    count;
	maplinedef_t       *p;

/*
   count = [ldefstore_i count];
   p = [ldefstore_i elementAt:0];
 */
	count = ldefstore_i -> count;
	p = ldefstore_i -> data;

#if 0
	for (i = 0; i < count; i++, p++)
		{
		p -> v1 = SHORT(p -> v1);
		p -> v2 = SHORT(p -> v2);
/* some ancient version of DoomEd left ML_MAPPED flags in some of the levels */
		p -> flags = SHORT(p -> flags & ~ML_MAPPED);
		p -> special = SHORT(p -> special);
		p -> tag = SHORT(p -> tag);
		p -> sidenum[0] = SHORT(p -> sidenum[0]);
		p -> sidenum[1] = SHORT(p -> sidenum[1]);
		}
#endif

	WriteStorage("linedefs", ldefstore_i, sizeof(maplinedef_t));
}


/*
   =================
   =
   = OutputSideDefs
   =
   =================
 */

void                OutputSideDefs(void)
{
	int                 i,
	                    count;
	mapsidedef_t       *p;

/*
   count = [sdefstore_i count];
   p = [sdefstore_i elementAt:0];
 */
	count = sdefstore_i -> count;
	p = sdefstore_i -> data;
/*
   for (i=0 ; i<count ; i++, p++)
   {
   p->textureoffset = SHORT(p->textureoffset);
   p->rowoffset = SHORT(p->rowoffset);
   p->sector = SHORT(p->sector);
   }
 */
	WriteStorage("sidedefs", sdefstore_i, sizeof(mapsidedef_t));
}


/*
   =================
   =
   = OutputNodes
   =
   =================
 */

void                OutputNodes(void)
{
	int                 i,
	                    j,
	                    count;
	mapnode_t          *p;

/*
   count = [nodestore_i count];
   p = [nodestore_i elementAt:0];
 */
	count = nodestore_i -> count;
	p = nodestore_i -> data;
/*
   for (i=0 ; i<count ; i++, p++)
   {
   for (j=0 ; j<sizeof(mapnode_t)/2 ; j++)
   ((short *)p)[j] = SHORT(((short *)p)[j]);
   }
 */
	WriteStorage("nodes", nodestore_i, sizeof(mapnode_t));
}


/*
   ===============================================================================

   PROCESSING

   ===============================================================================
 */


/*
   =================
   =
   = UniqueVertex
   =
   = Returns the vertex number, adding a new vertex if needed
   =================
 */

int                 UniqueVertex(int x, int y)
{
	int                 i,
	                    count;
	mapvertex_t         mv,
	                   *mvp;

	mv.x = x;
	mv.y = y;

/* see if an identical vertex already exists */
/*
   count = [mapvertexstore_i count];
   mvp = [mapvertexstore_i elementAt:0];
 */
	count = mapvertexstore_i -> count;
	mvp = mapvertexstore_i -> data;
	for (i = 0; i < count; i++, mvp++)
		if (mvp -> x == mv.x && mvp -> y == mv.y)
			return i;

/*      [mapvertexstore_i addElement: &mv]; */
	memcpy((mapvertex_t *) mapvertexstore_i -> data + mapvertexstore_i -> count, &mv,
		sizeof(mapvertex_t));
	mapvertexstore_i -> count += 1;
	mapvertexstore_i -> data = (mapvertex_t *) realloc(mapvertexstore_i -> data,
		sizeof(mapvertex_t) * (mapvertexstore_i -> count + 1));

	return count;
}


/*
   =============================================================================
 */

float               bbox[4];

/*
   =================
   =
   = AddPointToBBox
   =
   =================
 */

void                AddPointToBBox(NXPoint * pt)
{
	if (pt -> x < bbox[BOXLEFT])
		bbox[BOXLEFT] = pt -> x;
	if (pt -> x > bbox[BOXRIGHT])
		bbox[BOXRIGHT] = pt -> x;

	if (pt -> y > bbox[BOXTOP])
		bbox[BOXTOP] = pt -> y;
	if (pt -> y < bbox[BOXBOTTOM])
		bbox[BOXBOTTOM] = pt -> y;
}


/*
   =================
   =
   = ProcessLines
   =
   = Adds the lines in a subsector to the mapline storage
   =================
 */

/* void ProcessLines (id store_i) */
void                ProcessLines(STORAGE * store_i)
{
	int                 i,
	                    count;
	line_t             *wline;
	mapseg_t            line;
	short               angle;
	float               fangle;

	bbox[BOXLEFT] = INT_MAX;
	bbox[BOXRIGHT] = INT_MIN;
	bbox[BOXTOP] = INT_MIN;
	bbox[BOXBOTTOM] = INT_MAX;

/*      count = [store_i count]; */
	count = store_i -> count;
	for (i = 0; i < count; i++)
		{
/*              wline = [store_i elementAt: i]; */
		wline = (line_t *) store_i -> data + i;
		if (wline -> grouped)
			printf("ERROR: line regrouped\n");
		wline -> grouped = true;

		memset(&line, 0, sizeof(line));
		AddPointToBBox(&wline -> p1);
		AddPointToBBox(&wline -> p2);
		line.v1 = UniqueVertex(wline -> p1.x, wline -> p1.y);
		line.v2 = UniqueVertex(wline -> p2.x, wline -> p2.y);
		line.linedef = wline -> linedef;
		line.side = wline -> side;
		line.offset = wline -> offset;
		fangle = atan2(wline -> p2.y - wline -> p1.y, wline -> p2.x - wline -> p1.x);
		angle = (short)(fangle / (PI * 2) * 0x10000);
		line.angle = angle;
/*              [maplinestore_i addElement: &line]; */
		memcpy((mapseg_t *) maplinestore_i -> data + maplinestore_i -> count, &line,
			sizeof(line));
		maplinestore_i -> count += 1;
		maplinestore_i -> data = (mapseg_t *) realloc(maplinestore_i -> data,
			sizeof(mapseg_t) * (maplinestore_i -> count + 1));
		}
}


/*
   =================
   =
   = ProcessSubsector
   =
   =================
 */

/* int ProcessSubsector (id wmaplinestore_i) */
int                 ProcessSubsector(STORAGE * wmaplinestore_i)
{
	int                 count;
	worldline_t        *linedef;
	line_t             *wline;
	mapsubsector_t      sub;

	memset(&sub, 0, sizeof(sub));

/*      count = [wmaplinestore_i count]; */
	count = wmaplinestore_i -> count;
	if (count < 1)
		Error("ProcessSubsector: count = %i", count);

/*      wline = [wmaplinestore_i elementAt: 0]; */
	wline = wmaplinestore_i -> data;

/*      linedef = [linestore_i elementAt: wline->linedef]; */
	linedef = (worldline_t *) linestore_i -> data + wline -> linedef;
	sub.numsegs = count;
/*      sub.firstseg = [maplinestore_i count]; */
	sub.firstseg = maplinestore_i -> count;
	ProcessLines(wmaplinestore_i);

/* add the new subsector
   [subsecstore_i addElement: &sub];
 */
	memcpy((mapsubsector_t *) subsecstore_i -> data + subsecstore_i -> count, &sub,
		sizeof(mapsubsector_t));
	subsecstore_i -> count += 1;
	subsecstore_i -> data = (mapsubsector_t *) realloc(subsecstore_i -> data,
		sizeof(mapsubsector_t) * (subsecstore_i -> count + 1));

/*      return [subsecstore_i count]-1; */
	return subsecstore_i -> count - 1;
}

/*
   =================
   =
   = ProcessNode
   =
   =================
 */

int                 ProcessNode(bspnode_t * node, short *totalbox)
{
	short               subbox[2][4];
	int                 i,
	                    r;
	mapnode_t           mnode;

	memset(&mnode, 0, sizeof(mnode));

	if (node -> lines_i)			   /* NF_SUBSECTOR flags a subsector */
		{
		r = ProcessSubsector(node -> lines_i);
		for (i = 0; i < 4; i++)
			totalbox[i] = bbox[i];
		return r | NF_SUBSECTOR;
		}

	mnode.x = node -> divline.pt.x;
	mnode.y = node -> divline.pt.y;
	mnode.dx = node -> divline.dx;
	mnode.dy = node -> divline.dy;

	r = ProcessNode(node -> side[0], subbox[0]);
	mnode.children[0] = r;
	for (i = 0; i < 4; i++)
		mnode.bbox[0][i] = subbox[0][i];

	r = ProcessNode(node -> side[1], subbox[1]);
	mnode.children[1] = r;
	for (i = 0; i < 4; i++)
		mnode.bbox[1][i] = subbox[1][i];

	totalbox[BOXLEFT] = MIN(subbox[0][BOXLEFT], subbox[1][BOXLEFT]);
	totalbox[BOXTOP] = MAX(subbox[0][BOXTOP], subbox[1][BOXTOP]);
	totalbox[BOXRIGHT] = MAX(subbox[0][BOXRIGHT], subbox[1][BOXRIGHT]);
	totalbox[BOXBOTTOM] = MIN(subbox[0][BOXBOTTOM], subbox[1][BOXBOTTOM]);

/*      [nodestore_i addElement: &mnode]; */
	memcpy((mapnode_t *) nodestore_i -> data + nodestore_i -> count, &mnode, sizeof(mapnode_t));
	nodestore_i -> count += 1;
	nodestore_i -> data = (mapnode_t *) realloc(nodestore_i -> data,
		sizeof(mapnode_t) * (nodestore_i -> count + 1));

/*      return [nodestore_i count] - 1; */
	return nodestore_i -> count - 1;
}


/*
   =================
   =
   = ProcessNodes
   =
   = Recursively builds the nodes, subsectors, and line lists,
   = then writes the lumps
   =================
 */

void                ProcessNodes(void)
{
	short               worldbounds[4];

/*
   subsecstore_i = [[Storage alloc]
   initCount:              0
   elementSize:    sizeof(mapsubsector_t)
   description:    NULL];
   maplinestore_i = [[Storage alloc]
   initCount:              0
   elementSize:    sizeof(mapseg_t)
   description:    NULL];
   nodestore_i = [[Storage alloc]
   initCount:              0
   elementSize:    sizeof(mapnode_t)
   description:    NULL];
 */
	subsecstore_i = (STORAGE *) SafeMalloc(sizeof(STORAGE));
	subsecstore_i -> data = (mapsubsector_t *) SafeMalloc(sizeof(mapsubsector_t));
	subsecstore_i -> count = 0;
	subsecstore_i -> size = sizeof(mapsubsector_t);

	maplinestore_i = (STORAGE *) SafeMalloc(sizeof(STORAGE));
	maplinestore_i -> data = (mapseg_t *) SafeMalloc(sizeof(mapseg_t));
	maplinestore_i -> count = 0;
	maplinestore_i -> size = sizeof(mapseg_t);

	nodestore_i = (STORAGE *) SafeMalloc(sizeof(STORAGE));
	nodestore_i -> data = (mapnode_t *) SafeMalloc(sizeof(mapnode_t));
	nodestore_i -> count = 0;
	nodestore_i -> size = sizeof(mapnode_t);

	ProcessNode(startnode, worldbounds);

}


/*
   =================
   =
   = ProcessThings
   =
   =================
 */

void                ProcessThings(void)
{
	worldthing_t       *wt;
	mapthing_t          mt;
	int                 count;

/*
   mapthingstore_i = [[Storage alloc]
   initCount:              0
   elementSize:    sizeof(mapthing_t)
   description:    NULL];
 */
	mapthingstore_i = (STORAGE *) SafeMalloc(sizeof(STORAGE));
	mapthingstore_i -> data = (mapthing_t *) SafeMalloc(sizeof(mapthing_t));
	mapthingstore_i -> count = 0;
	mapthingstore_i -> size = sizeof(mapthing_t);

/*
   count = [thingstore_i count];
   wt = [thingstore_i elementAt: 0];
 */
	count = thingstore_i -> count;
	wt = thingstore_i -> data;
	while (count--)
		{
		memset(&mt, 0, sizeof(mt));
		mt.x = wt -> origin.x;
		mt.y = wt -> origin.y;
		mt.angle = wt -> angle;
		mt.type = wt -> type;
		mt.options = wt -> options;
/*              [mapthingstore_i addElement: &mt]; */
		memcpy((mapthing_t *) mapthingstore_i -> data + mapthingstore_i -> count, &mt,
			sizeof(mapthing_t));
		mapthingstore_i -> count += 1;
		mapthingstore_i -> data = (mapthing_t *) realloc(mapthingstore_i -> data,
			sizeof(mapthing_t) * (mapthingstore_i -> count + 1));
		wt++;
		}

}

/*
   =============================================================================
 */

/*
   ==================
   =
   = ProcessSidedef
   =
   ==================
 */

int                 ProcessSidedef(worldside_t * ws)
{
	mapsidedef_t        ms;

	ms.textureoffset = ws -> firstcollumn;
	ms.rowoffset = ws -> firstrow;
	memcpy(ms.toptexture, ws -> toptexture, 8);
	memcpy(ms.bottomtexture, ws -> bottomtexture, 8);
	memcpy(ms.midtexture, ws -> midtexture, 8);
	ms.sector = ws -> sector;

/*      [sdefstore_i addElement: &ms]; */
	memcpy((mapsidedef_t *) sdefstore_i -> data + sdefstore_i -> count, &ms, sizeof(mapsidedef_t));
	sdefstore_i -> count += 1;
	sdefstore_i -> data = (mapsidedef_t *) realloc(sdefstore_i -> data,
		sizeof(mapsidedef_t) * (sdefstore_i -> count + 1));

/*      return [sdefstore_i count]-1; */
	return sdefstore_i -> count - 1;
}

/*
   ==================
   =
   = ProcessLineSideDefs
   =
   = Must be called after BuildSectors
   ==================
 */

void                ProcessLineSideDefs(void)
{
	int                 i,
	                    count;
	maplinedef_t        ld;
	worldline_t        *wl;

/*
   mapvertexstore_i = [[Storage alloc]
   initCount:              0
   elementSize:    sizeof(mapvertex_t)
   description:    NULL];
   ldefstore_i = [[Storage alloc]
   initCount:              0
   elementSize:    sizeof(maplinedef_t)
   description:    NULL];
   sdefstore_i = [[Storage alloc]
   initCount:              0
   elementSize:    sizeof(mapsidedef_t)
   description:    NULL];
 */
	mapvertexstore_i = (STORAGE *) SafeMalloc(sizeof(STORAGE));
	mapvertexstore_i -> data = (mapvertex_t *) SafeMalloc(sizeof(mapvertex_t));
	mapvertexstore_i -> count = 0;
	mapvertexstore_i -> size = sizeof(mapvertex_t);

	ldefstore_i = (STORAGE *) SafeMalloc(sizeof(STORAGE));
	ldefstore_i -> data = (maplinedef_t *) SafeMalloc(sizeof(maplinedef_t));
	ldefstore_i -> count = 0;
	ldefstore_i -> size = sizeof(maplinedef_t);

	sdefstore_i = (STORAGE *) SafeMalloc(sizeof(STORAGE));
	sdefstore_i -> data = (mapsidedef_t *) SafeMalloc(sizeof(mapsidedef_t));
	sdefstore_i -> count = 0;
	sdefstore_i -> size = sizeof(mapsidedef_t);

/*
   count = [linestore_i count];
   wl = [linestore_i elementAt:0];
 */
	count = linestore_i -> count;
	wl = linestore_i -> data;
	for (i = 0; i < count; i++, wl++)
		{
		ld.v1 = UniqueVertex(wl -> p1.x, wl -> p1.y);
		ld.v2 = UniqueVertex(wl -> p2.x, wl -> p2.y);
		ld.flags = wl -> flags;
		ld.special = wl -> special;
		ld.tag = wl -> tag;
		ld.sidenum[0] = ProcessSidedef(&wl -> side[0]);
		if (wl -> flags & ML_TWOSIDED)
			ld.sidenum[1] = ProcessSidedef(&wl -> side[1]);
		else
			ld.sidenum[1] = -1;
/*              [ldefstore_i addElement: &ld]; */

		memcpy((maplinedef_t *) ldefstore_i -> data + ldefstore_i -> count, &ld, sizeof(maplinedef_t));
		ldefstore_i -> count += 1;
		ldefstore_i -> data = (maplinedef_t *) realloc(ldefstore_i -> data,
			sizeof(maplinedef_t) * (ldefstore_i -> count + 1));
		}

}

/*
   =============================================================================
 */

/*
   ==================
   =
   = SaveDoomMap
   =
   ==================
 */

void                SaveDoomMap(void)
{
	printf("\nBuildSectordefs\n");
	BuildSectordefs();

	printf("ProcessThings\n");
	ProcessThings();

	printf("ProcessLineSideDefs\n");
	ProcessLineSideDefs();

	printf("ProcessNodes\n");
	ProcessNodes();

	printf("ProcessSectors\n");
	ProcessSectors();

	printf("\nProcessConnections\n");
	ProcessConnections();

/* all processing is complete, write everything out */
	OutputThings();
	OutputLineDefs();
	OutputSideDefs();
	OutputVertexes();
	OutputSegs();
	OutputSubsectors();
	OutputNodes();
	OutputSectors();
	OutputConnections();
}
