/*
 *  Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
 *  Copyright (C) 2010 - DIGITEO - Pierre Lando
 *  Copyright (C) 2011-2012 - DIGITEO - Manuel Juliachs
 *
 *  This file must be used under the terms of the CeCILL.
 *  This source file is licensed as described in the file COPYING, which
 *  you should have received as part of this distribution.  The terms
 *  are also available at
 *  http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
 *
 */

#include <iostream>

#include "DecompositionUtils.hxx"
#include "PolylineDecomposer.hxx"
#include "Triangulator.hxx"
#include "ColorComputer.hxx"

extern "C"
{
#include <math.h>
#include <string.h>

#include "getGraphicObjectProperty.h"
#include "graphicObjectProperties.h"
}

int PolylineDecomposer::getDataSize(char* id)
{
    int nPoints = 0;
    int *piNPoints = &nPoints;
    int polylineStyle = 0;
    int* piPolylineStyle = &polylineStyle;
    int closed = 0;
    int* piClosed = &closed;

    getGraphicObjectProperty(id, __GO_POLYLINE_STYLE__, jni_int, (void**) &piPolylineStyle);
    getGraphicObjectProperty(id, __GO_DATA_MODEL_NUM_ELEMENTS__, jni_int, (void**) &piNPoints);
    getGraphicObjectProperty(id, __GO_CLOSED__, jni_bool, (void**) &piClosed);

    /* If 0 points, 0 elements */
    if (nPoints == 0)
    {
        return 0;
    }

    /* Segments */
    if (polylineStyle == 1)
    {
        return nPoints;
    }
    /* Staircase */
    else if (polylineStyle == 2)
    {
        if (closed)
        {
            return 2*nPoints;
        }
        else
        {
            return (2*nPoints)-1;
        }
    }
    /* Vertical segments plus segments */
    else if (polylineStyle == 3)
    {
        return 2*nPoints;
    }
    /* Segments with arrow heads */
    else if (polylineStyle == 4)
    {
        int nArrowVertices;
        /* The numbers of arrow head vertices and indices are exactly the same */
        nArrowVertices = PolylineDecomposer::getArrowTriangleIndicesSize(nPoints, closed);

        return nPoints + nArrowVertices;
    }
    /* Filled patch  */
    else if (polylineStyle == 5)
    {
        return nPoints;
    }
    /* Vertical bars plus segments */
    else if (polylineStyle == 6)
    {
        return 5*nPoints;
    }
    /* Horizontal bars plus segments */
    else if (polylineStyle == 7)
    {
        return 5*nPoints;
    }
    /* To be done: remaining styles */
    else
    {
        return 0;
    }

}

void PolylineDecomposer::fillVertices(char* id, float* buffer, int bufferLength, int elementsSize, int coordinateMask, double* scale, double* translation, int logMask)
{
    double* t = NULL;
    double* xshift = NULL;
    double* yshift = NULL;
    double* zshift = NULL;

    int polylineStyle = 0;
    int* piPolylineStyle = &polylineStyle;

    int nPoints = 0;
    int *piNPoints = &nPoints;

    getGraphicObjectProperty(id, __GO_DATA_MODEL_COORDINATES__, jni_double_vector, (void**) &t);
    getGraphicObjectProperty(id, __GO_DATA_MODEL_NUM_ELEMENTS__, jni_int, (void**) &piNPoints);

    getGraphicObjectProperty(id, __GO_DATA_MODEL_X_COORDINATES_SHIFT__, jni_double_vector, (void**) &xshift);
    getGraphicObjectProperty(id, __GO_DATA_MODEL_Y_COORDINATES_SHIFT__, jni_double_vector, (void**) &yshift);
    getGraphicObjectProperty(id, __GO_DATA_MODEL_Z_COORDINATES_SHIFT__, jni_double_vector, (void**) &zshift);

    getGraphicObjectProperty(id, __GO_POLYLINE_STYLE__, jni_int, (void**) &piPolylineStyle);

    if (polylineStyle == 1)
    {
        fillSegmentsDecompositionVertices(id, buffer, bufferLength, elementsSize, coordinateMask, scale, translation, logMask, t, nPoints, xshift, yshift, zshift);
    }
    else if (polylineStyle == 2)
    {
        fillStairDecompositionVertices(id, buffer, bufferLength, elementsSize, coordinateMask, scale, translation, logMask, t, nPoints, xshift, yshift, zshift);
    }
    else if (polylineStyle == 3)
    {
        fillVerticalLinesDecompositionVertices(id, buffer, bufferLength, elementsSize, coordinateMask, scale, translation, logMask, t, nPoints, xshift, yshift, zshift);
    }
    else if (polylineStyle == 4)
    {
        fillSegmentsDecompositionVertices(id, buffer, bufferLength, elementsSize, coordinateMask, scale, translation, logMask, t, nPoints, xshift, yshift, zshift);
    }
    else if (polylineStyle == 5)
    {
        fillSegmentsDecompositionVertices(id, buffer, bufferLength, elementsSize, coordinateMask, scale, translation, logMask, t, nPoints, xshift, yshift, zshift);
    }
    else if (polylineStyle == 6)
    {
        fillVerticalBarsDecompositionVertices(id, buffer, bufferLength, elementsSize, coordinateMask, scale, translation, logMask, t, nPoints, xshift, yshift, zshift);
    }
    else if (polylineStyle == 7)
    {
        fillHorizontalBarsDecompositionVertices(id, buffer, bufferLength, elementsSize, coordinateMask, scale, translation, logMask, t, nPoints, xshift, yshift, zshift);
    }

}

void PolylineDecomposer::fillSegmentsDecompositionVertices(char* id, float* buffer, int bufferLength, int elementsSize, int coordinateMask, double* scale, double* translation,
                                                           int logMask, double* coordinates, int nPoints, double* xshift, double* yshift, double* zshift)
{

    int componentIndices[3];

    // TODO Optimize ? (test if s = 1 and t = 0, coordinateMask = 0 ...)
    for (int i = 0; i < nPoints; i++)
    {
        /* Offset of a polyline vertex */
        int v0 = elementsSize*i;

        componentIndices[0] = i;
        componentIndices[1] = i;
        componentIndices[2] = i;

        getAndWriteVertexToBuffer(buffer, v0, coordinates, componentIndices, nPoints, elementsSize,
                                  xshift, yshift, zshift, coordinateMask, scale, translation, logMask);
    }

}

void PolylineDecomposer::getAndWriteVertexToBuffer(float* buffer, int offset, double* coordinates, int* vertexIndices, int nPoints, int elementsSize,
                                                   double* xshift, double* yshift, double* zshift, int coordinateMask, double* scale, double* translation, int logMask)
{
    double coordinate = 0.;

    if (coordinateMask & 0x01)
    {
        coordinate = coordinates[vertexIndices[0]];

        if (xshift != NULL)
        {
            coordinate += xshift[vertexIndices[0]];
        }

        if (logMask & 0x01)
        {
            coordinate = DecompositionUtils::getLog10Value(coordinate);
        }

        buffer[offset +0] = (float)(coordinate * scale[0] + translation[0]);
    }

    if (coordinateMask & 0x02)
    {
        coordinate = coordinates[vertexIndices[1] + nPoints];

        if (yshift != NULL)
        {
            coordinate += yshift[vertexIndices[1]];
        }

        if (logMask & 0x02)
        {
            coordinate = DecompositionUtils::getLog10Value(coordinate);
        }

        buffer[offset +1] = (float)(coordinate * scale[1] + translation[1]);
    }

    if (coordinateMask & 0x04)
    {
        coordinate = coordinates[vertexIndices[2] + 2*nPoints];

        if (zshift != NULL)
        {
            coordinate += zshift[vertexIndices[2]];
        }

        if (logMask & 0x04)
        {
            coordinate = DecompositionUtils::getLog10Value(coordinate);
        }

        buffer[offset +2] = (float)(coordinate * scale[2] + translation[2]);
    }

    if((elementsSize == 4) && (coordinateMask & 0x08))
    {
        buffer[offset +3] = 1.0;
    }

}

