/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * OpenSceneGraph Public License for more details.
*/

#ifndef OSGTEXT_TEXTBASE
#define OSGTEXT_TEXTBASE 1

#include <osg/Drawable>

#include <osgText/String>
#include <osgText/KerningType>
#include <osgText/Font>

namespace osgText {

#define NEW_APPROACH

class OSGTEXT_EXPORT TextBase : public osg::Drawable
{
public:

    TextBase();
    TextBase(const TextBase& text,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);

    //virtual osg::Object* cloneType() const { return new Text(); }
    //virtual osg::Object* clone(const osg::CopyOp& copyop) const { return new Text(*this,copyop); }
    virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast<const TextBase*>(obj)!=NULL; }
    virtual const char* className() const { return "TextBase"; }
    virtual const char* libraryName() const { return "osgText"; }

    void setColor(const osg::Vec4& color);
    const osg::Vec4& getColor() const { return _color; }

    /** Set the Font to use to render the text.
      * setFont(0) sets the use of the default font.*/
    virtual void setFont(Font* font=0) { setFont(osg::ref_ptr<Font>(font)); };

    /** Set the Font to use to render the text.*/
    virtual void setFont(osg::ref_ptr<Font> font);

    /** Set the font, loaded from the specified front file, to use to render the text,
      * setFont("") sets the use of the default font.
      * See the osgText::readFontFile function for how the font file will be located. */
    virtual void setFont(const std::string& fontfile);

    /** Get the font. Return 0 if default is being used.*/
    Font* getFont() { return _font.get(); }

    /** Get the const font. Return 0 if default is being used.*/
    const Font* getFont() const { return _font.get(); }


    /** Set the text style.*/
    void setStyle(Style* style) { _style = style; }
    /** Get the text style.*/
    Style* getStyle() { return _style.get(); }
    /** Get the const text style.*/
    const Style* getStyle() const { return _style.get(); }

    /** Get or create the text style.*/
    Style* getOrCreateStyle() { if (!_style) _style = new Style; return _style.get(); }

    /** Set the Font reference width and height resolution in texels.
      * Note, the size may not be supported by current font,
      * the closest supported font size will be selected.*/
    void setFontResolution(unsigned int width, unsigned int height);

    unsigned int getFontWidth() const { return _fontSize.first; }
    unsigned int getFontHeight() const { return _fontSize.second; }


    /** Set the text using a osgText::String.*/
    void setText(const String& text);

    /** Set the text using a std::string,
      * which is converted to an internal TextString.*/
    void setText(const std::string& text);

    /** Set the text using a Unicode encoded std::string, which is converted to an internal TextString.
      * The encoding parameter specificies which Unicode encodeding is used in the std::string. */
    void setText(const std::string& text,String::Encoding encoding);

    /** Set the text using a wchar_t string,
      * which is converted to an internal TextString.*/
    void setText(const wchar_t* text);

    /** Get the text string.
      * Note, if you modify the string you must call Text::update() for
      * the internal glyph reprentation to be updated.*/
    String& getText() { return _text; }

    /** Get the const text string.*/
    const String& getText() const { return _text; }

    /** update internal glyph respresentation used for rendering,
      * and bounding volume.*/
    void update() { computeGlyphRepresentation(); }


    /** Set the rendered character size in object coordinates.*/
    void setCharacterSize(float height);

    /** Set the rendered character size in object coordinates.*/
    void setCharacterSize(float height, float aspectRatio);

    float getCharacterHeight() const { return _characterHeight; }
    float getCharacterAspectRatio() const { return _style.valid()? _style->getWidthRatio() : 1.0f; }

    enum CharacterSizeMode
    {
        OBJECT_COORDS, /// default
        SCREEN_COORDS, /// internally scale the characters to be constant screen size.
        OBJECT_COORDS_WITH_MAXIMUM_SCREEN_SIZE_CAPPED_BY_FONT_HEIGHT /// text that behavaves like OBJECT_COORDS sized text when a long distance way, but has its screen sized capped automatically when the viewer gets near.
    };

    /** Set how the CharacterSize value relates to the final rendered character.*/
    void setCharacterSizeMode(CharacterSizeMode mode) { _characterSizeMode = mode; }

