/* dxfReader for OpenSceneGraph  Copyright (C) 2005 by GraphArchitecture ( grapharchitecture.com )
 * Programmed by Paul de Repentigny <pdr@grapharchitecture.com>
 *
 * OpenSceneGraph is (C) 2004 Robert Osfield
 *
 * This library is provided as-is, without support of any kind.
 *
 * Read DXF docs or OSG docs for any related questions.
 *
 * You may contact the author if you have suggestions/corrections/enhancements.
 */


#ifndef DXF_ENTITY
#define DXF_ENTITY 1

#include <vector>
#include <string>
#include <iostream>

#include <osg/Referenced>
#include <osg/ref_ptr>
#include <osg/Array>
#include <osg/Vec3d>
#include <osg/Node>
#include <osg/Matrixd>
#include <osgText/Text>

#include "dxfBlock.h"


class scene;
class codeValue;
class dxfFile;

static inline void
getOCSMatrix(const osg::Vec3d& ocs, osg::Matrixd& m)
{
    static const double one_64th = 1.0/64.0;
    m.makeIdentity();
    if (ocs == osg::Vec3d(0,0,1)) return;
    osg::Vec3d ax(1,0,0), ay(0,1,0), az(0,0,1);
    osg::Vec3d ocsaxis(ocs);
    ocsaxis.normalize();
    if (fabs(ocsaxis.x()) < one_64th && fabs(ocsaxis.y()) < one_64th) {
        ax = ay ^ ocsaxis;
    } else {
        ax = az ^ ocsaxis;
    }
    ax.normalize();
    ay = ocsaxis ^ ax;
    ay.normalize();
    m = osg::Matrixd(    ax.x(), ax.y(), ax.z(), 0,
                        ay.x(), ay.y(), ay.z(), 0,
                        ocsaxis.x(), ocsaxis.y(), ocsaxis.z(), 0,
                        0,0,0,1);
//    m = m.inverse(m);
}

class dxfBasicEntity : public osg::Referenced
{
public:
    dxfBasicEntity() : _color(0), _useAccuracy(false), _maxError(0.01), _improveAccuracyOnly(false), _lineWidth(-1), _lineThickness(-1) {}
    virtual ~dxfBasicEntity() {}
    virtual dxfBasicEntity* create() = 0;
    virtual const char* name() = 0;
    virtual void assign(dxfFile* dxf, codeValue& cv);
    virtual void drawScene(scene*) {}
    const std::string getLayer() const { return _layer; }

    void setAccuracy(bool useAccuracy,double maxError,bool improveAccuracyOnly) {
        _useAccuracy=useAccuracy;
        _maxError=maxError;
        _improveAccuracyOnly=improveAccuracyOnly;
    }


protected:
    std::string    _layer;
    unsigned short    _color;

    bool _useAccuracy;          // true to specify a maximum deviation for curve rendering
    double _maxError;         // the error in model units, if _useAccuracy==true
    bool _improveAccuracyOnly;// if true only use _maxError where it would increase the quality of curves compared to the previous algorithm

    double  _lineWidth;
    double  _lineThickness;
};


class dxfCircle : public dxfBasicEntity
{
public:
    dxfCircle() : _radius(0), _ocs(0,0,1) {}
    virtual ~dxfCircle() {}
    virtual dxfBasicEntity* create() { // we create a copy which uses our accuracy settings
        dxfBasicEntity* circle=new dxfCircle;
        circle->setAccuracy(_useAccuracy,_maxError,_improveAccuracyOnly);
        return circle;
    }
    virtual const char* name() { return "CIRCLE"; }
    virtual void assign(dxfFile* dxf, codeValue& cv);
    virtual void drawScene(scene* sc);
protected:
    osg::Vec3d    _center;
    double    _radius;
    osg::Vec3d    _ocs;
};

class dxfArc : public dxfBasicEntity
{
public:
    dxfArc() : _radius(0), _startAngle(0), _endAngle(360), _ocs(0,0,1) {}
    virtual ~dxfArc() {}
    virtual dxfBasicEntity* create() { // we create a copy which uses our accuracy settings
        dxfBasicEntity* arc=new dxfArc;
        arc->setAccuracy(_useAccuracy,_maxError,_improveAccuracyOnly);
        //std::cout<<"dxfArc::create with _useAccuracy="<<_useAccuracy<<" maxError="<<_maxError<<" improveAccuracyOnly="<<_improveAccuracyOnly<<std::endl;
        return arc;
    }
    virtual const char* name() { return "ARC"; }
    virtual void assign(dxfFile* dxf, codeValue& cv);
    virtual void drawScene(scene* sc);
protected:
    osg::Vec3d    _center;
    double    _radius;
    double    _startAngle;
    double    _endAngle;
    osg::Vec3d    _ocs;
};