void PolylineDecomposer::fillStairDecompositionVertices(char* id, float* buffer, int bufferLength, int elementsSize, int coordinateMask, double* scale, double* translation,
                                                        int logMask, double* coordinates, int nPoints, double* xshift, double* yshift, double* zshift)
{
    int closed = 0;
    int* piClosed = &closed;

    /* Offsets of the left and right vertices (respectively) */
    int v0 = 0;
    int v1 = 0;

    int componentIndices[3];

    if (nPoints == 0)
    {
        return;
    }

    getGraphicObjectProperty(id, __GO_CLOSED__, jni_bool, (void**) &piClosed);

    for (int i = 0; i < nPoints-1; i++)
    {
        v0 = elementsSize*2*i;
        v1 = elementsSize*(2*i+1);

        componentIndices[0] = i;
        componentIndices[1] = i;
        componentIndices[2] = i;

        getAndWriteVertexToBuffer(buffer, v0, coordinates, componentIndices, nPoints, elementsSize, xshift, yshift, zshift, coordinateMask, scale, translation, logMask);

        componentIndices[0] = i+1;
        componentIndices[1] = i;
        componentIndices[2] = i;

        /* To be optimized: the y and z components are fetched and transformed twice */
        getAndWriteVertexToBuffer(buffer, v1, coordinates, componentIndices, nPoints, elementsSize, xshift, yshift, zshift, coordinateMask, scale, translation, logMask);
    }

    /* Last point */
    v0 = elementsSize*2*(nPoints-1);

    componentIndices[0] = nPoints-1;
    componentIndices[1] = nPoints-1;
    componentIndices[2] = nPoints-1;

    getAndWriteVertexToBuffer(buffer, v0, coordinates, componentIndices, nPoints, elementsSize, xshift, yshift, zshift, coordinateMask, scale, translation, logMask);

    /*
     * One additional vertex if closed
     * Its x-coordinate is equal to the one of the polyline's first point
     * whereas its y and z coordinates are equal to those of the last point.
     */
    if (closed)
    {
        v0 = elementsSize*(2*nPoints-1);

        componentIndices[0] = 0;
        componentIndices[1] = nPoints-1;
        componentIndices[2] = nPoints-1;

        getAndWriteVertexToBuffer(buffer, v0, coordinates, componentIndices, nPoints, elementsSize, xshift, yshift, zshift, coordinateMask, scale, translation, logMask);
    }

}

void PolylineDecomposer::fillVerticalLinesDecompositionVertices(char* id, float* buffer, int bufferLength, int elementsSize, int coordinateMask, double* scale, double* translation,
                                                                int logMask, double* coordinates, int nPoints, double* xshift, double* yshift, double* zshift)
{
    for (int i = 0; i < nPoints; i++)
    {
        /* Offsets of the lower and upper vertices (respectively) */
        int v0 = 0;
        int v1 = 0;

        /* Coordinates of the lower and upper vertices (respectively) */
        double coord0 = 0.;
        double coord1 = 0.;

        v0 = elementsSize*2*i;
        v1 = elementsSize*(2*i+1);

        /* Lower and upper endpoints x coordinates */
        if (coordinateMask & 0x01)
        {
            coord0 = coordinates[i];
            coord1 = coordinates[i];

            if (xshift != NULL)
            {
                coord0 += xshift[i];
                coord1 += xshift[i];
            }

            if (logMask & 0x01)
            {
                coord0 = DecompositionUtils::getLog10Value(coord0);
                coord1 = DecompositionUtils::getLog10Value(coord1);
            }

            buffer[v0 +0] = (float)(coord0 * scale[0] + translation[0]);
            buffer[v1 +0] = (float)(coord1 * scale[0] + translation[0]);
        }

        /* Lower and upper endpoints y coordinates */
        if (coordinateMask & 0x02)
        {
            coord0 = 0.0;
            coord1 = coordinates[i + nPoints];

            if (yshift != NULL)
            {
                /*
                 * Only the upper vertex's y coordinate is shifted,
                 * the lower vertex's one remains unchanged.
                 */
                coord1 += yshift[i];
            }

            if (logMask & 0x02)
            {
                /* The lower endpoint's y coordinate is unchanged (it amounts to log10(1), which is 0) */
                coord1 = DecompositionUtils::getLog10Value(coord1);
            }

            buffer[v0 +1] = (float)(coord0 * scale[1] + translation[1]);
            buffer[v1 +1] = (float)(coord1 * scale[1] + translation[1]);
        }

        /* Lower and upper endpoints z coordinates */
        if (coordinateMask & 0x04)
        {
            coord0 = coordinates[2*nPoints+i];
            coord1 = coordinates[2*nPoints+i];

            if (zshift != NULL)
            {
                coord0 += zshift[i];
                coord1 += zshift[i];
            }

            if (logMask & 0x04)
            {
                coord0 = DecompositionUtils::getLog10Value(coord0);
                coord1 = DecompositionUtils::getLog10Value(coord1);
            }

            buffer[v0 +2] = (float)(coord0 * scale[2] + translation[2]);
            buffer[v1 +2] = (float)(coord1 * scale[2] + translation[2]);
        }

        if((elementsSize == 4) && (coordinateMask & 0x08))
        {
            buffer[v0 +3] = 1.0;
            buffer[v1 +3] = 1.0;
        }

    }

}

void PolylineDecomposer::writeBarVerticesToBuffer(float* buffer, int* offsets, int componentOffset, double* coordinates, double shift, int shiftUsed,
                                                  double scale, double translation, int logUsed)
{
    if (shiftUsed)
    {
        coordinates[0] += shift;
        coordinates[1] += shift;
        coordinates[2] += shift;
        coordinates[3] += shift;

        coordinates[4] += shift;
    }

    if (logUsed)
    {
        coordinates[0] = DecompositionUtils::getLog10Value(coordinates[0]);
        coordinates[1] = DecompositionUtils::getLog10Value(coordinates[1]);
        coordinates[2] = DecompositionUtils::getLog10Value(coordinates[2]);
        coordinates[3] = DecompositionUtils::getLog10Value(coordinates[3]);

        coordinates[4] = DecompositionUtils::getLog10Value(coordinates[4]);
    }

    buffer[offsets[0] +componentOffset] = (float)(coordinates[0] * scale + translation);
    buffer[offsets[1] +componentOffset] = (float)(coordinates[1] * scale + translation);
    buffer[offsets[2] +componentOffset] = (float)(coordinates[2] * scale + translation);
    buffer[offsets[3] +componentOffset] = (float)(coordinates[3] * scale + translation);

    buffer[offsets[4] +componentOffset] = (float)(coordinates[4] * scale + translation);
}