    /** Get the CharacterSizeMode.*/
    CharacterSizeMode getCharacterSizeMode() const { return _characterSizeMode; }


    /** Set the maximum width of the text box.
      * With horizontal layouts any characters which do not fit are wrapped around.
      * 0 or negative values indicate that no maximum width is set, lines can be as long as
      * they need be to fit thre required text*/
    void setMaximumWidth(float maximumWidth);

    /** Get the maximim width of the text box.*/
    float getMaximumWidth() const { return _maximumWidth; }

    /** Set the maximum height of the text box.
      * With horizontal layouts any characters which do not fit are wrapped around.
      * 0 or negative values indicate that no maximum height is set, lines can be as long as
      * they need be to fit the required text*/
    void setMaximumHeight(float maximumHeight);

    /** Get the maximum height of the text box.*/
    float getMaximumHeight() const { return _maximumHeight; }

    /** Set the line spacing of the text box, given as a percentage of
      * the character height. The default value is 0 for backward
      * compatibility. For longer paragraphs of text, a value of at
      * least 25% (i.e. set line spacing to 0.25) is recommended. */
    void setLineSpacing(float lineSpacing);

    /** Get the line spacing of the text box. */
    float getLineSpacing() const { return _lineSpacing; }



    /** Set the position of text.*/
    void setPosition(const osg::Vec3& pos);

    /** Get the position of text.*/
    const osg::Vec3& getPosition() const { return _position; }


    enum AlignmentType
    {
        LEFT_TOP,
        LEFT_CENTER,
        LEFT_BOTTOM,

        CENTER_TOP,
        CENTER_CENTER,
        CENTER_BOTTOM,

        RIGHT_TOP,
        RIGHT_CENTER,
        RIGHT_BOTTOM,

        LEFT_BASE_LINE,
        CENTER_BASE_LINE,
        RIGHT_BASE_LINE,

        LEFT_BOTTOM_BASE_LINE,
        CENTER_BOTTOM_BASE_LINE,
        RIGHT_BOTTOM_BASE_LINE,

        BASE_LINE = LEFT_BASE_LINE /// default.

    };

    void setAlignment(AlignmentType alignment);
    AlignmentType getAlignment() const { return _alignment; }


    enum AxisAlignment
    {
        XY_PLANE,
        REVERSED_XY_PLANE,
        XZ_PLANE,
        REVERSED_XZ_PLANE,
        YZ_PLANE,
        REVERSED_YZ_PLANE,
        SCREEN,
        USER_DEFINED_ROTATION
    };

    void setAxisAlignment(AxisAlignment axis);
    AxisAlignment getAxisAlignment() const { return _axisAlignment; }

    void setRotation(const osg::Quat& quat);
    const osg::Quat& getRotation() const { return _rotation; }

    void setAutoRotateToScreen(bool autoRotateToScreen);
    bool getAutoRotateToScreen() const { return _autoRotateToScreen; }

    enum Layout
    {
        LEFT_TO_RIGHT, /// default
        RIGHT_TO_LEFT,
        VERTICAL
    };

    void setLayout(Layout layout);

    Layout getLayout() const { return _layout; }


    enum DrawModeMask
    {
        TEXT              = 1, /// default
        BOUNDINGBOX       = 2,
        FILLEDBOUNDINGBOX = 4,
        ALIGNMENT         = 8
    };

    void setDrawMode(unsigned int mode);

    unsigned int getDrawMode() const { return _drawMode; }

    void setBoundingBoxMargin(float margin);

    float getBoundingBoxMargin() const { return _textBBMargin; }

    void setBoundingBoxColor(const osg::Vec4& color){ _textBBColor = color; }

    const osg::Vec4& getBoundingBoxColor() const { return _textBBColor; }


    void setKerningType(KerningType kerningType) { _kerningType = kerningType; }

    KerningType getKerningType() const { return _kerningType; }

    /** Get the number of wrapped lines - only valid after computeGlyphRepresentation() has been called, returns 0 otherwise */
    unsigned int getLineCount() const { return _lineCount; }

