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

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

  MxBlockModel

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

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

#include "MxDynBlock.h"
#include "MxGeoPrims.h"
#include "MxRaster.h"

#define MX_UNBOUND 0x0
#define MX_PERFACE 0x1
#define MX_PERVERTEX 0x2
#define MX_MAX_BINDING 0x2

#define MX_NORMAL_MASK   0x3
#define MX_COLOR_MASK    (0x3<<2)
#define MX_TEXTURE_MASK  (0x3<<4)
#define MX_ALL_MASK      (MX_NORMAL_MASK|MX_COLOR_MASK|MX_TEXTURE_MASK)

class MxBlockModel
{
private:
    unsigned char cbinding, nbinding, tbinding;

    unsigned int flags;

    // Required blocks
    MxDynBlock<MxVertex> vertices;
    MxDynBlock<MxFace> faces;

    // Optional blocks
    MxDynBlock<MxNormal> *normals;
    MxDynBlock<MxColor> *colors;
    MxDynBlock<MxTexCoord> *tcoords;
    // prop_block *properties;  // Indirect block for arbitrary properties

    // Optional texture map
    MxRaster *tex;
    char *tex_name;

protected:
    virtual MxVertexID alloc_vertex(float, float, float);
    virtual void init_vertex(MxVertexID) { }
    virtual void free_vertex(MxVertexID) { }
    virtual MxFaceID alloc_face(MxVertexID, MxVertexID, MxVertexID);
    virtual void init_face(MxFaceID) { }
    virtual void free_face(MxFaceID) { }

public:
    uint binding_mask;

public:
    MxBlockModel(int nvert, int nface) : vertices(nvert), faces(nface)
	{
	    colors = NULL;  normals = NULL;  tcoords = NULL;
	    cbinding = nbinding = tbinding = MX_UNBOUND;
	    binding_mask = MX_ALL_MASK;
	    tex = NULL;
	    tex_name = NULL;
	}
    virtual ~MxBlockModel()
	{
	    if( normals ) delete normals;
	    if( colors ) delete colors;
	    if( tcoords ) delete tcoords;
	    if( tex ) delete tex;
	    if( tex_name ) delete tex_name;
	}

    MxBlockModel *clone(MxBlockModel *into=NULL);

    unsigned int vert_count() const { return vertices.length(); }
    unsigned int face_count() const { return faces.length(); }
    unsigned int color_count() const { return (colors?colors->length():0); }
    unsigned int normal_count() const { return (normals?normals->length():0); }
    uint texcoord_count() const { return (tcoords?tcoords->length():0); }

    MxVertexID add_vertex(float, float, float);
    MxFaceID add_face(uint, uint, uint, bool will_link=true);
    unsigned int add_color(float, float, float);
    unsigned int add_normal(float, float, float);
    unsigned int add_texcoord(float, float);

    MxVertexID add_vertex(float *v) { return add_vertex(v[0], v[1], v[2]); }
    MxFaceID add_face(unsigned int *f) { return add_face(f[0], f[1], f[2]); }

    void remove_vertex(MxVertexID v);
    void remove_face(MxFaceID f);

    MxVertex& vertex(unsigned int i) { return vertices(i); }
    MxFace& face(unsigned int i) { return faces(i); }
    MxVertex& corner(MxFaceID f, short i) { return vertex(face(f)[i]); }
    MxColor& color(unsigned int i) { assert(colors);  return (*colors)(i); }
    MxNormal& normal(unsigned int i) { assert(normals); return (*normals)(i); }
    MxTexCoord& texcoord(uint i) { assert(tcoords); return (*tcoords)(i); }

    int color_binding() { return (cbinding&(binding_mask>>2)); }
    int normal_binding() { return (nbinding&binding_mask); }
    int texcoord_binding() { return (tbinding&(binding_mask>>4)); }
    void color_binding(unsigned char b);
    void normal_binding(unsigned char b);
    void texcoord_binding(unsigned char b);

    const char *binding_name(int);
    int parse_binding(const char *);

    MxRaster *texmap() const { return tex; }
    const char *texmap_name() const { return tex_name; }
    uint add_texmap(MxRaster *, const char *name);

    void compute_face_normal(MxFaceID, double *, bool will_unitize=true);
    void compute_face_normal(MxFaceID, float *, bool will_unitize=true);
    void compute_face_plane(MxFaceID, float *, bool will_unitize=true);
    double compute_face_area(MxFaceID);
    double compute_face_perimeter(MxFaceID, bool *edge_flags=NULL);

    double compute_corner_angle(MxFaceID, uint);
};


// MXBLOCKMODEL_INCLUDED
#endif
