//
// Lol Engine
//
// Copyright: (c) 2010-2013 Sam Hocevar <sam@hocevar.net>
//            (c) 2009-2013 Cédric Lecacheur <jordx@free.fr>
//            (c) 2009-2013 Benjamin "Touky" Huet <huet.benjamin@gmail.com>
//   This program is free software; you can redistribute it and/or
//   modify it under the terms of the Do What The Fuck You Want To
//   Public License, Version 2, as published by Sam Hocevar. See
//   http://www.wtfpl.net/ for more details.
//

//
// The EasyMesh class
// ------------------
//

#include <lol/engine-internal.h>

namespace lol
{

//-----------------------------------------------------------------------------
//helpers func to retrieve a vertex.
int VertexDictionnary::FindVertexMaster(const int search_idx)
{
    //Resolve current vertex idx in the dictionnary (if exist)
    for (int j = 0; j < vertex_list.Count(); j++)
        if (vertex_list[j].m1 == search_idx)
            return vertex_list[j].m3;
    return VDictType::DoesNotExist;
}

//-----------------------------------------------------------------------------
//retrieve a list of matching vertices, doesn't include search_idx.
bool VertexDictionnary::FindMatchingVertices(const int search_idx, array<int> &matching_ids)
{
    int cur_mast = FindVertexMaster(search_idx);

    if (cur_mast == VDictType::DoesNotExist || cur_mast == VDictType::Alone)
        return false;

    if (cur_mast == VDictType::Master)
        cur_mast = search_idx;
    else
        matching_ids << vertex_list[cur_mast].m1;

    for (int j = 0; j < vertex_list.Count(); j++)
        if (vertex_list[j].m3 == cur_mast && vertex_list[j].m1 != search_idx)
            matching_ids << vertex_list[j].m1;

    return (matching_ids.Count() > 0);
}

//-----------------------------------------------------------------------------
//Will return connected vertices (through triangles), if returned vertex has matching ones, it only returns the master.
bool VertexDictionnary::FindConnectedVertices(const int search_idx, const array<uint16_t> &tri_list, const int tri0, array<int> &connected_vert, array<int> const *ignored_tri)
{
    array<int> connected_tri;
    FindConnectedTriangles(search_idx, tri_list, tri0, connected_tri, ignored_tri);

    for (int i = 0; i < connected_tri.Count(); i++)
    {
        for (int j = 0; j < 3; j++)
        {
            int v_indice = tri_list[connected_tri[i] + j];
            if (v_indice != search_idx)
            {
                int found_master = FindVertexMaster(tri_list[connected_tri[i] + j]);
                if (found_master == VDictType::Alone || found_master == VDictType::Master)
                    found_master = v_indice;
                if (found_master != search_idx)
                {
                    bool already_exist = false;
                    for (int k = 0; !already_exist && k < connected_vert.Count(); k++)
                        if (connected_vert[k] == found_master)
                            already_exist = true;
                    if (!already_exist)
                        connected_vert << found_master;
                }
            }
        }
    }
    return (connected_vert.Count() > 0);
}
//-----------------------------------------------------------------------------
bool VertexDictionnary::FindConnectedTriangles(const int search_idx, const array<uint16_t> &tri_list, const int tri0, array<int> &connected_tri, array<int> const *ignored_tri)
{
    return FindConnectedTriangles(ivec3(search_idx, search_idx, search_idx), tri_list, tri0, connected_tri, ignored_tri);
}
//-----------------------------------------------------------------------------
bool VertexDictionnary::FindConnectedTriangles(const ivec2 &search_idx, const array<uint16_t> &tri_list, const int tri0, array<int> &connected_tri, array<int> const *ignored_tri)
{
    return FindConnectedTriangles(ivec3(search_idx, search_idx.x), tri_list, tri0, connected_tri, ignored_tri);
}
//-----------------------------------------------------------------------------
bool VertexDictionnary::FindConnectedTriangles(const ivec3 &search_idx, const array<uint16_t> &tri_list, const int tri0, array<int> &connected_tri, array<int> const *ignored_tri)
{
    int needed_validation = 0;
    array<int> vert_list[3];
    for (int i = 0; i < 3; i++)
    {
        //Small optim since above func will use this one
        if ((i == 1 && search_idx[0] == search_idx[1]) ||
            (i == 2 && (search_idx[0] == search_idx[2] || search_idx[1] == search_idx[2])))
            continue;
        else
        {
            //increment the validation info, hence empty list aren't taken into account.
            needed_validation++;
            vert_list[i] << search_idx[i];
            FindMatchingVertices(search_idx[i], vert_list[i]);
        }
    }

    for (int i = tri0; i < tri_list.Count(); i += 3)
    {
        if (ignored_tri)
        {
            bool should_pass = false;
            for (int j = 0; !should_pass && j < ignored_tri->Count(); j++)
                if ((*ignored_tri)[j] == i)
                    should_pass = true;
            if (should_pass)
                continue;
        }
        int found_validation = 0;
        for (int j = 0; j < 3; j++)
        {
            bool validated = false;
            for (int k = 0; !validated && k < vert_list[j].Count(); k++)
                for (int l = 0; !validated && l < 3; l++)
                    if (vert_list[j][k] == tri_list[i + l])
                        validated = true;
            found_validation += (validated)?(1):(0);
        }
        //triangle is validated store it
        if (found_validation == needed_validation)
            connected_tri << i;
    }

    return (connected_tri.Count() > 0);
}

//-----------------------------------------------------------------------------
//Will update the given list with all the vertices on the same spot.
void VertexDictionnary::RegisterVertex(const int vert_id, const vec3 vert_coord)
{
    for (int j = 0; j < vertex_list.Count(); j++)
        if (vertex_list[j].m1 == vert_id)
            return;

    //First, build the vertex Dictionnary
    int i = 0;
    for (; i < master_list.Count(); i++)
    {
        int cur_mast  = master_list[i];
        int cur_id    = vertex_list[cur_mast].m1;
        vec3 cur_loc  = vertex_list[cur_mast].m2;
        int &cur_type = vertex_list[cur_mast].m3;

        if (cur_id == vert_id)
            return;

        if (sqlength(cur_loc - vert_coord) < TestEpsilon::Get())
        {
            if (cur_type == VDictType::Alone)
                cur_type = VDictType::Master;
            vertex_list.Push(vert_id, vert_coord, cur_mast);
            return;
        }
    }

    //We're here because we couldn't find any matching vertex
    master_list.Push((int)vertex_list.Count());
    vertex_list.Push(vert_id, vert_coord, VDictType::Alone);
}

//-----------------------------------------------------------------------------
//Will update the given list with all the vertices on the same spot.
void VertexDictionnary::RemoveVertex(const int vert_id)
{
    int j = 0;
    for (; j < vertex_list.Count(); j++)
        if (vertex_list[j].m1 == vert_id)
            break;

    if (vertex_list[j].m3 == VDictType::Master)
    {
        int jf = -1;
        //change all the master ref in the list
        for (int i = 0; i < vertex_list.Count(); i++)
        {
            if (vertex_list[i].m3 == j)
            {
                if (jf < 0)
                {
                    jf = i;
                    vertex_list[i].m3 = VDictType::Master;
                }
                else
                    vertex_list[i].m3 = jf;
            }
        }
    }
    vertex_list.Remove(j);
    for (int i = 0; i < master_list.Count(); i++)
        if (master_list[j] == j)
            break;
}

} /* namespace lol */