class dxfPoint : public dxfBasicEntity
{
public:
    dxfPoint() : _ocs(0,0,1) {}
    virtual ~dxfPoint() {}
    virtual dxfBasicEntity* create() { return new dxfPoint; }
    virtual const char* name() { return "POINT"; }
    virtual void assign(dxfFile* dxf, codeValue& cv);
    virtual void drawScene(scene* sc);
protected:
    osg::Vec3d    _a;
    //osg::Vec3d    _b;
    osg::Vec3d    _ocs;
};

class dxfLine : public dxfBasicEntity
{
public:
    dxfLine() : _ocs(0,0,1) {}
    virtual ~dxfLine() {}
    virtual dxfBasicEntity* create() { return new dxfLine; }
    virtual const char* name() { return "LINE"; }
    virtual void assign(dxfFile* dxf, codeValue& cv);
    virtual void drawScene(scene* sc);
protected:
    osg::Vec3d    _a;
    osg::Vec3d    _b;
    osg::Vec3d    _ocs;
};

class dxf3DFace : public dxfBasicEntity
{
public:
    dxf3DFace()
    {
        _vertices[0] = osg::Vec3d(0,0,0);
        _vertices[1] = osg::Vec3d(0,0,0);
        _vertices[2] = osg::Vec3d(0,0,0);
        _vertices[3] = osg::Vec3d(0,0,0);
    }
    virtual ~dxf3DFace() {}
    virtual dxfBasicEntity* create() { return new dxf3DFace; }
    virtual const char* name() { return "3DFACE"; }
    virtual void assign(dxfFile* dxf, codeValue& cv);
    virtual void drawScene(scene* sc);
protected:
    osg::Vec3d _vertices[4];
};

class dxfVertex : public dxfBasicEntity
{
public:
    dxfVertex() : _vertex(osg::Vec3d(0,0,0)), _indice1(0), _indice2(0), _indice3(0), _indice4(0) {}
    virtual ~dxfVertex() {}
    virtual dxfBasicEntity* create() { return new dxfVertex; }
    virtual const char* name() { return "VERTEX"; }
    virtual void assign(dxfFile* dxf, codeValue& cv);
    void getVertex(double &x, double &y, double &z) { x=_vertex.x();y=_vertex.y();z=_vertex.z(); }
    const osg::Vec3d& getVertex() const { return _vertex; }
    unsigned int getIndice1() const { return _indice1; }
    unsigned int getIndice2() const { return _indice2; }
    unsigned int getIndice3() const { return _indice3; }
    unsigned int getIndice4() const { return _indice4; }

protected:
    osg::Vec3d    _vertex;
    unsigned int _indice1, _indice2, _indice3, _indice4;
};

class dxfPolyline : public dxfBasicEntity
{
public:
    dxfPolyline() : _currentVertex(NULL),
                    _elevation(0.0),
                    _flag(0),
                    _mcount(0),
                    _ncount(0),
                    _nstart(0),
                    _nend(0),
                    _ocs(osg::Vec3d(0,0,1)),
                    _mdensity(0),
                    _ndensity(0),
                    _surfacetype(0)
                    {}
    virtual ~dxfPolyline() {}
    virtual dxfBasicEntity*        create() { return new dxfPolyline; }
    virtual const char*            name() { return "POLYLINE"; }
    virtual void                assign(dxfFile* dxf, codeValue& cv);
    virtual int                    vertexCount() { return _vertices.size(); }
    virtual void                drawScene(scene* sc);

protected:
    dxfVertex*                    _currentVertex;
    std::vector<osg::ref_ptr<dxfVertex> >        _vertices;
    std::vector<osg::ref_ptr<dxfVertex> >        _indices;
    double                        _elevation;
    unsigned short                _flag;
    unsigned int                  _mcount;
    unsigned int                  _ncount;
    unsigned short                _nstart; // 71
    unsigned short                _nend; //72
    osg::Vec3d                    _ocs; //210 220 230
    unsigned short                _mdensity; // 73
    unsigned short                _ndensity; // 74
    unsigned short                _surfacetype; //75

};