void PolylineDecomposer::fillVerticalBarsDecompositionVertices(char* id, float* buffer, int bufferLength, int elementsSize, int coordinateMask, double* scale, double* translation,
                                                               int logMask, double* coordinates, int nPoints, double* xshift, double* yshift, double* zshift)
{
    double barWidth = 0.0;
    double* pdBarWidth = &barWidth;

    int shiftUsed[3];
    int *piShiftUsed = NULL;

    /*
     * Offsets of the lower-left, lower-right, upper-right and upper-left bar vertices (respectively)
     * and of the polyline vertex proper
     */
    int offsets[5];

    /*
     * {x,y or z}-component values of a bar's 4 vertices (same ordering as the offsets)
     * and of the polyline vertex proper.
     */
    double coords[5];

    double shift = 0.;


    getGraphicObjectProperty(id, __GO_BAR_WIDTH__, jni_double, (void**) &pdBarWidth);

    piShiftUsed = &shiftUsed[0];
    getGraphicObjectProperty(id, __GO_DATA_MODEL_X_COORDINATES_SHIFT_SET__, jni_double_vector, (void**) &piShiftUsed);
    piShiftUsed = &shiftUsed[1];
    getGraphicObjectProperty(id, __GO_DATA_MODEL_Y_COORDINATES_SHIFT_SET__, jni_double_vector, (void**) &piShiftUsed);
    piShiftUsed = &shiftUsed[2];
    getGraphicObjectProperty(id, __GO_DATA_MODEL_Z_COORDINATES_SHIFT_SET__, jni_double_vector, (void**) &piShiftUsed);


    for (int i = 0; i < nPoints; i++)
    {
        offsets[0] = elementsSize*4*i;
        offsets[1] = elementsSize*(4*i+1);
        offsets[2] = elementsSize*(4*i+2);
        offsets[3] = elementsSize*(4*i+3);

        offsets[4] = elementsSize*(4*nPoints+i);

        if (coordinateMask & 0x01)
        {
            coords[0] = coordinates[i] - 0.5*barWidth;
            coords[1] = coordinates[i] + 0.5*barWidth;
            coords[2] = coordinates[i] + 0.5*barWidth;
            coords[3] = coordinates[i] - 0.5*barWidth;

            coords[4] = coordinates[i];

            if (shiftUsed[0])
            {
                shift = xshift[i];
            }

            writeBarVerticesToBuffer(buffer, offsets, 0, coords, shift, shiftUsed[0], scale[0], translation[0], logMask & 0x01);
        }

        if (coordinateMask & 0x02)
        {
            coords[0] = 0.0;
            coords[1] = 0.0;
            coords[2] = coordinates[i + nPoints];
            coords[3] = coordinates[i + nPoints];

            coords[4] = coordinates[i + nPoints];

            if (shiftUsed[1])
            {
                shift = yshift[i];
            }

            if (logMask & 0x02)
            {
                /*
                 * The two lower endpoints' y coordinates must be set to 1
                 * since writeBarVerticesToBuffer applies the logarithmic transformation.
                 */
                coords[0] = 1.0;
                coords[1] = 1.0;
            }

            writeBarVerticesToBuffer(buffer, offsets, 1, coords, shift, shiftUsed[1], scale[1], translation[1], logMask & 0x02);
        }

        if (coordinateMask & 0x04)
        {
            coords[0] = coordinates[i + 2 * nPoints];
            coords[1] = coordinates[i + 2 * nPoints];
            coords[2] = coordinates[i + 2 * nPoints];
            coords[3] = coordinates[i + 2 * nPoints];

            coords[4] = coordinates[i + 2* nPoints];

            if (shiftUsed[2])
            {
                shift = zshift[i];
            }

            writeBarVerticesToBuffer(buffer, offsets, 2, coords, shift, shiftUsed[2], scale[2], translation[2], logMask & 0x04);
        }

        if((elementsSize == 4) && (coordinateMask & 0x08))
        {
            buffer[offsets[0] +3] = 1.0;
            buffer[offsets[1] +3] = 1.0;
            buffer[offsets[2] +3] = 1.0;
            buffer[offsets[3] +3] = 1.0;

            buffer[offsets[4] +3] = 1.0;
        }

    }

}

/*
 * To do: -refactor with fillVerticalBarsDecompositionVertices as these two functions are very similar, possibly by implementing
 a PolylineBarDecomposer class.
*/
void PolylineDecomposer::fillHorizontalBarsDecompositionVertices(char* id, float* buffer, int bufferLength, int elementsSize, int coordinateMask, double* scale, double* translation,
                                                                 int logMask, double* coordinates, int nPoints, double* xshift, double* yshift, double* zshift)
{
    double barWidth = 0.0;
    double* pdBarWidth = &barWidth;

    int shiftUsed[3];
    int *piShiftUsed = NULL;

    /*
     * Offsets of the lower-left, lower-right, upper-right and upper-left bar vertices (respectively)
     * and of the polyline vertex proper
     */
    int offsets[5];

    /*
     * {x,y or z}-component values of a bar's 4 vertices (same ordering as the offsets)
     * and of the polyline vertex proper.
     */
    double coords[5];

    double shift = 0.;


    getGraphicObjectProperty(id, __GO_BAR_WIDTH__, jni_double, (void**) &pdBarWidth);

    piShiftUsed = &shiftUsed[0];
    getGraphicObjectProperty(id, __GO_DATA_MODEL_X_COORDINATES_SHIFT_SET__, jni_double_vector, (void**) &piShiftUsed);
    piShiftUsed = &shiftUsed[1];
    getGraphicObjectProperty(id, __GO_DATA_MODEL_Y_COORDINATES_SHIFT_SET__, jni_double_vector, (void**) &piShiftUsed);
    piShiftUsed = &shiftUsed[2];
    getGraphicObjectProperty(id, __GO_DATA_MODEL_Z_COORDINATES_SHIFT_SET__, jni_double_vector, (void**) &piShiftUsed);

    for (int i = 0; i < nPoints; i++)
    {
        offsets[0] = elementsSize*4*i;
        offsets[1] = elementsSize*(4*i+1);
        offsets[2] = elementsSize*(4*i+2);
        offsets[3] = elementsSize*(4*i+3);

        offsets[4] = elementsSize*(4*nPoints+i);

        /* The actual x coordinates correspond to the polyline's y coordinates. */
        if (coordinateMask & 0x01)
        {
            coords[0] = 0.0;
            coords[1] = 0.0;
            coords[2] = coordinates[i + nPoints];
            coords[3] = coordinates[i + nPoints];

            coords[4] = coordinates[i];

            if (shiftUsed[1])
            {
                shift = yshift[i];
            }

            writeBarVerticesToBuffer(buffer, offsets, 0, coords, shift, shiftUsed[1], scale[0], translation[0], logMask & 0x01);
        }

        /* The actual y coordinates correspond to the polyline's x coordinates. */
        if (coordinateMask & 0x02)
        {
            coords[0] = coordinates[i] - 0.5*barWidth;
            coords[1] = coordinates[i] + 0.5*barWidth;
            coords[2] = coordinates[i] + 0.5*barWidth;
            coords[3] = coordinates[i] - 0.5*barWidth;

            coords[4] = coordinates[i + nPoints];

            if (shiftUsed[0])
            {
                shift = xshift[i];
            }

            if (logMask & 0x02)
            {
                /*
                 * The two lower endpoints' y coordinates must be set to 1
                 * since writeBarVerticesToBuffer applies the logarithmic transformation.
                 */
                coords[0] = 1.0;
                coords[1] = 1.0;
            }

            writeBarVerticesToBuffer(buffer, offsets, 1, coords, shift, shiftUsed[0], scale[1], translation[1], logMask & 0x02);
        }

        if (coordinateMask & 0x04)
        {
            coords[0] = coordinates[i + 2 * nPoints];
            coords[1] = coordinates[i + 2 * nPoints];
            coords[2] = coordinates[i + 2 * nPoints];
            coords[3] = coordinates[i + 2 * nPoints];

            coords[4] = coordinates[i + 2* nPoints];

            if (shiftUsed[2])
            {
                shift = zshift[i];
            }

            writeBarVerticesToBuffer(buffer, offsets, 2, coords, shift, shiftUsed[2], scale[2], translation[2], logMask & 0x04);
        }

        if((elementsSize == 4) && (coordinateMask & 0x08))
        {
            buffer[offsets[0] +3] = 1.0;
            buffer[offsets[1] +3] = 1.0;
            buffer[offsets[2] +3] = 1.0;
            buffer[offsets[3] +3] = 1.0;

            buffer[offsets[4] +3] = 1.0;
        }

    }

}

