#ifndef MXSTDMODEL_INCLUDED // -*- C++ -*-
#define MXSTDMODEL_INCLUDED
#if !defined(__GNUC__)
#  pragma once
#endif

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

  MxStdModel

  Copyright (C) 1998 Michael Garland.  See "COPYING.txt" for details.
  
  $Id: MxStdModel.h 5690 2005-02-14 14:21:56Z rivol $

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


#include "MxBlockModel.h"

typedef MxSizedDynBlock<unsigned int, 6> MxFaceList;
typedef MxSizedDynBlock<unsigned int, 6> MxVertexList;
typedef MxDynBlock<MxEdge> MxEdgeList;

class MxPairContraction
{
public:
    MxPairContraction() { }
    MxPairContraction(const MxPairContraction& c) { *this = c; }

    MxPairContraction& operator=(const MxPairContraction& c);

    MxVertexID v1, v2;
    float dv1[3], dv2[3];  // dv2 is not really necessary

    uint delta_pivot;
    MxFaceList delta_faces;
    MxFaceList dead_faces;
};

class MxFaceContraction
{
public:

    MxFaceID f;
    float dv1[3], dv2[3], dv3[3];

    MxFaceList delta_faces;
    MxFaceList dead_faces;
};

typedef MxPairContraction MxPairExpansion;

// Masks for internal tag bits
#define MX_VALID_FLAG 0x01
#define MX_PROXY_FLAG 0x02
#define MX_TOUCHED_FLAG 0x04

class MxStdModel : public MxBlockModel
{
private:
    struct vertex_data {
        unsigned char mark, tag;             // Internal tag bits
        unsigned char user_mark, user_tag;   // External tag bits
    };
    struct face_data {
        unsigned char mark, tag;             // Internal tag bits
        unsigned char user_mark, user_tag;   // External tag bits
    };

    MxDynBlock<vertex_data> v_data;
    MxDynBlock<face_data> f_data;
    MxDynBlock<MxFaceList *> face_links;

protected:

    //
    // Accessors for internal tag and mark bits
    uint v_check_tag(MxVertexID i, uint tag) const {return v_data(i).tag&tag;}
    void v_set_tag(MxVertexID i, uint tag) { v_data(i).tag |= tag; }
    void v_unset_tag(MxVertexID i, uint tag) { v_data(i).tag &= ~tag; }
    unsigned char vmark(MxVertexID i) const { return v_data(i).mark; }
    void vmark(MxVertexID i, unsigned char m) { v_data(i).mark = m; }

    uint f_check_tag(MxFaceID i, uint tag) const { return f_data(i).tag&tag; }
    void f_set_tag(MxFaceID i, uint tag) { f_data(i).tag |= tag; }
    void f_unset_tag(MxFaceID i, uint tag) { f_data(i).tag &= ~tag; }
    unsigned char fmark(MxFaceID i) const { return f_data(i).mark; }
    void fmark(MxFaceID i, unsigned char m) { f_data(i).mark = m; }

protected:
    MxVertexID alloc_vertex(float, float, float);
    void free_vertex(MxVertexID);
    void free_face(MxFaceID);
    MxFaceID alloc_face(MxVertexID, MxVertexID, MxVertexID);
    void init_face(MxFaceID);

public:
    MxStdModel(unsigned int nvert, unsigned int nface)
	: MxBlockModel(nvert,nface),
	  v_data(nvert), f_data(nface), face_links(nvert)
	{
	}
    virtual ~MxStdModel();
    MxStdModel *clone();

    ////////////////////////////////////////////////////////////////////////
    //  Tagging and marking
    //
    uint vertex_is_valid(MxVertexID i) const
	{ return v_check_tag(i,MX_VALID_FLAG); }
    void vertex_mark_valid(MxVertexID i) { v_set_tag(i,MX_VALID_FLAG); }
    void vertex_mark_invalid(MxVertexID i) { v_unset_tag(i,MX_VALID_FLAG); }

    uint face_is_valid(MxFaceID i) const {return f_check_tag(i,MX_VALID_FLAG);}
    void face_mark_valid(MxFaceID i) { f_set_tag(i,MX_VALID_FLAG); }
    void face_mark_invalid(MxFaceID i) { f_unset_tag(i,MX_VALID_FLAG); }

    uint vertex_is_proxy(MxVertexID i) const
	{ return v_check_tag(i,MX_PROXY_FLAG); }
    void vertex_mark_proxy(MxVertexID i) { v_set_tag(i,MX_PROXY_FLAG); }
    void vertex_mark_nonproxy(MxVertexID i) { v_unset_tag(i,MX_PROXY_FLAG); }