    /** Immediately compile this \c Drawable into an OpenGL Display List/VertexBufferObjects.
        * @note Operation is ignored if \c _useDisplayList is \c false or VertexBufferObjects are not used.
    */
    virtual void compileGLObjects(osg::RenderInfo& renderInfo) const;

    /** Resize any per context GLObject buffers to specified size. */
    virtual void resizeGLObjectBuffers(unsigned int maxSize);

    /** If State is non-zero, this function releases OpenGL objects for
      * the specified graphics context. Otherwise, releases OpenGL objexts
      * for all graphics contexts. */
    virtual void releaseGLObjects(osg::State* state=0) const;


    virtual osg::BoundingBox computeBoundingBox() const;

    typedef osg::ref_ptr<osg::Vec3Array> Coords;
    Coords& getCoords() { return _coords; }
    const Coords& getCoords() const { return _coords; }

    void getCoord(unsigned int i, osg::Vec2& c) const { c.set((*_coords)[i].x(), (*_coords)[i].y()); }
    void getCoord(unsigned int i, osg::Vec3& c) const { c = (*_coords)[i]; }

    /** Get the cached internal matrix used to provide positioning of text.  The cached matrix is originally computed by computeMatrix(..). */
    const osg::Matrix& getMatrix() const { return _matrix; }

    /** compute the matrix that positions the text in model space for the given viewpoint.*/
    bool computeMatrix(osg::Matrix& matrix, osg::State* state=0) const;

protected:

    virtual ~TextBase();

    virtual osg::StateSet* createStateSet();

    virtual void assignStateSet();

    void initArraysAndBuffers();

    osg::VertexArrayState* createVertexArrayStateImplementation(osg::RenderInfo& renderInfo) const;

    void positionCursor(const osg::Vec2 & endOfLine_coords, osg::Vec2 & cursor, unsigned int linelength);
    String::iterator computeLastCharacterOnLine(osg::Vec2& cursor, String::iterator first,String::iterator last);
    void computePositions();
    virtual void computePositionsImplementation();

    virtual void computeGlyphRepresentation() = 0;

    typedef osg::ref_ptr<osg::Vec2Array> TexCoords;
    typedef osg::ref_ptr<osg::Vec4Array> ColorCoords;
    typedef std::vector< osg::ref_ptr<osg::DrawElements> > Primitives;


    // members which have public access.
    osg::Vec4                               _color;
    osg::ref_ptr<Font>                      _font;
    osg::ref_ptr<Style>                     _style;
    FontResolution                          _fontSize;
    float                                   _characterHeight;
    CharacterSizeMode                       _characterSizeMode;
    float                                   _maximumWidth;
    float                                   _maximumHeight;
    float                                   _lineSpacing;

    String                                  _text;
    osg::Vec3                               _position;
    AlignmentType                           _alignment;
    AxisAlignment                           _axisAlignment;
    osg::Quat                               _rotation;
    bool                                    _autoRotateToScreen;
    Layout                                  _layout;
    unsigned int                            _drawMode;
    float                                   _textBBMargin;
    osg::Vec4                               _textBBColor;
    KerningType                             _kerningType;
    unsigned int                            _lineCount;
    bool                                    _glyphNormalized;

    osg::Vec3                               _offset;
    osg::Vec3                               _normal;
    osg::BoundingBox                        _textBB;
    osg::BoundingBox                        _textBBWithMargin;

    mutable osg::Matrix                     _matrix;

    Primitives                              _decorationPrimitives;

    void setupDecoration();

    osg::ref_ptr<osg::VertexBufferObject>   _vbo;
    osg::ref_ptr<osg::ElementBufferObject>  _ebo;

    Coords                                  _coords;
    Coords                                  _normals;
    ColorCoords                             _colorCoords;
    TexCoords                               _texcoords;

    unsigned int addCoord(const osg::Vec2& c) { unsigned int s = _coords->size(); _coords->push_back(osg::Vec3(c.x(), c.y(), 0.0f)); _coords->dirty(); return s; }
    unsigned int addCoord(const osg::Vec3& c) { unsigned int s = _coords->size(); _coords->push_back(c); _coords->dirty(); return s; }


    void addTexCoord(const osg::Vec2& tc) { _texcoords->push_back(tc); _texcoords->dirty(); }

};

}


#endif