/*
 * To do:
 *  -implement for the other relevant polyline style values.
 *  -fix the no colors written problem (related to polyline C build functions, see below).
 */
void PolylineDecomposer::fillColors(char* id, float* buffer, int bufferLength, int elementsSize)
{
    char* parent = NULL;
    char* parentFigure = NULL;

    int interpColorMode = 0;
    int* piInterpColorMode = &interpColorMode;
    int polylineStyle = 0;
    int* piPolylineStyle = &polylineStyle;
    int nPoints = 0;
    int *piNPoints = &nPoints;
    int colormapSize = 0;
    int* piColormapSize = &colormapSize;
    int bufferOffset = 0;
    int* interpColorVector = NULL;

    double* colormap = NULL;

    getGraphicObjectProperty(id, __GO_INTERP_COLOR_MODE__, jni_bool, (void**) &piInterpColorMode);

    if (interpColorMode == 0)
    {
        return;
    }

    getGraphicObjectProperty(id, __GO_POLYLINE_STYLE__, jni_int, (void**) &piPolylineStyle);

    if (polylineStyle  != 1)
    {
        return;
    }

    getGraphicObjectProperty(id, __GO_DATA_MODEL_NUM_ELEMENTS__, jni_int, (void**) &piNPoints);
    getGraphicObjectProperty(id, __GO_PARENT__, jni_string, (void**) &parent);

    /* Temporary: to avoid getting a null parent_figure property when the object is built */
    if (strcmp(parent, "") == 0)
    {
        return;
    }

    getGraphicObjectProperty(id, __GO_PARENT_FIGURE__, jni_string, (void**) &parentFigure);

    /*
     * In some cases, the polyline's parent figure may be unitialized, when this point is reached from the
     * filled polygons build C functions (xfpolys, with several polygons and a color vector for each one).
     * This check prevents from crashing when getting the colormap. However, it results in incorrectly
     * black-filled polygons, as no colors are written.
     * As the sequentially built polygons are inserted within a Compound object, the latter object may be
     * still unattached to a Figure as its Polyline children are rendered. This occurs about once in 5 to 10,
     * hence possibly caused by a race condition.
     * To be fixed.
     */
    if (strcmp(parentFigure, "") == 0)
    {
        return;
    }

    /*
     * The interpolated color vector is a 3- or 4-element vector.
     * However, if nPoints is greater than 4, we choose to output
     * 4 colors (this behaviour is kept for compatibility, see fillTriangleIndices).
     */
    if (nPoints < 3)
    {
        return;
    }

    getGraphicObjectProperty(id, __GO_INTERP_COLOR_VECTOR__, jni_int_vector, (void**) &interpColorVector);
    getGraphicObjectProperty(parentFigure, __GO_COLORMAP__, jni_double_vector, (void**) &colormap);
    getGraphicObjectProperty(parentFigure, __GO_COLORMAP_SIZE__, jni_int, (void**) &piColormapSize);

    if (nPoints > 4)
    {
        nPoints = 4;
    }

    for (int i = 0; i < nPoints; i++)
    {
        ColorComputer::getDirectColor((double) interpColorVector[i] - 1.0, colormap, colormapSize, &buffer[bufferOffset]);

        if (elementsSize == 4)
        {
            buffer[bufferOffset+3] = 1.0;
        }

        bufferOffset += elementsSize;
    }

    releaseGraphicObjectProperty(__GO_COLORMAP__, colormap, jni_double_vector, colormapSize);
    releaseGraphicObjectProperty(__GO_INTERP_COLOR_VECTOR__, interpColorVector, jni_int_vector, 0);
}

void PolylineDecomposer::fillTextureCoordinates(char* id, float* buffer, int bufferLength)
{
    char* parent = NULL;
    char* parentFigure = NULL;

    int interpColorMode = 0;
    int* piInterpColorMode = &interpColorMode;
    int polylineStyle = 0;
    int* piPolylineStyle = &polylineStyle;
    int nPoints = 0;
    int *piNPoints = &nPoints;
    int colormapSize = 0;
    int* piColormapSize = &colormapSize;
    int bufferOffset = 0;
    int* interpColorVector = NULL;

    double* colormap = NULL;

    getGraphicObjectProperty(id, __GO_INTERP_COLOR_MODE__, jni_bool, (void**) &piInterpColorMode);

    if (interpColorMode == 0)
    {
        return;
    }

    getGraphicObjectProperty(id, __GO_POLYLINE_STYLE__, jni_int, (void**) &piPolylineStyle);

    if (polylineStyle  != 1)
    {
        return;
    }

    getGraphicObjectProperty(id, __GO_DATA_MODEL_NUM_ELEMENTS__, jni_int, (void**) &piNPoints);
    getGraphicObjectProperty(id, __GO_PARENT__, jni_string, (void**) &parent);

    /* Temporary: to avoid getting a null parent_figure property when the object is built */
    if (strcmp(parent, "") == 0)
    {
        return;
    }

    getGraphicObjectProperty(id, __GO_PARENT_FIGURE__, jni_string, (void**) &parentFigure);

    /*
     * In some cases, the polyline's parent figure may be unitialized, when this point is reached from the
     * filled polygons build C functions (xfpolys, with several polygons and a color vector for each one).
     * This check prevents from crashing when getting the colormap. However, it results in incorrectly
     * black-filled polygons, as no colors are written.
     * As the sequentially built polygons are inserted within a Compound object, the latter object may be
     * still unattached to a Figure as its Polyline children are rendered. This occurs about once in 5 to 10,
     * hence possibly caused by a race condition.
     * To be fixed.
     */
    if (strcmp(parentFigure, "") == 0)
    {
        return;
    }

    /*
     * The interpolated color vector is a 3- or 4-element vector.
     * However, if nPoints is greater than 4, we choose to output
     * 4 colors (this behaviour is kept for compatibility, see fillTriangleIndices).
     */
    if (nPoints < 3)
    {
        return;
    }

    getGraphicObjectProperty(id, __GO_INTERP_COLOR_VECTOR__, jni_int_vector, (void**) &interpColorVector);
    getGraphicObjectProperty(parentFigure, __GO_COLORMAP__, jni_double_vector, (void**) &colormap);
    getGraphicObjectProperty(parentFigure, __GO_COLORMAP_SIZE__, jni_int, (void**) &piColormapSize);

    if (nPoints > 4)
    {
        nPoints = 4;
    }

    for (int i = 0; i < nPoints; i++)
    {
        double index = (ColorComputer::getDirectIndex((double) interpColorVector[i] - 1.0, colormapSize) + 2.0 + COLOR_TEXTURE_OFFSET) / (double) (colormapSize + 2);

        buffer[bufferOffset] = (float)index;
        buffer[bufferOffset+1] = 0.0;
        buffer[bufferOffset+2] = 0.0;
        buffer[bufferOffset+3] = 1.0;

        bufferOffset += 4;
    }

    releaseGraphicObjectProperty(__GO_COLORMAP__, colormap, jni_double_vector, colormapSize);
    releaseGraphicObjectProperty(__GO_INTERP_COLOR_VECTOR__, interpColorVector, jni_int_vector, 0);
}