    //
    // Accessors for external tag and mark bits
    uint vertex_check_tag(MxVertexID i, uint tag) const
	{ return v_data(i).user_tag&tag; }
    void vertex_set_tag(MxVertexID i, uint tag) { v_data(i).user_tag|=tag; }
    void vertex_unset_tag(MxVertexID i, uint tag) {v_data(i).user_tag&= ~tag;}
    unsigned char vertex_mark(MxVertexID i) { return v_data(i).user_mark; }
    void vertex_mark(MxVertexID i, unsigned char m) { v_data(i).user_mark=m; }

    uint face_check_tag(MxFaceID i, uint tag) const
	{ return f_data(i).user_tag&tag; }
    void face_set_tag(MxFaceID i, uint tag) { f_data(i).user_tag|=tag; }
    void face_unset_tag(MxFaceID i, uint tag) {f_data(i).user_tag&= ~tag;}
    unsigned char face_mark(MxFaceID i) { return f_data(i).user_mark; }
    void face_mark(MxFaceID i, unsigned char m) { f_data(i).user_mark = m; }


    ////////////////////////////////////////////////////////////////////////
    //  Vertex proxy management and proxy-aware accessors
    //
    MxVertexID add_proxy_vertex(MxVertexID);
    MxVertexID& vertex_proxy_parent(MxVertexID);
    MxVertexID resolve_proxies(MxVertexID v);
    float *vertex_position(MxVertexID v);


    ////////////////////////////////////////////////////////////////////////
    //  Neighborhood collection and management
    //
    void mark_neighborhood(MxVertexID, unsigned short mark=0);
    void collect_unmarked_neighbors(MxVertexID, MxFaceList& faces);
    void mark_neighborhood_delta(MxVertexID, short delta);
    void partition_marked_neighbors(MxVertexID, unsigned short pivot,
				    MxFaceList& below, MxFaceList& above);

    void mark_corners(const MxFaceList& faces, unsigned short mark=0);
    void collect_unmarked_corners(const MxFaceList& faces,MxVertexList& verts);

    void collect_edge_neighbors(MxVertexID, MxVertexID, MxFaceList&);
    void collect_vertex_star(MxVertexID v, MxVertexList& verts);

    MxFaceList& neighbors(MxVertexID v) { return *face_links(v); }
    const MxFaceList& neighbors(MxVertexID v) const { return *face_links(v); }

    void collect_neighborhood(MxVertexID v, int depth, MxFaceList& faces);

    void compute_vertex_normal(MxVertexID v, float *);
    void synthesize_normals(uint start=0);

    ////////////////////////////////////////////////////////////////////////
    // Primitive transformation operations
    //
    void remap_vertex(MxVertexID from, MxVertexID to);
    MxVertexID split_edge(MxVertexID v1,MxVertexID v2,float x,float y,float z);
    MxVertexID split_edge(MxVertexID v1, MxVertexID v2);

    void flip_edge(MxVertexID v1, MxVertexID v2);

    // split_face3
    void split_face4(MxFaceID f, MxVertexID *newverts=NULL);

    void unlink_face(MxFaceID f);

    ////////////////////////////////////////////////////////////////////////
    // Contraction and related operations
    //
    void compact_vertices();
    void remove_degeneracy(MxFaceList&);

    // Pair contraction interface
    void compute_contraction(MxVertexID, MxVertexID,
			     MxPairContraction *, const float *vnew=NULL);
    void apply_contraction(const MxPairContraction&);
    void apply_expansion(const MxPairExpansion&);
    void contract(MxVertexID v1, MxVertexID v2,
		  const float *, MxPairContraction *);

    // Triple contraction interface
    void compute_contraction(MxFaceID, MxFaceContraction *);
    void contract(MxVertexID v1, MxVertexID v2, MxVertexID v3,
		  const float *vnew, MxFaceList& changed);

    // Generalized contraction interface
    void contract(MxVertexID v1, const MxVertexList& rest,
		  const float *vnew, MxFaceList& changed);
};


extern void mx_render_model(MxStdModel&);
extern void mx_draw_mesh(MxStdModel&, double *color=NULL);
extern void mx_draw_wireframe(MxStdModel&, double *color=NULL);
extern void mx_draw_boundaries(MxStdModel&);
extern void mx_draw_pointcloud(MxStdModel&);

// MXSTDMODEL_INCLUDED
#endif
