/* bspbuild.c
 *
 * BSP tree builder - builds a bsp tree from a BRender model
 *
 * Created: 4/7/97
 */

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

#include "brender.h"
#include "dosio.h"

#include "mci.h"
#include "bsp.h"
#include "collide.h"

#define BSPTHRESH 0.01

static void PlacePlaneInTree(br_vector3 *v1, br_vector3 *v2, br_vector3 *v3,
                             c_bsp *node)
{
    br_scalar diff1, diff2, diff3;

    diff1 = c_CompareVertexWithPlane(v1, &node->plane);
    diff2 = c_CompareVertexWithPlane(v2, &node->plane);
    diff3 = c_CompareVertexWithPlane(v3, &node->plane);

    if((diff1 < -BSPTHRESH) || (diff2 < -BSPTHRESH) || (diff3 < -BSPTHRESH))
        if(node->in)
            PlacePlaneInTree(v1, v2, v3, node->in);
        else {
            node->in = BrResAllocate(node, sizeof(c_bsp),
                BR_MEMORY_APPLICATION);
            BrPlaneEquation(&node->in->plane, v1, v2, v3);
        }

    if((diff1 > BSPTHRESH) || (diff2 > BSPTHRESH) || (diff3 > BSPTHRESH))
        if(node->out)
            PlacePlaneInTree(v1, v2, v3, node->out);
        else {
            node->out = BrResAllocate(node, sizeof(c_bsp),
                BR_MEMORY_APPLICATION);
            BrPlaneEquation(&node->out->plane, v1, v2, v3);
        }
}

static c_bsp *BuildBspTreeFromModel(br_model *model)
{
    c_bsp *root;
    br_face *face;
    int i;

    if(model->nfaces <= 0)
        return NULL;

    root = BrResAllocate(NULL, sizeof(c_bsp), BR_MEMORY_APPLICATION);

    face = model->faces;
    BrPlaneEquation(&root->plane,
        &model->vertices[face->vertices[0]].p,
        &model->vertices[face->vertices[1]].p,
        &model->vertices[face->vertices[2]].p);

    if(model->nfaces == 1)
        return root;

    face++;
    for(i = 1; i < model->nfaces; i++, face++){
        PlacePlaneInTree(&model->vertices[face->vertices[0]].p,
            &model->vertices[face->vertices[1]].p,
            &model->vertices[face->vertices[2]].p,
            root);
    }

    return root;
}

static c_mci *GetMci(br_model *model, br_uint_32 flags)
{
    c_mci *mci;
    int i;

    mci = BrResAllocate(NULL, sizeof(c_mci), BR_MEMORY_APPLICATION);

    mci->lowest = BR_SCALAR(10000);
    mci->highest = BR_SCALAR(-10000);
    for(i = 0; i < model->nvertices; i++){
        if(model->vertices[i].p.v[1] < mci->lowest)
            mci->lowest = model->vertices[i].p.v[1];
        if(model->vertices[i].p.v[1] > mci->highest)
            mci->highest = model->vertices[i].p.v[1];
    }
    mci->flags = flags;
    mci->bsp = BuildBspTreeFromModel(model);

    return mci;
}

