
#include <WFX/scene/OctreeNode.h>
#include <WFX/scene/SceneGraphVisitor.h>
#include <WFX/scene/OctreeBuilder.h>

using namespace WFX;
using namespace WFX::scene;
using namespace WFX::math;
using namespace std;

UInt OctreeNode::Drawn = 0;

OctreeNode::OctreeNode(OctreeNodeID id) {
    NodeID = id;
    Children = new OctreeNode*[8];
    memset(Children, 0, 8*sizeof(OctreeNode*));
}

OctreeNode::~OctreeNode() {
    //delete associated Geometry
    std::list<Spatial*>::iterator it;
    for (it = Geometry.begin(); it != Geometry.end(); it++) {
        delete (*it);
    }
    //delete Children
    for (UInt i=0; i<8; i++) {
        if (Children[i])
            delete Children[i];
    }
    delete[] Children;
}

void OctreeNode::draw(WFX::renderer::Renderer* r) {
    Drawn++;
    //draw childnodes
    for (UInt i=0; i<8; i++) {
        if (Children[i])
            Children[i]->onDraw(r);
    }
    //draw Geometry
    std::list<Spatial*>::iterator it;
    for (it = Geometry.begin(); it != Geometry.end(); it++) {
        (*it)->draw(r);
    }
    WorldBound->draw(r);
}

void OctreeNode::accept(WFX::scene::SceneGraphVisitor& v) {
    v.visitOctreeNode(*this);
}

void OctreeNode::updateDown() {
    if (!Dirty) return;
    //call super class
    Spatial::updateDown();
    //update Children
    for (UInt i=0; i<8; i++) {
        if (Children[i])
            Children[i]->updateDown();
    }
    Dirty = false;
}

void OctreeNode::setBoundingCube(WFX::math::Cube* c) {
    BoundingCube = c;
    setModelBound(c);
}

void OctreeNode::addTriMesh(TriMesh& m, UInt subdiv) {
    //check whether the tricount is larger than the maximim Triangles per node 
    //and the current subdivision is not larger than the maximum subdivisions
    if (   (m.getFaceCount() > OctreeBuilder::getMaxFaces()) 
        && (subdiv < OctreeBuilder::getMaxDivisions())) {

        //create a bool vector for each child node denoting which triangles to use
        vector<bool> triList1(m.getFaceCount());
        vector<bool> triList2(m.getFaceCount());
        vector<bool> triList3(m.getFaceCount());
        vector<bool> triList4(m.getFaceCount());
        vector<bool> triList5(m.getFaceCount());
        vector<bool> triList6(m.getFaceCount());
        vector<bool> triList7(m.getFaceCount());
        vector<bool> triList8(m.getFaceCount());
        //create a bool vector to store whether at least one triangle is in the child node
        vector<bool> childList(8);

        //go through all vertices and determine to which childnode they belong
        Vector3 center = BoundingCube->getCenter();
        Vector3* verts = m.getVertices();
        UInt* conn = m.getConnectivity();
        for (UInt i=0; i<m.getFaceCount(); i++) {
            //check all vertices
            for (UInt k=0; k<3; k++) {

                //check if the vertex lies within the TOP_LEFT_FRONT node
                if ((verts[conn[i*3+k]].X <= center.X) && (verts[conn[i*3+k]].Y >= center.Y) && (verts[conn[i*3+k]].Z >= center.Z)) {
                    triList1[i] = true;
                    childList[0] = true;
                }

                //check if the vertex lies within the TOP_LEFT_BACK node
                if ((verts[conn[i*3+k]].X <= center.X) && (verts[conn[i*3+k]].Y >= center.Y) && (verts[conn[i*3+k]].Z <= center.Z)) {
                    triList2[i] = true;
                    childList[1] = true;
                }

                //check if the vertex lies within the TOP_RIGHT_BACK node
                if ((verts[conn[i*3+k]].X >= center.X) && (verts[conn[i*3+k]].Y >= center.Y) && (verts[conn[i*3+k]].Z <= center.Z)) {
                    triList3[i] = true;
                    childList[2] = true;
                }

                //check if the vertex lies within the TOP_RIGHT_FRONT node
                if ((verts[conn[i*3+k]].X >= center.X) && (verts[conn[i*3+k]].Y >= center.Y) && (verts[conn[i*3+k]].Z >= center.Z)) {
                    triList4[i] = true;
                    childList[3] = true;
                }

                //check if the vertex lies within the BOTTOM_LEFT_FRONT node
                if ((verts[conn[i*3+k]].X <= center.X) && (verts[conn[i*3+k]].Y <= center.Y) && (verts[conn[i*3+k]].Z >= center.Z)) {
                    triList5[i] = true;
                    childList[4] = true;
                }

                //check if the vertex lies within the BOTTOM_LEFT_BACK node
                if ((verts[conn[i*3+k]].X <= center.X) && (verts[conn[i*3+k]].Y <= center.Y) && (verts[conn[i*3+k]].Z <= center.Z)) {
                    triList6[i] = true;
                    childList[5] = true;
                }

                //check if the vertex lies within the BOTTOM_RIGHT_BACK node
                if ((verts[conn[i*3+k]].X >= center.X) && (verts[conn[i*3+k]].Y <= center.Y) && (verts[conn[i*3+k]].Z <= center.Z)) {
                    triList7[i] = true;
                    childList[6] = true;
                }

                //check if the vertex lies within the BOTTOM_RIGHT_FRONT node
                if ((verts[conn[i*3+k]].X >= center.X) && (verts[conn[i*3+k]].Y <= center.Y) && (verts[conn[i*3+k]].Z >= center.Z)) {
                    triList8[i] = true;
                    childList[7] = true;
                }
            }
        }
    
        //easier iteration
        vector<vector<bool>* > lists(8);
        lists[0] = &triList1; lists[1] = &triList2; 
        lists[2] = &triList3; lists[3] = &triList4; 
        lists[4] = &triList5; lists[5] = &triList6;
        lists[6] = &triList7; lists[7] = &triList8;

        bool sd = false;
        //now create child nodes if needed
        for (UInt i=0; i<8; i++) {
            if (childList[i]) { 
                if (!Children[i]) {//create ChildNode
                    sd = true;
                    Children[i] = new OctreeNode((OctreeNodeID)i);
                    Children[i]->setBoundingCube(calcBoundingCube((OctreeNodeID)i));
                }
                Children[i]->addTriMesh(*(m.split(*lists[i])), subdiv+1);
            }
        }
        
        if (sd && subdiv>0)
            delete &m;
        
    } else {
        //add to Geometry
        addGeometry(&m);
    }
}