class dxfLWPolyline : public dxfBasicEntity
{
public:
    dxfLWPolyline() :
        _elevation(0.0),
        _flag(0),
        _vcount(0),
        _ocs(osg::Vec3d(0,0,1)),
        _lastv(0,0,0)
        {}
    virtual ~dxfLWPolyline() {}
    virtual dxfBasicEntity*        create() { return new dxfLWPolyline; }
    virtual const char*            name() { return "LWPOLYLINE"; }
    virtual void                assign(dxfFile* dxf, codeValue& cv);
    virtual int                    vertexCount() { return _vertices.size(); }
    virtual void                drawScene(scene* sc);

protected:
    double                        _elevation;
    unsigned short                _flag;
    unsigned short                _vcount; // 90
    osg::Vec3d                    _ocs; //210 220 230
    osg::Vec3d                    _lastv;
    std::vector< osg::Vec3d >     _vertices;

};

class dxfInsert : public dxfBasicEntity
{
public:
    dxfInsert() : _block(NULL),
                    _done(false),
                    _rotation(0),
                    _scale(1,1,1),
                    _point(osg::Vec3d(0,0,0)),
                    _ocs(osg::Vec3d(0,0,1)) {}
    virtual ~dxfInsert() {}
    virtual dxfBasicEntity* create() { return new dxfInsert; }
    virtual const char* name() { return "INSERT"; }
    virtual void assign(dxfFile* dxf, codeValue& cv);
    virtual void drawScene(scene* sc);

protected:
    std::string _blockName;
    osg::ref_ptr<dxfBlock> _block;
    bool _done; // since we are on a SEQEND, we must
                // make sure not getting values addressed to other
                // entities (dxf garble things) in the sequence
    double            _rotation;
    osg::Vec3d        _scale;
    osg::Vec3d        _point;
    osg::Vec3d        _ocs;
};

class dxfText : public dxfBasicEntity
{
public:
    dxfText() :
        encoding(osgText::String::ENCODING_ASCII),
        font("arial.ttf"),
        _string(""),
        _point1(0,0,0),
        _point2(0,0,0),
        _ocs(0,0,1),
        _height(1),
        _xscale(1),
        _rotation(0),
        _flags(0),
        _hjustify(0),
        _vjustify(0) {}

    virtual ~dxfText() {}
    virtual dxfBasicEntity* create()
    {
        dxfText* text = new dxfText;
        text->encoding = encoding;
        text->font = font;
        return text;
    }

    virtual const char*            name() { return "TEXT"; }
    virtual void                   assign(dxfFile* dxf, codeValue& cv);
    virtual void                   drawScene(scene* sc);

    // application settings
    osgText::String::Encoding encoding;
    std::string font;

protected:
    std::string       _string;    // 1
    osg::Vec3d        _point1;    // 10,20,30
    osg::Vec3d        _point2;    // 11,21,31
    osg::Vec3d        _ocs;       // 210,220,230
    double            _height;    // 40
    double            _xscale;    // 41
    double            _rotation;  // 50
    int               _flags;     // 71
    int               _hjustify;  // 72
    int               _vjustify;  // 73
};

class dxfEntity : public osg::Referenced
{
public:
    dxfEntity(std::string s) : _entity(NULL), _seqend(false)
    {
        _entity = findByName(s);
        if (_entity) {
            _entityList.push_back(_entity);
        //    std::cout << "entity " << s << std::endl;
        }
    }
    virtual void assign(dxfFile* dxf, codeValue& cv);
    virtual bool done() { return !_seqend; }
    static void registerEntity(dxfBasicEntity*);
    static void unregisterEntity(dxfBasicEntity*);
    static dxfBasicEntity* findByName(std::string s)
    {
        dxfBasicEntity* be = _registry[s].get();
        if (be)
            return be->create();
        else {
            std::cout << " no " << s << std::endl;
            return NULL;
        }
    }
    virtual void drawScene(scene* sc);
    dxfBasicEntity* getEntity() { return _entity; }

    // Returns the exemplar from the registry - all other entities of this type are created by this one via entity->create
    static dxfBasicEntity* getRegistryEntity(std::string s) {
            return _registry[s].get();
    }

protected:
    std::vector<osg::ref_ptr<dxfBasicEntity> > _entityList;
    static std::map<std::string, osg::ref_ptr<dxfBasicEntity> > _registry;
    dxfBasicEntity* _entity;
    bool    _seqend; // bypass 0 codes. needs a 0 seqend to close.



};

/** Proxy class for automatic registration of dxf entities reader/writers.*/
template<class T>
class RegisterEntityProxy
{
    public:
        RegisterEntityProxy()
        {
            _rw = new T;
            dxfEntity::registerEntity(_rw.get());
        }

        ~RegisterEntityProxy()
        {
            dxfEntity::unregisterEntity(_rw.get());
        }

        T* get() { return _rw.get(); }

    protected:
        osg::ref_ptr<T> _rw;
};

#endif