#if 0
static void TestThing(br_model *model, c_bsp *tree)
{
    br_pixelmap *front, *back, *zbuffer;
    br_model *thing;
    br_actor *world, *light, *camera, *m1, *m2, *m2b;
    br_int_32 mousex, mousey;
    br_uint_32 mousebuttons;
    br_matrix34 to, tob;
    br_vector4 *plane;

    thing = BrModelLoad("arrow.dat");
    BrModelAdd(thing);
    BrModelAdd(model);

    front = DOSGfxBegin(NULL);
    back = BrPixelmapMatch(front, BR_PMMATCH_OFFSCREEN);
    zbuffer = BrPixelmapMatch(back, BR_PMMATCH_DEPTH_16);

    world = BrActorAllocate(BR_ACTOR_NONE, NULL);
    light = BrActorAllocate(BR_ACTOR_LIGHT, NULL);
    camera = BrActorAllocate(BR_ACTOR_CAMERA, NULL);
    m1 = BrActorAllocate(BR_ACTOR_MODEL, NULL);
    m2 = BrActorAllocate(BR_ACTOR_MODEL, NULL);
    m2b = BrActorAllocate(BR_ACTOR_MODEL, NULL);

    BrActorAdd(world, light);
    BrActorAdd(world, camera);
    BrActorAdd(world, m1);
    BrActorAdd(world, m2);
    BrActorAdd(world, m2b);

    BrMatrix34Translate(&camera->t.t.mat, 0, 0, BR_SCALAR(2));
    ((br_camera *)camera->type_data)->type = BR_CAMERA_PERSPECTIVE_OLD;

    BrMatrix34Translate(&m2->t.t.mat, 0, BR_SCALAR(-0.5), 0);
    BrMatrix34Translate(&m2b->t.t.mat, 0, BR_SCALAR(0.5), 0);

    m1->model = model;
    m2->model = thing;
    m2b->model = thing;

    BrLightEnable(light);

    BrZbBegin(front->type, zbuffer->type);

    DOSKeyBegin();
    DOSMouseBegin();

    while(!DOSKeyTest(SC_Q, 0, 0)){
        BrZbSceneRender(world, camera, back, zbuffer);
        BrPixelmapDoubleBuffer(front, back);
        mousex = mousey = 0;
        DOSMouseRead(&mousex, &mousey, &mousebuttons);
        if(mousebuttons & BR_MSM_BUTTONL){
                BrMatrix34PreRotateX(&m1->t.t.mat, BR_ANGLE_DEG(
                    BR_MUL(BR_SCALAR(mousex), BR_SCALAR(0.1))));
                BrMatrix34PreRotateY(&m1->t.t.mat, BR_ANGLE_DEG(
                    BR_MUL(BR_SCALAR(mousey), BR_SCALAR(0.1))));
        } else {
            if(mousebuttons & BR_MSM_BUTTONR)
                BrMatrix34PostTranslate(&m1->t.t.mat,
                    BR_MUL(BR_SCALAR(mousex), BR_SCALAR(0.001)), 0,
                    BR_MUL(BR_SCALAR(mousey), BR_SCALAR(0.001)));
            else
                BrMatrix34PostTranslate(&m1->t.t.mat,
                    BR_MUL(BR_SCALAR(mousex), BR_SCALAR(0.001)),
                    BR_MUL(BR_SCALAR(mousey), BR_SCALAR(0.001)), 0);
        }

        /* These two lines are the important bits for collision detection.
         * m2 is the points being tested, m1 is the model it's being tested
         * with, tree is a bsp tree of model m1.  If the value is less than
         * zero then point m2 is inside model m1.
         */
        BrActorToActorMatrix34(&to, m2, m1);
        BrActorToActorMatrix34(&tob, m2b, m1);
        if(c_CompareLineWithTree(&plane, (br_vector3 *)&to.m[3],
            (br_vector3 *)&tob.m[3], tree) < 0)
            BrPixelmapFill(back, 100);
        else
            BrPixelmapFill(back, 0);
        BrPixelmapFill(zbuffer, 0xffffffff);
    }

    DOSMouseEnd();
    DOSKeyEnd();
    BrZbEnd();
    DOSGfxEnd();
}
#endif

int main(int argc, char *argv[])
{
    char *modelfilename = NULL, *mcifilename = NULL, *cp = NULL;
    br_model *model;
    br_scalar compare;
    c_mci *mci;
    br_uint_32 flags = C_MCI_ALL;

    if(argc < 2){
        fprintf(stderr, "Syntax: MCIBUILD <model file> [-{abj}] [-o <mci file>]");
        return 1;
    }

    while(argv++, --argc)
        if(**argv == '-'){
			/*
			 * Got one or more flags
			 */
			for (cp = *argv + 1; *cp; cp++) {
                if(strchr("o", *cp)){
					argv++;
					if(--argc == 0)
                        BR_ERROR1("The %c option requires an argument.", *cp);
				}
                switch(*cp){
                    case '?':
                        fprintf(stderr,
                            "Syntax: MCIBUILD <model file> [-{abj}] [-o <mci file>]");
                        exit(0);
                    case 'o':
                        if(mcifilename != NULL)
                            BR_ERROR0("Output file name specified twice.");
                        mcifilename = *argv;
                        break;
                    case 'a':
                        flags &= ~C_MCI_ALL;
                        break;
                    case 'b':
                        flags &= ~C_MCI_BULLET;
                        break;
                    case 'j':
                        flags &= ~C_MCI_OBJECT;
                        break;
                    default:
                        BR_ERROR1("unknown option '%c'.\n", *cp);
                        break;
				}
			}
		} else {
            if(modelfilename != NULL){
                fprintf(stderr, "Model file name specified twice.");
                return 1;
            }
            modelfilename = *argv;
		}

    BrBegin();

    if(!(model = BrModelLoad(modelfilename)))
        BR_ERROR1("Could not find file %s.\n", modelfilename);

    mci = GetMci(model, flags);

    c_MciSave(mcifilename, mci);

    BrEnd();

    return 0;
}