/*
 * To do: see fillIndices
 * -take into account polyline_style.
 */
int PolylineDecomposer::getIndicesSize(char* id)
{
    int nPoints = 0;
    int *piNPoints = &nPoints;
    int polylineStyle = 0;
    int* piPolylineStyle = &polylineStyle;
    int closed = 0;
    int* piClosed = &closed;

    int nIndices = 0;

    getGraphicObjectProperty(id, __GO_DATA_MODEL_NUM_ELEMENTS__, jni_int, (void**) &piNPoints);
    getGraphicObjectProperty(id, __GO_POLYLINE_STYLE__, jni_int, (void**) &piPolylineStyle);
    getGraphicObjectProperty(id, __GO_CLOSED__, jni_bool, (void**) &piClosed);

    /* No facets if 0 points */
    if (nPoints == 0)
    {
        return 0;
    }

    /* Segments */
    if (polylineStyle == 1)
    {
        if (nPoints < 3)
        {
            return 0;
        }

        /* Maximum number of triangles output by the triangulator */
        nIndices = 3*(nPoints-2);
    }
    /* Arrowed segments */
    else if (polylineStyle == 4)
    {
        nIndices = PolylineDecomposer::getArrowTriangleIndicesSize(nPoints, closed);
    }
    /* Filled patch  */
    else if (polylineStyle == 5)
    {
        if (nPoints < 3)
        {
            return 0;
        }

        /* Maximum number of triangles output by the triangulator */
        nIndices = 3*(nPoints-2);
    }
    /* Vertical bars plus segments */
    else if (polylineStyle == 6)
    {
        nIndices = PolylineDecomposer::getBarsDecompositionTriangleIndicesSize(nPoints);
    }
    /* Horizontal bars plus segments */
    else if (polylineStyle == 7)
    {
        nIndices = PolylineDecomposer::getBarsDecompositionTriangleIndicesSize(nPoints);
    }

    return nIndices;
}

int PolylineDecomposer::getArrowTriangleIndicesSize(int nPoints, int closed)
{
    int nIndices = 0;

    if (nPoints < 2)
    {
        nIndices = 0;
    }
    else
    {
        nIndices = 3*(nPoints-1);

        if (closed)
        {
            nIndices += 3;
        }
    }

    return nIndices;
}

int PolylineDecomposer::getBarsDecompositionTriangleIndicesSize(int nPoints)
{
    return 2*3*nPoints;
}

/*
 * To do:
 * -take into account the polyline style property (note: vertical bars -style 6- are filled
 *  whatever fill_mode's value), by implementing the relevant functions (see fillTriangleIndices),
 *  as the curve must be filled if fill_mode set to on, whatever its polyline style value.
 */
int PolylineDecomposer::fillIndices(char* id, int* buffer, int bufferLength, int logMask)
{
    double* coordinates = NULL;
    double* xshift = NULL;
    double* yshift = NULL;
    double* zshift = NULL;

    int nPoints = 0;
    int* piNPoints = &nPoints;
    int polylineStyle = 0;
    int* piPolylineStyle = &polylineStyle;
    int fillMode = 0;
    int* piFillMode = &fillMode;

    getGraphicObjectProperty(id, __GO_DATA_MODEL_COORDINATES__, jni_double_vector, (void**) &coordinates);
    getGraphicObjectProperty(id, __GO_DATA_MODEL_NUM_ELEMENTS__, jni_int, (void**) &piNPoints);
    getGraphicObjectProperty(id, __GO_POLYLINE_STYLE__, jni_int, (void**) &piPolylineStyle);
    getGraphicObjectProperty(id, __GO_DATA_MODEL_X_COORDINATES_SHIFT__, jni_double_vector, (void**) &xshift);
    getGraphicObjectProperty(id, __GO_DATA_MODEL_Y_COORDINATES_SHIFT__, jni_double_vector, (void**) &yshift);
    getGraphicObjectProperty(id, __GO_DATA_MODEL_Z_COORDINATES_SHIFT__, jni_double_vector, (void**) &zshift);

    getGraphicObjectProperty(id, __GO_FILL_MODE__, jni_bool, (void**) &piFillMode);

    /* 0 triangles if 0 points */
    if (nPoints == 0)
    {
        return 0;
    }

    if (polylineStyle == 1)
    {
        return fillTriangleIndices(id, buffer, bufferLength, logMask, coordinates, nPoints, xshift, yshift, zshift, fillMode, polylineStyle);
    }
    else if (polylineStyle == 4)
    {
        return fillArrowTriangleIndices(id, buffer, bufferLength, logMask, coordinates, nPoints, xshift, yshift, zshift);
    }
    else if (polylineStyle == 5)
    {
        /* Set fill mode to on, since patches are always filled whatever fill mode's value */
        return fillTriangleIndices(id, buffer, bufferLength, logMask, coordinates, nPoints, xshift, yshift, zshift, 1, polylineStyle);
    }
    else if (polylineStyle == 6)
    {
        return fillBarsDecompositionTriangleIndices(id, buffer, bufferLength, logMask, coordinates, nPoints, xshift, yshift, zshift);
    }
    else if (polylineStyle == 7)
    {
        return fillBarsDecompositionTriangleIndices(id, buffer, bufferLength, logMask, coordinates, nPoints, xshift, yshift, zshift);
    }

    return 0;
}

