//#     Filename:       SpatialEdge.cpp
//#
//#     The SpatialEdge class is defined here.
//#
//#     Author:         Peter Z. Kunszt based on A. Szalay's code
//#
//#     Date:           October 15, 1998
//#
//#		Copyright (C) 2000  Peter Z. Kunszt, Alex S. Szalay, Aniruddha R. Thakar
//#                     The Johns Hopkins University
//#
//#     Modification History:
//#
//#     Oct 18, 2001 : Dennis C. Dinge -- Replaced ValVec with std::vector
//#

#include "SpatialEdge.h"

// ===========================================================================
//
// Macro definitions for readability
//
// ===========================================================================
#define IV(x) tree_.nodes_[index].v_[(x)]
#define IW(x) tree_.nodes_[index].w_[(x)]
#define LAYER tree_.layers_[layerindex_]

// ===========================================================================
//
// Member functions for class SpatialEdge
//
// ===========================================================================

/////////////CONSTRUCTOR//////////////////////////////////
//
SpatialEdge::SpatialEdge(SpatialIndex &tree, size_t layerindex) : tree_(tree), layerindex_(layerindex)
{
    edges_ = new Edge[LAYER.nEdge_ + 1];
    lTab_  = new Edge *[LAYER.nVert_ * 6];

    // initialize lookup table, we depend on that nullptr
    for (size_t i = 0; i < LAYER.nVert_ * 6; i++)
        lTab_[i] = nullptr;

    // first vertex index for the vertices to be generated
    index_ = LAYER.nVert_;
}

/////////////DESTRUCTOR///////////////////////////////////
//
SpatialEdge::~SpatialEdge()
{
    delete[] edges_;
    delete[] lTab_;
}

/////////////MAKEMIDPOINTS////////////////////////////////
// makeMidPoints: interface to this class. Set midpoints of every
//                node in this layer.
void SpatialEdge::makeMidPoints()
{
    size_t c = 0;
    size_t index;

    // build up the new edges

    index = (size_t)LAYER.firstIndex_;
    for (size_t i = 0; i < LAYER.nNode_; i++, index++)
    {
        c = newEdge(c, index, 0);
        c = newEdge(c, index, 1);
        c = newEdge(c, index, 2);
    }
}

/////////////NEWEDGE//////////////////////////////////////
// newEdge: determines whether the edge em is already in the list.  k
//          is the label of the edge within the node Returns index of next
//          edge, if not found, or returns same if it is already there.  Also
//          registers the midpoint in the node.

size_t SpatialEdge::newEdge(size_t emindex, size_t index, int k)
{
    Edge *en, *em;
    size_t swap;

    em = &edges_[emindex];

    switch (k)
    {
        case 0:
            em->start_ = IV(1);
            em->end_   = IV(2);
            break;
        case 1:
            em->start_ = IV(0);
            em->end_   = IV(2);
            break;
        case 2:
            em->start_ = IV(0);
            em->end_   = IV(1);
            break;
    }

    // sort the vertices by increasing index

    if (em->start_ > em->end_)
    {
        swap       = em->start_;
        em->start_ = em->end_;
        em->end_   = swap;
    }

    // check all previous edges for a match, return pointer if
    // already present, log the midpoint with the new face as well

    if ((en = edgeMatch(em)) != nullptr)
    {
        IW(k) = en->mid_;
        return emindex;
    }

    // this is a new edge, immediately process the midpoint,
    // and save it with the nodes and the edge as well

    insertLookup(em);
    IW(k)    = getMidPoint(em);
    em->mid_ = IW(k);
    return ++emindex;
}

/////////////INSERTLOOKUP/////////////////////////////////
// insertLookup: insert the edge em into the lookup table.
//               indexed by em->start_.
//               Every vertex has at most 6 edges, so only
//               that much lookup needs to be done.
void SpatialEdge::insertLookup(Edge *em)
{
    int j = 6 * em->start_;
    int i;

    // do not loop beyond 6

    for (i = 0; i < 6; i++, j++)
    {
        if (lTab_[j] == nullptr)
        {
            lTab_[j] = em;
            return;
        }
    }
}

/////////////EDGEMATCH////////////////////////////////////
// edgeMatch: fast lookup using the first index em->start_.
//            return pointer to edge if matches, null if not.
SpatialEdge::Edge *SpatialEdge::edgeMatch(Edge *em)
{
    int i = 6 * em->start_;

    while (lTab_[i] != nullptr)
    {
        if (em->end_ == lTab_[i]->end_)
            return lTab_[i];

        i++;
    }
    return nullptr;
}

/////////////GETMIDPOINT//////////////////////////////////
// getMidPoint: compute the midpoint of the edge using vector
//              algebra and return its index in the vertex list
size_t SpatialEdge::getMidPoint(Edge *em)
{
    tree_.vertices_[index_] = tree_.vertices_[em->start_] + tree_.vertices_[em->end_];
    tree_.vertices_[index_].normalize();
    return index_++;
}
