#include <stdlib.h>
#include "GoTorus.h"

#define PI 3.14159265358979324

GoTorus::GoTorus(double radius,
                 int    slices,
                 double ringRadius,
                 int    ringSlices)
{
    this->radius = radius;
    this->slices = slices;
    this->ringRadius = ringRadius;
    this->ringSlices = ringSlices;

    solidDirty = true;
    wireDirty = true;
}

void GoTorus::renderSolid(Go *go)
{
    int i;

    if(solidDirty)
        updateSolid();
    
    go->push(Go::FLAGS);
    go->enable(Go::CULL);

    for(i = 0; i < ringSlices; i++) {
        go->render(dataSolid[i]);
    }
    
    go->pop(Go::FLAGS);
}

void GoTorus::renderWire(Go *go)
{
    int i;

    if(wireDirty)
        updateWire();
    
    for(i = 0; i < ringSlices + slices + 1; i++) {
        go->render(dataWire[i]);
    }
}

void GoTorus::renderWireSlices(Go *go)
{
    int i;

    if(wireDirty)
        updateWire();
    
    for(i = ringSlices; i < ringSlices + slices + 1; i++) {
        go->render(dataWire[i]);
    }
}

void GoTorus::updateSolid(void)
{
    int     i, j;

    //
    // Create the data.
    //
    
    double d[slices + 1][ringSlices + 1][3];
    double c[slices + 1][ringSlices + 1][3];

    double ringSliceAngleInc = 2.0 * PI / ringSlices;
    double ringSliceAngle = 0.0;

    for(i = 0; i <= ringSlices; i++) {

        d[0][i][0] = radius + ringRadius * cos(ringSliceAngle);;
        d[0][i][1] = 0;
        d[0][i][2] = ringRadius * sin(ringSliceAngle);

        c[0][i][0] = radius;
        c[0][i][1] = 0;
        c[0][i][2] = 0;

        ringSliceAngle += ringSliceAngleInc;
    }

    double sliceAngleInc = 2.0 * PI / slices;
    double sliceAngle = sliceAngleInc;

    for(i = 1; i <= slices; i++) {

        for(j = 0; j <= ringSlices; j++) {

            d[i][j][0] = d[0][j][0] * cos(sliceAngle);
            d[i][j][1] = d[0][j][0] * sin(sliceAngle);
            d[i][j][2] = d[0][j][2];

            c[i][j][0] = c[0][j][0] * cos(sliceAngle);
            c[i][j][1] = c[0][j][0] * sin(sliceAngle);
            c[i][j][2] = c[0][j][2];
        }

        sliceAngle += sliceAngleInc;
    }

    //
    // Create the triangle strips.
    //

    dataSolid = new GoTriangleStrip *[ringSlices];

    for(i = 0; i < ringSlices; i++) {

        dataSolid[i] = new GoTriangleStrip((slices + 1) * 2, Go::NORMAL);

        for(j = 0; j <= slices; j++) {
            
            dataSolid[i]->xyz(j*2, d[j][i+1][0], d[j][i+1][1], d[j][i+1][2]);
            dataSolid[i]->xyz(j*2+1, d[j][i][0], d[j][i][1], d[j][i][2]);

            dataSolid[i]->ijk(j*2,
                              d[j][i+1][0] - c[j][i+1][0],
                              d[j][i+1][1] - c[j][i+1][1],
                              d[j][i+1][2] - c[j][i+1][2]);
            dataSolid[i]->ijk(j*2+1,
                              d[j][i][0] - c[j][i][0],
                              d[j][i][1] - c[j][i][1],
                              d[j][i][2] - c[j][i][2]);
        }
    }
    
    solidDirty = false;
}

void GoTorus::updateWire(void)
{
    int     i, j;

    //
    // Create the data.
    //
    
    double d[slices + 1][ringSlices + 1][3];

    double ringSliceAngleInc = 2.0 * PI / ringSlices;
    double ringSliceAngle = 0.0;

    for(i = 0; i <= ringSlices; i++) {

        d[0][i][0] = radius + ringRadius * cos(ringSliceAngle);;
        d[0][i][1] = 0;
        d[0][i][2] = ringRadius * sin(ringSliceAngle);

        ringSliceAngle += ringSliceAngleInc;
    }

    double sliceAngleInc = 2.0 * PI / slices;
    double sliceAngle = sliceAngleInc;

    for(i = 1; i <= slices; i++) {

        for(j = 0; j <= ringSlices; j++) {

            d[i][j][0] = d[0][j][0] * cos(sliceAngle);
            d[i][j][1] = d[0][j][0] * sin(sliceAngle);
            d[i][j][2] = d[0][j][2];
        }

        sliceAngle += sliceAngleInc;
    }

    //
    // Create the line loops.
    //

    dataWire = new GoLineLoop *[ringSlices + slices + 1];

    int count = 0;

    for(i = 0; i < ringSlices; i++) {

        dataWire[count] = new GoLineLoop(slices);

        for(j = 0; j < slices; j++) {
            
            dataWire[count]->xyz(j, d[j][i][0], d[j][i][1], d[j][i][2]);
        }

        count++;
    }

    for(j = 0; j <= slices; j++) {
        
        dataWire[count] = new GoLineLoop(ringSlices);

        for(i = 0; i < ringSlices; i++) {

            dataWire[count]->xyz(i, d[j][i][0], d[j][i][1], d[j][i][2]);
        }

        count++;
    }

    wireDirty = false;
}