int PolylineDecomposer::fillTriangleIndices(char* id, int* buffer, int bufferLength,
                                            int logMask, double* coordinates, int nPoints, double* xshift, double* yshift, double* zshift, int fillMode, int polylineStyle)
{
    double coords[4][3];

    int interpColorMode = 0;
    int* piInterpColorMode = &interpColorMode;
    int triangulate = 0;

    int isValid = 0;
    int tmpValid = 0;
    int nIndices = 0;

    /* At least 3 points needed */
    if (nPoints < 3)
    {
        return 0;
    }

    if (fillMode == 0)
    {
        return 0;
    }

    getGraphicObjectProperty(id, __GO_INTERP_COLOR_MODE__, jni_bool, (void**) &piInterpColorMode);

    /*
     * Do not triangulate if the interpolated color mode is set to 'on' and the polyline style is filled patch (5).
     * The quadrilateral facet decomposition function is used instead, if nPoints == 4,
     * for compatibility reasons, although triangulation could be used.
     */
    if (interpColorMode && polylineStyle != 5)
    {
        triangulate = 0;
    }
    else if (nPoints > 3)
    {
        /* Perform triangulation only if more than 3 points */
        triangulate = 1;
    }

    if (triangulate)
    {
        Triangulator triangulator;
        int numTriangles;
        int* indices;

        isValid = 1;

        for (int i = 0; i < nPoints; i++)
        {
            getShiftedPolylinePoint(coordinates, xshift, yshift, zshift, nPoints, i, &coords[0][0], &coords[0][1], &coords[0][2]);

            tmpValid = DecompositionUtils::isValid(coords[0][0], coords[0][1], coords[0][2]);

            if (logMask)
            {
                tmpValid &= DecompositionUtils::isLogValid(coords[0][0], coords[0][1], coords[0][2], logMask);

                if (logMask & 0x01)
                {
                    coords[0][0] = DecompositionUtils::getLog10Value(coords[0][0]);
                }

                if (logMask & 0x02)
                {
                    coords[0][1] = DecompositionUtils::getLog10Value(coords[0][1]);
                }

                if (logMask & 0x04)
                {
                    coords[0][2] = DecompositionUtils::getLog10Value(coords[0][2]);
                }
            }

            isValid &= tmpValid;

            if (!isValid)
            {
                break;
            }

            triangulator.addPoint(coords[0][0], coords[0][1], coords[0][2]);
        }

        if (!isValid)
        {
            return 0;
        }

        /* Triangulate */
        triangulator.initialize();
        triangulator.triangulate();

        numTriangles = triangulator.getNumberTriangles();
        indices = triangulator.getIndices();

        for (int i = 0; i < numTriangles; i++)
        {
            buffer[3*i] = indices[3*i];
            buffer[3*i+1] = indices[3*i+1];
            buffer[3*i+2] = indices[3*i+2];
            nIndices += 3;
        }

        triangulator.clear();

        return nIndices;
    }
    else
    {
        /* Do not triangulate: either the interpolation color mode is set to on or it is not and there are only 3 points. */

        /* 3 points: only one triangle output */
        if (nPoints == 3)
        {
            getShiftedPolylinePoint(coordinates, xshift, yshift, zshift, nPoints, 0, &coords[0][0], &coords[0][1], &coords[0][2]);
            getShiftedPolylinePoint(coordinates, xshift, yshift, zshift, nPoints, 1, &coords[1][0], &coords[1][1], &coords[1][2]);
            getShiftedPolylinePoint(coordinates, xshift, yshift, zshift, nPoints, 2, &coords[2][0], &coords[2][1], &coords[2][2]);

            tmpValid = DecompositionUtils::isValid(coords[0][0], coords[0][1], coords[0][2]);
            tmpValid &= DecompositionUtils::isValid(coords[1][0], coords[1][1], coords[1][2]);
            tmpValid &= DecompositionUtils::isValid(coords[2][0], coords[2][1], coords[2][2]);

            isValid = tmpValid;

            if (logMask)
            {
                tmpValid = DecompositionUtils::isLogValid(coords[0][0], coords[0][1], coords[0][2], logMask);
                tmpValid &= DecompositionUtils::isLogValid(coords[1][0], coords[1][1], coords[1][2], logMask);
                tmpValid &= DecompositionUtils::isLogValid(coords[2][0], coords[2][1], coords[2][2], logMask);
                isValid &= tmpValid;
            }

            if (isValid)
            {
                buffer[0] = 0;
                buffer[1] = 1;
                buffer[2] = 2;

                nIndices += 3;
            }
        }
        else if (nPoints >= 4)
        {
            /*
             * 4 points: the quadrilateral facet decomposition algorithm is used.
             * If the Polyline has more than 4 points, we still output two triangles
             * corresponding to the first 4 points. This behaviour is kept for compatibility.
             * Possible correction: do not output indices if there are more than 4 points and
             * the interpolation color mode is set to on.
             */
            int facetVertexIndices[4] = {0, 1, 2, 3};

            getShiftedPolylinePoint(coordinates, xshift, yshift, zshift, nPoints, 0, &coords[0][0], &coords[0][1], &coords[0][2]);
            getShiftedPolylinePoint(coordinates, xshift, yshift, zshift, nPoints, 1, &coords[1][0], &coords[1][1], &coords[1][2]);
            getShiftedPolylinePoint(coordinates, xshift, yshift, zshift, nPoints, 2, &coords[2][0], &coords[2][1], &coords[2][2]);
            getShiftedPolylinePoint(coordinates, xshift, yshift, zshift, nPoints, 3, &coords[3][0], &coords[3][1], &coords[3][2]);

            tmpValid = DecompositionUtils::isValid(coords[0][0], coords[0][1], coords[0][2]);
            tmpValid &= DecompositionUtils::isValid(coords[1][0], coords[1][1], coords[1][2]);
            tmpValid &= DecompositionUtils::isValid(coords[2][0], coords[2][1], coords[2][2]);
            tmpValid &= DecompositionUtils::isValid(coords[3][0], coords[3][1], coords[3][2]);

            isValid = tmpValid;

            if (logMask)
            {
                tmpValid = DecompositionUtils::isLogValid(coords[0][0], coords[0][1], coords[0][2], logMask);
                tmpValid &= DecompositionUtils::isLogValid(coords[1][0], coords[1][1], coords[1][2], logMask);
                tmpValid &= DecompositionUtils::isLogValid(coords[2][0], coords[2][1], coords[2][2], logMask);
                tmpValid &= DecompositionUtils::isLogValid(coords[3][0], coords[3][1], coords[3][2], logMask);
                isValid &= tmpValid;
            }

            if (isValid)
            {
                DecompositionUtils::getDecomposedQuadTriangleIndices(coords, facetVertexIndices, buffer);
                nIndices += 6;
            }
        }

    }

    return nIndices;
}

int PolylineDecomposer::fillArrowTriangleIndices(char* id, int* buffer, int bufferLength,
                                                 int logMask, double* coordinates, int nPoints, double* xshift, double* yshift, double* zshift)
{
    int closed = 0;
    int* piClosed = &closed;

    int currentValid = 0;
    int nextValid = 0;

    int firstArrowVertex = 0;
    int nArrows = 0;

    int offset = 0;
    int numberValidIndices = 0;

    /* At least 2 points needed to form segments */
    if (nPoints < 2)
    {
        return 0;
    }

    getGraphicObjectProperty(id, __GO_CLOSED__, jni_bool, (void**) &piClosed);

    /* If closed, an additional segment is present */
    if (closed)
    {
        nArrows = nPoints;
    }
    else
    {
        nArrows = nPoints-1;
    }

    /*
     * Arrow head vertices are stored consecutively after all the line vertices.
     * Hence the offset to the first arrow vertex.
     */
    firstArrowVertex = nPoints;

    for (int i = 0; i < nArrows; i++)
    {
        /* Indices of the tip, left and right vertices */
        int n = 3 * i;
        buffer[n] = firstArrowVertex + n;
        buffer[n + 1] = buffer[n] + 1;
        buffer[n + 2] = buffer[n] + 2;
    }

    return 3 * nArrows;
}

/*
 * Only bars are filled at the present moment, the curve is not.
 * See fillTriangleIndices for more information.
 */
int PolylineDecomposer::fillBarsDecompositionTriangleIndices(char* id, int* buffer, int bufferLength,
                                                             int logMask, double* coordinates, int nPoints, double* xshift, double* yshift, double* zshift)
{
    double barWidth = 0.0;
    double* pdBarWidth = &barWidth;
    double coordsi[3];

    int triangleIndices[6];
    int offset = 0;
    int numberValidIndices = 0;

    if (nPoints == 0)
    {
        return 0;
    }

    getGraphicObjectProperty(id, __GO_BAR_WIDTH__, jni_double, (void**) &pdBarWidth);

    /* 0 indices if the bar width is invalid, as it is the same for all bars. */
    if (!DecompositionUtils::isValid(barWidth))
    {
        return 0;
    }

    /*
     * Gets the indices corresponding to a rectangle decomposed into 2 triangles.
     * All the bars are decomposed the same way.
     */
    DecompositionUtils::getDecomposedRectangleTriangleIndices(triangleIndices);

    /* Bars */
    for (int i = 0; i < nPoints; i++)
    {
        getShiftedPolylinePoint(coordinates, xshift, yshift, zshift, nPoints, i, &coordsi[0], &coordsi[1], &coordsi[2]);

        if (DecompositionUtils::isValid(coordsi[0], coordsi[1], coordsi[2]))
        {
            if (logMask && !DecompositionUtils::isLogValid(coordsi[0], coordsi[1], coordsi[2], logMask))
            {
                continue;
            }

            buffer[6*offset] = 4*i + triangleIndices[0];
            buffer[6*offset+1] = 4*i + triangleIndices[1];
            buffer[6*offset+2] = 4*i + triangleIndices[2];
            buffer[6*offset+3] = 4*i + triangleIndices[3];
            buffer[6*offset+4] = 4*i + triangleIndices[4];
            buffer[6*offset+5] = 4*i + triangleIndices[5];

            numberValidIndices += 6;
            offset++;
        }

    }

    return numberValidIndices;
}

