/*
 * NodeText.cpp
 *
 * Copyright (C) 1999 Stephen F. White
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file "COPYING" for details); if 
 * not, write to the Free Software Foundation, Inc., 675 Mass Ave, 
 * Cambridge, MA 02139, USA.
 */

#include <stdio.h>
#include "stdafx.h"

#include "NodeText.h"
#include "Proto.h"
#include "FieldValue.h"
#include "MFString.h"
#include "SFNode.h"
#include "MFFloat.h"
#include "SFFloat.h"
#include "SFString.h"
#include "SFBool.h"
#include "Scene.h"
#include "NodeFontStyle.h"
#include "FontInfo.h"
#include "Util.h"

ProtoText::ProtoText(Scene *scene, const char* name)
  : GeometryProto(scene, name)
{
    addElements();
}

ProtoText::ProtoText(Scene *scene)
  : GeometryProto(scene, "Text")
{
    addElements();
}

void 
ProtoText::addElements(void)
{
    fontStyle.set(
          addExposedField(SFNODE, "fontStyle", new SFNode(NULL), 
                          FONT_STYLE_NODE));
    length.set(
          addExposedField(MFFLOAT, "length", new MFFloat(), new MFFloat(0.0f)));
    maxExtent.set(
          addExposedField(SFFLOAT, "maxExtent", new SFFloat(0.0f), 
                          new SFFloat(0.0f)));
    string.set(
          addExposedField(MFSTRING, "string", new MFString()));
    if (TheApp->getKambiMode()) {
        solid.set(
              addExposedField(SFBOOL, "solid", new SFBool(false)));
        setFieldFlags(solid, FF_KAMBI_ONLY);
    } else {
        solid.set(
              addField(SFBOOL, "solid", new SFBool(false)));
        setFieldFlags(solid, FF_X3D_ONLY | FF_KAMBI_ONLY);
    }
    addEventOut(MFVEC2F, "lineBounds", FF_X3D_ONLY);
    addEventOut(SFVEC3F, "origin", FF_X3D_ONLY);
    addEventOut(MFVEC2F, "textBounds", FF_X3D_ONLY);
}

Node *
ProtoText::create(Scene *scene)
{ 
    return new NodeText(scene, this); 
}

NodeText::NodeText(Scene *scene, Proto *def)
  : GeometryNode(scene, def)
{
    _textDirty = true;
    _deBackslashedStrings = NULL;
}

// since version white_dune-0.29beta548 incorperate the needed font parts 
// from openglut
// #ifdef HAVE_LIBGLUT
// #include "GL/glut.h"

#ifndef _WIN32
extern "C" {
#include "openglutfont.h"
}

static char *
deBackslashString(const char *string)
{
    char *ret;

    if (string == NULL)
        return NULL;

    ret = (char *) malloc(strlen(string) + 2);
    ret[0] = 0;
  
    int retCounter = 0;
    for (int i = 0; i < strlen(string); i++) {
        if (string[i] == '\\')
            i++;
        ret[retCounter] = string[i];
        retCounter++;
    }
    ret[retCounter] = 0;
    return ret;
}

void
NodeText::cleanText(void)
{
    MFString *mfstring = string();
    if (mfstring == NULL)
        return;
    delete _deBackslashedStrings;
    _deBackslashedStrings = new MFString(); 
    for (int i = 0; i < mfstring->getSize(); i++) {
        const char *oldString = mfstring->getValue(i);
        char *newString = deBackslashString(oldString);
        MyString sfValue = "";
        sfValue += newString;
        _deBackslashedStrings->insertSFValue(i, sfValue);
        free(newString);
    }
    _textDirty = false;
}

// yet another ugly textnode implementation 8-(

void
NodeText::draw()
{
    if (_textDirty)
        cleanText();

    MFString *mfstring = _deBackslashedStrings;
    if (mfstring == NULL)
        return;
    NodeFontStyle *fontStyle = (NodeFontStyle *) 
                    ((SFNode *) getField(fontStyle_Field()))->getValue();

    float fsize = 1;
    if (fontStyle)
        fsize = fontStyle->size()->getValue();
    
    GLfloat diff_color[4];
    GLfloat em_color[4];

    glGetMaterialfv(GL_FRONT, GL_DIFFUSE, diff_color);
    glGetMaterialfv(GL_FRONT, GL_EMISSION, em_color);
    GLfloat color[4];
    for (int i = 0; i < 3; i++) {
        color[i] = em_color[i];
        if (_scene->getNumLights() != 0)
           color[i] += diff_color[i];
        if (color[i] > 1)
           color[i] = 1 ;
    }
    color[3] = diff_color[3];

    glPushAttrib(GL_ENABLE_BIT);

    glDisable(GL_LIGHTING);
    glDisable(GL_BLEND);
    
    Util::myGlColor4f(color[0], color[1], color[2], color[3]);
    glEnable(GL_LINE_SMOOTH);

    for (int j = 0; j < mfstring->getSize(); j++) {
        const char  *str = mfstring->getValue(j);
        int n = strlen(str);
        glPushMatrix();
        glTranslatef(0, -j * fsize, 0);
        const float GLUT_STROKE_ROMAN_SIZE = 119.05;
        float scale = 1/GLUT_STROKE_ROMAN_SIZE;
        glScalef(scale * fsize, scale * fsize, 1.0);
        for (int i = 0; i < n; i++)
            glutStrokeCharacter(GLUT_STROKE_ROMAN, str[i]);
        glPopMatrix();
    }

    glLineWidth(1.0);

    glPopAttrib();
}

