/**     
 **       File       : glmesh.cxx
 **       Description: Implementations of CGlMesh class
 **/
#include "precomp.h"
#pragma hdrstop


#include "glmesh.h"
#include "pmerrors.h"


/**************************************************************************/

/*
 *  CGlMesh: Constructor
 */
CGlMesh::CGlMesh()
{
    //Dynamically allocated arrays
    m_matArray = NULL;
    m_varray = NULL;
    m_narray = NULL;
    m_tarray = NULL;
    m_wedgelist = NULL;
    m_fnei = NULL;
    m_facemap = NULL;

    m_numFaces =
    m_numWedges =
    m_numVerts = 
    m_numMaterials = 
    m_numTextures = 0;
}


/*
 *  CGlMesh: Destructor
 */
CGlMesh::~CGlMesh()
{
    delete [] m_matArray;
    delete [] m_varray;
    delete [] m_narray;
    delete [] m_tarray;
    delete [] m_wedgelist;
    delete [] m_fnei;
    delete [] m_facemap;
}

/*
 *  CGlMesh: Print
 */
STDMETHODIMP CGlMesh::Print(ostream& os)
{

    os << "\n\nMaterials:";
    for (int i=0; i<m_numMaterials; ++i)
    {
        LPGLmaterial lpglm = &m_matArray[i];
      
        os << "\n\nMaterial [" << i << "] :";
        os << "\nShininess : " << lpglm->shininess;
        os << "\nDiffuse  : (" << lpglm->diffuse.r << ", "
           << lpglm->diffuse.g << ", "
           << lpglm->diffuse.b << ", "
           << lpglm->diffuse.a << ")";
        os << "\nSpecular : (" << lpglm->specular.r << ", "
           << lpglm->specular.g << ", "
           << lpglm->specular.b << ", "
           << lpglm->specular.a << ")";
        os << "\nEmissive : (" << lpglm->emissive.r << ", "
           << lpglm->emissive.g << ", "
           << lpglm->emissive.b << ", "
           << lpglm->emissive.a << ")";
        os << "\nNumber of faces: " << m_matcnt[i];
        for (int j=0; j< m_matcnt[i]; j++)
        {
#ifdef __MATPOS_IS_A_PTR
            os << "\n(" << m_matpos[i][j].w[0]   << ","
               << (m_matpos[i][j]).w[1] << ","
               << (m_matpos[i][j]).w[2] << ")";
#else
            os << "\n(" << (m_farray[m_matpos[i] + j]).w[0]   << ","
               << (m_farray[m_matpos[i] + j]).w[0]  << ","
               << (m_farray[m_matpos[i] + j]).w[0]  << ")";
#endif
        }
    }

    os << "\n\nWedge connectivity:";
    for (i=0; i<m_numWedges; ++i)
    {
        os << "\n" << m_wedgelist[i];
    }
    
    os << "\n\nWedge data:";
    for (i=0; i<m_numWedges; ++i)
    {
        os   << "\n(" << m_varray[i].x << ", "
                      << m_varray[i].y << ", "
                      << m_varray[i].z << ") "
             << "  (" << m_narray[i].x << ", "
                      << m_narray[i].y << ", "
                      << m_narray[i].z << ") "
             << "  (" << m_tarray[i].s << ", "
                      << m_tarray[i].t << ") ";
    }
    return S_OK;
}

/*
 *  CGlMesh: Render
 */
STDMETHODIMP CGlMesh::Render(RenderType rt)
{
    if (rt == GLPM_SOLID)
    {
        glVertexPointer(3, GL_FLOAT, 0, (void *)&(m_varray[0].x));
        glNormalPointer (GL_FLOAT, 0, (void *)&(m_narray[0].x));
        glTexCoordPointer (2, GL_FLOAT, 0, (void *)&(m_tarray[0].s));
        
        glEnableClientState (GL_VERTEX_ARRAY);
        glEnableClientState (GL_NORMAL_ARRAY);
        
        for (int i=0; i<m_numMaterials; i++)
        {
            LPGLmaterial lpglm = &(m_matArray[i]);
            
            if (m_matcnt[i] == (WORD) 0) continue;
            
            glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, lpglm->shininess);
            glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, 
                         (GLfloat *) &(lpglm->specular));
            glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,  
                         (GLfloat *) &(lpglm->diffuse));
            glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, 
                     (GLfloat *) &(lpglm->emissive));
            glDrawElements (GL_TRIANGLES, (GLuint) m_matcnt[i]*3, 
                            GL_UNSIGNED_SHORT, 
#ifdef __MATPOS_IS_A_PTR
                            (void *) m_matpos[i]);
#else
                            (void *) &(m_farray[m_matpos[i]]));
#endif
        }
        return S_OK;
    }
    else
    {
        return E_NOTIMPL;
    }
}

PHASHENTRY* hashtable;
PHASHENTRY hashentries;
int freeptr, maxptr;

void CGlMesh::HashAdd(WORD va, WORD vb, WORD f)
{
#ifdef _DEBUG
    if (va > m_numWedges || va < 0)
        throw CHashOvrflw();
#endif
    for (PHASHENTRY* t = &(hashtable[va]); *t; t = &((*t)->next));
    PHASHENTRY p = &(hashentries[freeptr++]);
    p->f = f;
    p->v2 = vb;
    p->next = NULL;
    *t=p;
}