int PolylineDecomposer::getWireIndicesSize(char* id)
{
    int nPoints = 0;
    int *piNPoints = &nPoints;
    int polylineStyle = 0;
    int* piPolylineStyle = &polylineStyle;

    int lineMode = 0;
    int* piLineMode = &lineMode;

    int closed = 0;
    int* piClosed = &closed;

    getGraphicObjectProperty(id, __GO_POLYLINE_STYLE__, jni_int, (void**) &piPolylineStyle);
    getGraphicObjectProperty(id, __GO_DATA_MODEL_NUM_ELEMENTS__, jni_int, (void**) &piNPoints);
    getGraphicObjectProperty(id, __GO_LINE_MODE__, jni_bool, (void**) &piLineMode);
    getGraphicObjectProperty(id, __GO_CLOSED__, jni_bool, (void**) &piClosed);

    /* No segments if 0 points */
    if (nPoints == 0)
    {
        return 0;
    }

    /* Segments */
    if (polylineStyle == 1)
    {
        return getSegmentsDecompositionSegmentIndicesSize(nPoints, lineMode, closed);
    }
    /* Staircase */
    else if (polylineStyle == 2)
    {
        return getStairDecompositionSegmentIndicesSize(nPoints, lineMode, closed);
    }
    /* Vertical segments plus segments */
    else if (polylineStyle == 3)
    {
        return getVerticalLinesDecompositionSegmentIndicesSize(nPoints, lineMode);
    }
    /* Segments with arrow heads */
    else if (polylineStyle == 4)
    {
        return getSegmentsDecompositionSegmentIndicesSize(nPoints, lineMode, closed);
    }
    /* Filled patch  */
    else if (polylineStyle == 5)
    {
        return getSegmentsDecompositionSegmentIndicesSize(nPoints, lineMode, closed);
    }
    /* Vertical bars plus segments */
    else if (polylineStyle == 6)
    {
        return getBarsDecompositionSegmentIndicesSize(nPoints, lineMode);
    }
    /* Horizontal bars plus segments */
    else if (polylineStyle == 7)
    {
        return getBarsDecompositionSegmentIndicesSize(nPoints, lineMode);
    }
    else
    {
        return 0;
    }

}

int PolylineDecomposer::getSegmentsDecompositionSegmentIndicesSize(int nPoints, int lineMode, int closed)
{
    if (nPoints < 2)
    {
        return 0;
    }

    if (lineMode)
    {
        if (closed)
        {
            return nPoints + 1;
        }
        else
        {
            return nPoints;
        }
    }
    else
    {
        return 0;
    }
}

int PolylineDecomposer::getStairDecompositionSegmentIndicesSize(int nPoints, int lineMode, int closed)
{
    if (nPoints < 2)
    {
        return 0;
    }

    if (lineMode)
    {
        if (closed)
        {
            return 2 * nPoints + 1;
        }
        else
        {
            return 2 * nPoints - 1;
        }
    }
    else
    {
        return 0;
    }
}

int PolylineDecomposer::getVerticalLinesDecompositionSegmentIndicesSize(int nPoints, int lineMode)
{
    if (nPoints == 0)
    {
        return 0;
    }

    if (lineMode)
    {
        return 2*(nPoints) + 2*(nPoints-1);
    }
    else
    {
        return 2*(nPoints);
    }
}

int PolylineDecomposer::getBarsDecompositionSegmentIndicesSize(int nPoints, int lineMode)
{
    if (nPoints == 0)
    {
        return 0;
    }

    if (lineMode)
    {
        return 2*4*(nPoints) + 2*(nPoints-1);
    }
    else
    {
        return 2*4*(nPoints);
    }
}

int PolylineDecomposer::fillWireIndices(char* id, int* buffer, int bufferLength, int logMask)
{
    double* coordinates = NULL;
    double* xshift = NULL;
    double* yshift = NULL;
    double* zshift = NULL;

    int polylineStyle = 0;
    int* piPolylineStyle = &polylineStyle;
    int nPoints = 0;
    int* piNPoints = &nPoints;
    int closed = 0;
    int* piClosed = &closed;
    int lineMode = 0;
    int* piLineMode = &lineMode;

    getGraphicObjectProperty(id, __GO_POLYLINE_STYLE__, jni_int, (void**) &piPolylineStyle);

    getGraphicObjectProperty(id, __GO_DATA_MODEL_COORDINATES__, jni_double_vector, (void**) &coordinates);
    getGraphicObjectProperty(id, __GO_DATA_MODEL_NUM_ELEMENTS__, jni_int, (void**) &piNPoints);
    getGraphicObjectProperty(id, __GO_DATA_MODEL_X_COORDINATES_SHIFT__, jni_double_vector, (void**) &xshift);
    getGraphicObjectProperty(id, __GO_DATA_MODEL_Y_COORDINATES_SHIFT__, jni_double_vector, (void**) &yshift);
    getGraphicObjectProperty(id, __GO_DATA_MODEL_Z_COORDINATES_SHIFT__, jni_double_vector, (void**) &zshift);

    getGraphicObjectProperty(id, __GO_LINE_MODE__, jni_bool, (void**) &piLineMode);
    getGraphicObjectProperty(id, __GO_CLOSED__, jni_bool, (void**) &piClosed);

    if (polylineStyle == 1)
    {
        return fillSegmentsDecompositionSegmentIndices(id, buffer, bufferLength, logMask, coordinates, nPoints, xshift, yshift, zshift, lineMode, closed);
    }
    else if (polylineStyle == 2)
    {
        return fillStairDecompositionSegmentIndices(id, buffer, bufferLength, logMask, coordinates, nPoints, xshift, yshift, zshift, lineMode, closed);
    }
    else if (polylineStyle == 3)
    {
        return fillVerticalLinesDecompositionSegmentIndices(id, buffer, bufferLength, logMask, coordinates, nPoints, xshift, yshift, zshift, lineMode);
    }
    else if (polylineStyle == 4)
    {
        return fillSegmentsDecompositionSegmentIndices(id, buffer, bufferLength, logMask, coordinates, nPoints, xshift, yshift, zshift, lineMode, closed);
    }
    else if (polylineStyle == 5)
    {
        return fillSegmentsDecompositionSegmentIndices(id, buffer, bufferLength, logMask, coordinates, nPoints, xshift, yshift, zshift, lineMode, closed);
    }
    else if (polylineStyle == 6)
    {
        return fillBarsDecompositionSegmentIndices(id, buffer, bufferLength, logMask, coordinates, nPoints, xshift, yshift, zshift, lineMode);
    }
    else if (polylineStyle == 7)
    {
        return fillBarsDecompositionSegmentIndices(id, buffer, bufferLength, logMask, coordinates, nPoints, xshift, yshift, zshift, lineMode);
    }

    return 0;
}