#else

void
NodeText::draw()
{
    MFString *mfstring = string();
    NodeFontStyle *fontStyle = (NodeFontStyle *) 
                    ((SFNode *) getField(fontStyle_Field()))->getValue();
//    const float *length = ((MFFloat *) getField(length_Field()))->getValue();
//    float maxExtent = ((SFFloat *) getField(maxExtent_Field()))->getValue();
    FontInfo *font = NULL;
    bool fhorizontal = true;
    bool bleftToRight = true;
    float fsize = 1.0f;
    float fspacing = 1.0f;
    bool btopToBottom = true;

    if (fontStyle) {
        MFString *ffamily = fontStyle->family();
        fhorizontal = fontStyle->horizontal()->getValue();
//      MFString *justify = fontStyle->justify();
//      const char *language = fontStyle->language()->getValue();
        bleftToRight = fontStyle->leftToRight()->getValue();
        fsize = fontStyle->size()->getValue();
        fspacing = fontStyle->spacing()->getValue();
        const char *sstyle = fontStyle->style()->getValue();
        btopToBottom = fontStyle->topToBottom()->getValue();

        for (int i = 0; i < ffamily->getSize(); i++) {
            font = _scene->LoadGLFont(ffamily->getValue(i), sstyle);
            if (font) break;
        }

    } else {
        font = _scene->LoadGLFont("SERIF", "PLAIN");
    }

    if (!font) return; // couldn't find the font

    glPushMatrix();
    glPushAttrib(GL_TEXTURE_BIT);

    glScalef(fsize, fsize, 1.0f);

    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);

    glEnable(GL_TEXTURE_GEN_S);
    glEnable(GL_TEXTURE_GEN_T);

    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
    float sPlane[4] = {1.0, 0.0, 0.0, 0.0};
    float tPlane[4] = {0.0, 1.0, 0.0, 0.0};

    float ySpacing = btopToBottom ? - fspacing : fspacing;

    for (int j = 0; j < mfstring->getSize(); j++) {
        const char  *str = mfstring->getValue(j);
        int n = strlen(str);
        if (fhorizontal) {
            sPlane[3] = 0.0f;
            glPushMatrix();
            for (int i = 0; i < n; i++) {
               if (bleftToRight) {
                    glTexGenfv(GL_S, GL_OBJECT_PLANE, sPlane);
                    glTexGenfv(GL_T, GL_OBJECT_PLANE, tPlane);
                    glCallList(font->displayListBase + str[i]);
                    sPlane[3] += font->kernX[str[i]];
                } else {
                    glTranslatef(-font->kernX[str[i]], 0.0f, 0.0f);
                    sPlane[3] -= font->kernX[str[i]];
                    glTexGenfv(GL_S, GL_OBJECT_PLANE, sPlane);
                    glTexGenfv(GL_T, GL_OBJECT_PLANE, tPlane);
                    glPushMatrix();
                    glCallList(font->displayListBase + str[i]);
                    glPopMatrix();
                }
            }
            glPopMatrix();
            glTranslatef(0.0f, ySpacing, 0.0f);
            tPlane[3] += ySpacing;
        } else {
            tPlane[3] = 0.0f;
            if (!bleftToRight) {
                glTranslatef(-fspacing * 0.5f, 0.0f, 0.0f);
                sPlane[3] += -fspacing * 0.5f;
            }
            glPushMatrix();
            for (int i = 0; i < n; i++) {
                if (btopToBottom) {
                    float y = -font->kernY[str[i]];
                    if (y == 0.0) y = -1.0f;  // FIXME:  default advance?
                    glTranslatef(0.0f, y, 0.0f);
                    tPlane[3] += y;
                    glPushMatrix();
                    glTexGenfv(GL_S, GL_OBJECT_PLANE, sPlane);
                    glTexGenfv(GL_T, GL_OBJECT_PLANE, tPlane);
                    glCallList(font->displayListBase + str[i]);
                    glPopMatrix();
                } else {
                    glPushMatrix();
                    glTexGenfv(GL_S, GL_OBJECT_PLANE, sPlane);
                    glTexGenfv(GL_T, GL_OBJECT_PLANE, tPlane);
                    glCallList(font->displayListBase + str[i]);
                    glPopMatrix();
                    float y = font->kernY[str[i]];
                    if (y == 0.0) y = 1.0f;  // FIXME:  default advance?
                    glTranslatef(0.0f, y, 0.0f);
                    tPlane[3] += y;
                }
            }
            glPopMatrix();
            if (bleftToRight) {
                glTranslatef(fspacing * 0.5f, 0.0f, 0.0f);
                sPlane[3] += fspacing * 0.5f;
            }
        }
    }

    glDisable(GL_TEXTURE_GEN_S);
    glDisable(GL_TEXTURE_GEN_T);

    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);

    glPopAttrib();
    glPopMatrix();
}

#endif