Cube* OctreeNode::calcBoundingCube(OctreeNodeID id) {
    Vector3 newCenter;
    Vector3 Center = BoundingCube->getCenter();
    Real Width = BoundingCube->getWidth();

    //determine which node
    switch (id) {
        case TOP_LEFT_FRONT:
            newCenter.init(Center.X - Width/4, Center.Y + Width/4, Center.Z + Width/4);
            break;
        case TOP_LEFT_BACK:
            newCenter.init(Center.X - Width/4, Center.Y + Width/4, Center.Z - Width/4);
            break;
        case TOP_RIGHT_BACK:
            newCenter.init(Center.X + Width/4, Center.Y + Width/4, Center.Z - Width/4);
            break;
        case TOP_RIGHT_FRONT:
            newCenter.init(Center.X + Width/4, Center.Y + Width/4, Center.Z + Width/4);
            break;
        case BOTTOM_LEFT_FRONT:
            newCenter.init(Center.X - Width/4, Center.Y - Width/4, Center.Z + Width/4);
            break;
        case BOTTOM_LEFT_BACK:
            newCenter.init(Center.X - Width/4, Center.Y - Width/4, Center.Z - Width/4);
            break;
        case BOTTOM_RIGHT_BACK:
            newCenter.init(Center.X + Width/4, Center.Y - Width/4, Center.Z - Width/4);
            break;
        case BOTTOM_RIGHT_FRONT:
            newCenter.init(Center.X + Width/4, Center.Y - Width/4, Center.Z + Width/4);
            break;
        default:
            THROW("OctreeNode::calcBoundingCube - unexpected node id " << id);
    }

    return new Cube(newCenter, BoundingCube->getWidth()/2);
}

void OctreeNode::addGeometry(Spatial* s) {
    s->updateDown();
    Geometry.push_back(s);
}

void OctreeNode::setChild(OctreeNode* n) {
    if (Children[n->NodeID])
        delete Children[n->NodeID];
    Children[n->NodeID] = n;
}

UInt OctreeNode::childCount() {
    UInt ret = 0;
    for (UInt i=0; i<8; i++) {
        if (Children[i])
            ret++;
    }
    return ret;
}

