#include "pw/core/mesh.hpp"
#include "pw/core/debug.hpp"
#include "pw/core/serialize.hpp"

namespace pw {


void geometry::compute_normals()
{
    // assumption is that we have some array as the vertices
    vertex3array_t normals; normals.resize(_vertices.size());

    // for indexed-faceset
    for (size_t i = 1; i < _indices.size()-1;i++)
    {
        // left index and right index
        auto il = (i - 1 + _indices.size()) % _indices.size();
        auto ir = (i + 1 + _indices.size()) % _indices.size();

        // translate to actual indices
        auto ci = _indices[ i];
        auto li = _indices[il];
        auto ri = _indices[ir];

        // calculate edges between vertices
        auto edgeLeft = vector3( _vertices[li] - _vertices[ci] );
        auto edgeRight = vector3( _vertices[ri] - _vertices[ci] );

        // calculate counter clockwise and normalize
        auto N = edgeRight.cross(edgeLeft).normalized();

        // NOTE that addition is ugly
        normals[ci] = N + normals[ci];
        normals[li] = N + normals[li];
        normals[ri] = N + normals[ri];
    }

    for (auto & n : normals) {
        n.normalize();
    }

    this->set_normals(normals);

    for (auto N : normals) {
        debug::s() << "( " << serialize::matrix(N.transposed()) << ") ";
    }


    // for triangle-strip


    // for triangle-fan


    // now set back
}

void geometry::set_vertices(const geometry::vertex3array_t &v)
{
    // first set vertices
    _vertices = v;

    // update bounds
    compute_bounds();
}

void geometry::set_normals(const geometry::vertex3array_t &v)
{
    _normals = v;
}

const geometry::vertex3array_t &geometry::normals() const
{
    return _normals;
}

void geometry::apply(const matrix4x4 &m)
{
    // apply transformation to all vertices
    for (auto &v : _vertices)
        v = vector4(m * v.homogenous()).project();

    // apply to normals
    for(auto &n : _normals)
        n = vector4(m * n.homogenous(0)).xyz();

    // recompute bounds
    compute_bounds();
}

void geometry::compute_bounds()
{
    // only do if there are vertices
    if (_vertices.size()) {

        // reset
        _aabb.max = _aabb.min = _vertices.front();

        // update
        for (auto const & v : _vertices) {
            _aabb.max.x() = std::max(v.x(),_aabb.max.x());
            _aabb.max.y() = std::max(v.y(),_aabb.max.y());
            _aabb.max.z() = std::max(v.z(),_aabb.max.z());
            _aabb.min.x() = std::min(v.x(),_aabb.min.x());
            _aabb.min.y() = std::min(v.y(),_aabb.min.y());
            _aabb.min.z() = std::min(v.z(),_aabb.min.z());
        }
    }
}

}