int PolylineDecomposer::fillSegmentsDecompositionSegmentIndices(char* id, int* buffer, int bufferLength,
                                                                int logMask, double* coordinates, int nPoints, double* xshift, double* yshift, double* zshift, int lineMode, int closed)
{
    /* If less than 2 points, no segments */
    if (nPoints < 2)
    {
        return 0;
    }

    if (lineMode == 0)
    {
        return 0;
    }

    for (int i = 0; i < nPoints; i++)
    {
        buffer[i] = i;
    }

    if (closed)
    {
        buffer[nPoints] = 0;
    }

    return closed ? (nPoints + 1) : nPoints;
}

int PolylineDecomposer::fillStairDecompositionSegmentIndices(char* id, int* buffer, int bufferLength,
                                                             int logMask, double* coordinates, int nPoints, double* xshift, double* yshift, double* zshift, int lineMode, int closed)
{
    int currentValid = 0;
    int middleVertexValid = 0;
    int nextValid = 0;

    int offset = 0;
    int numberValidIndices = 0;

    /* If less than 2 points, no segments */
    if (nPoints < 2)
    {
        return 0;
    }

    if (lineMode == 0)
    {
        return 0;
    }

    for (int i = 0; i < 2 * nPoints - 1; i++)
    {
        buffer[i] = i;
    }

    if (closed)
    {
        buffer[2 * nPoints - 1] = 2 * nPoints - 1;
        buffer[2 * nPoints] = 0;
    }

    return closed ? (2 * nPoints + 1) : (2 * nPoints - 1);
}

int PolylineDecomposer::fillVerticalLinesDecompositionSegmentIndices(char* id, int* buffer, int bufferLength,
                                                                     int logMask, double* coordinates, int nPoints, double* xshift, double* yshift, double* zshift, int lineMode)
{
    double coordsi[3];

    int offset = 0;
    int numberValidIndices = 0;

    if (nPoints == 0)
    {
        return 0;
    }

    /* Vertical lines */
    for (int i = 0; i < nPoints; i++)
    {
        getShiftedPolylinePoint(coordinates, xshift, yshift, zshift, nPoints, i, &coordsi[0], &coordsi[1], &coordsi[2]);

        if (DecompositionUtils::isValid(coordsi[0], coordsi[1], coordsi[2]))
        {
            if (logMask && !DecompositionUtils::isLogValid(coordsi[0], coordsi[1], coordsi[2], logMask))
            {
                continue;
            }

            buffer[2*offset] = 2*i;
            buffer[2*offset+1] = 2*i+1;

            numberValidIndices += 2;
            offset++;
        }

    }

    if (lineMode)
    {
        int currentValid;
        int nextValid;

        getShiftedPolylinePoint(coordinates, xshift, yshift, zshift, nPoints, 0, &coordsi[0], &coordsi[1], &coordsi[2]);

        currentValid = DecompositionUtils::isValid(coordsi[0], coordsi[1], coordsi[2]);

        if (logMask)
        {
            currentValid &= DecompositionUtils::isLogValid(coordsi[0], coordsi[1], coordsi[2], logMask);
        }

        for (int i = 0; i < nPoints-1; i++)
        {
            getShiftedPolylinePoint(coordinates, xshift, yshift, zshift, nPoints, i+1, &coordsi[0], &coordsi[1], &coordsi[2]);

            nextValid = DecompositionUtils::isValid(coordsi[0], coordsi[1], coordsi[2]);

            if (logMask)
            {
                nextValid &= DecompositionUtils::isLogValid(coordsi[0], coordsi[1], coordsi[2], logMask);
            }

            if (currentValid && nextValid)
            {
                buffer[2*offset] = 2*i+1;
                buffer[2*offset+1] = 2*(i+1)+1;

                numberValidIndices += 2;
                offset++;
            }

            currentValid = nextValid;
        }
    }

    return numberValidIndices;
}


int PolylineDecomposer::fillBarsDecompositionSegmentIndices(char* id, int* buffer, int bufferLength,
                                                            int logMask, double* coordinates, int nPoints, double* xshift, double* yshift, double* zshift, int lineMode)
{
    double barWidth = 0.0;
    double* pdBarWidth = &barWidth;
    double coordsi[3];

    int barWidthValid = 0;
    int offset = 0;
    int numberValidIndices = 0;

    if (nPoints == 0)
    {
        return 0;
    }

    getGraphicObjectProperty(id, __GO_BAR_WIDTH__, jni_double, (void**) &pdBarWidth);

    barWidthValid = DecompositionUtils::isValid(barWidth);

    /* 0 bar segment indices if the bar width is invalid, as it is the same for all bars. */
    if (barWidthValid)
    {
        /* Bars */
        for (int i = 0; i < nPoints; i++)
        {
            getShiftedPolylinePoint(coordinates, xshift, yshift, zshift, nPoints, i, &coordsi[0], &coordsi[1], &coordsi[2]);

            if (DecompositionUtils::isValid(coordsi[0], coordsi[1], coordsi[2]))
            {
                if (logMask && !DecompositionUtils::isLogValid(coordsi[0], coordsi[1], coordsi[2], logMask))
                {
                    continue;
                }

                buffer[8*offset] = 4*i;
                buffer[8*offset+1] = 4*i+1;
                buffer[8*offset+2] = 4*i+1;
                buffer[8*offset+3] = 4*i+2;
                buffer[8*offset+4] = 4*i+2;
                buffer[8*offset+5] = 4*i+3;
                buffer[8*offset+6] = 4*i+3;
                buffer[8*offset+7] = 4*i;

                numberValidIndices += 8;
                offset++;
            }

        }

    }

    /* Lines */
    if (lineMode)
    {
        int loffset = 0;

        int currentValid;
        int nextValid;

        getShiftedPolylinePoint(coordinates, xshift, yshift, zshift, nPoints, 0, &coordsi[0], &coordsi[1], &coordsi[2]);

        currentValid = DecompositionUtils::isValid(coordsi[0], coordsi[1], coordsi[2]);

        if (logMask)
        {
            currentValid &= DecompositionUtils::isLogValid(coordsi[0], coordsi[1], coordsi[2], logMask);
        }

        for (int i = 0; i < nPoints-1; i++)
        {
            getShiftedPolylinePoint(coordinates, xshift, yshift, zshift, nPoints, i+1, &coordsi[0], &coordsi[1], &coordsi[2]);

            nextValid = DecompositionUtils::isValid(coordsi[0], coordsi[1], coordsi[2]);

            if (logMask)
            {
                nextValid &= DecompositionUtils::isLogValid(coordsi[0], coordsi[1], coordsi[2], logMask);
            }

            if (currentValid && nextValid)
            {
                buffer[8*offset+2*loffset] = 4*nPoints +i;
                buffer[8*offset+2*loffset+1] = 4*nPoints +i+1;

                numberValidIndices += 2;
                loffset++;
            }

            currentValid = nextValid;
        }
    }

    return numberValidIndices;
}

void PolylineDecomposer::getShiftedPolylinePoint(double* coordinates, double* xshift, double* yshift, double* zshift, int nPoints, int index,
                                                 double* x, double* y, double* z)
{
    *x = coordinates[index];

    if (xshift != NULL)
    {
        *x += xshift[index];
    }

    *y = coordinates[index + nPoints];

    if (yshift != NULL)
    {
        *y += yshift[index];
    }

    *z = coordinates[index + 2*nPoints];

    if (zshift != NULL)
    {
        *z += zshift[index];
    }

}