WORD CGlMesh::HashFind(WORD va, WORD vb)
{
#ifdef _DEBUG
    if (va > m_baseWedges || va < 0)
        throw CHashOvrflw();
#endif
    for (PHASHENTRY* t = &(hashtable[va]); *t; t = &((*t)->next))
    {
        if ((*t)->v2 == vb)
        {
            return (*t)->f;
        }
    }
    return USHRT_MAX;
}


void CGlMesh::ComputeAdjacency(void)
{
    freeptr = 0;
    maxptr = m_numFaces*3;
    hashtable = new PHASHENTRY[m_numWedges];

    // An entry for each 3 edges of each face in base mesh
    hashentries = new hashentry[maxptr];
    if (!hashtable)
        throw CExNewFailed();
    memset(hashtable, 0, sizeof(PHASHENTRY)*m_numWedges);

    /*
     * For each group of faces
     */
    for(int i=0; i<m_numMaterials; ++i)
    { 
        /* 
         * For each face in the group
         */
#ifdef __MATPOS_IS_A_PTR
        for (int k=0; k<m_matcnt[i]; ++k)
        { 
            int v1 = FindVertexIndex((m_matpos[i][k]).w[0]);
            int v2 = FindVertexIndex((m_matpos[i][k]).w[1]);
            int v3 = FindVertexIndex((m_matpos[i][k]).w[2]);
            
            int fi = GetFaceIndex(i,k);
            HashAdd(v1,v2,fi);
            HashAdd(v2,v3,fi);
            HashAdd(v3,v1,fi);
        }
#else
        for (int k=m_matpos[i]; k<(m_matpos[i]+m_matcnt[i]); ++k)
        { 
            int v1 = FindVertexIndex((m_farray[k]).w[0]);
            int v2 = FindVertexIndex((m_farray[k]).w[1]);
            int v3 = FindVertexIndex((m_farray[k]).w[2]);
            
            HashAdd(v1,v2,k);
            HashAdd(v2,v3,k);
            HashAdd(v3,v1,k);
        }
#endif
    }

#ifdef _DEBUG
    if (freeptr > maxptr)
        throw CHashOvrflw();
#endif
    /*
     * For each group of faces
     */
    for(i=0; i<m_numMaterials; ++i)
    { 
        /* 
         * For each face in the group
         */
#ifdef __MATPOS_IS_A_PTR
        for (int k=0; k<m_matcnt[i]; ++k)
        { 
            int v1 = FindVertexIndex((m_matpos[i][k]).w[0]);
            int v2 = FindVertexIndex((m_matpos[i][k]).w[1]);
            int v3 = FindVertexIndex((m_matpos[i][k]).w[2]);

            int fi = GetFaceIndex(i,k);
            m_fnei[fi][0] = HashFind(v3,v2);
            m_fnei[fi][1] = HashFind(v1,v3);
            m_fnei[fi][2] = HashFind(v2,v1);
        }
#else
        for (int k=m_matpos[i]; k<(m_matpos[i]+m_matcnt[i]); ++k)
        { 
            int v1 = FindVertexIndex((m_farray[k]).w[0]);
            int v2 = FindVertexIndex((m_farray[k]).w[1]);
            int v3 = FindVertexIndex((m_farray[k]).w[2]);

            m_fnei[k][0] = HashFind(v3,v2);
            m_fnei[k][1] = HashFind(v1,v3);
            m_fnei[k][2] = HashFind(v2,v1);
        }
#endif
    }
    delete [] hashtable;
    delete [] hashentries;
}

#if 0
STDMETHODIMP AddWedge (WORD vertex_id, GLnormal& n, 
                       GLtexCoord& t,  DWORD* const wedge_id)
{
    WORD w;
  
    w = m_numWedges++;
    m_wedgelist[w] = m_wedgelist[vertex_id];
    m_wedgelist[vertex_id] = w;
    
    m_varray[w] = m_varray[vertex_id];
    m_narray[w] = n;
    m_tarray[w] = t;
    
    *wedge_id = w;
    return S_OK;
}

STDMETHODIMP AddWedge (Glvertex &v, GLnormal& n, GLtexCoord& t, 
                       DWORD* const wedge_id)
{
    WORD w;
  
    w = m_numWedges++;
    m_wedgelist[w] = w;
    
    m_varray[w] = v;
    m_narray[w] = n;
    m_tarray[w] = t;
    
    *wedge_id = w;
    return S_OK;
}



STDMETHODIMP AddWedge (Glvertex &v)
{
    WORD w;
  
    w = m_numWedges++;
    m_wedgelist[w] = w;
    
    m_varray[w] = v;
    //m_narray[w] = n;
    //m_tarray[w] = t;
    
    *wedge_id = w;
    return S_OK;
}



STDMETHODIMP AddWedge (WORD vertex_id, WORD old_wedge_id, 
                       DWORD* const wedge_id)
{
    WORD w;
  
    w = m_numWedges++;

    /*
     * Add wnl to the list of wedges sharing vertex_id
     */
    m_wedgelist[w] = m_wedgelist[vertex_id];
    m_wedgelist[vertex_id] = w;
    
    /*
     * Copy wedge attributes
     */
    m_varray[w] = m_varray[vertex_id];
    m_narray[w] = m_narray[old_wedge_id];
    m_tarray[w] = m_tarray[old_wedge_id];
    
    *wedge_id = w;
    return S_OK;
}



STDMETHODIMP AddFace (WORD matid, GLface& f)
{
    return S_OK;
}
#endif