#include <grass/gis.h>
#include "vizual.h"

/*
 ** the corner of the cube
 **
 **
 */
/* this subroutine draws polygons from dspf file using flat shading */

static float ZNexag = 1.0;

void fill_data_cube();
void get_vert_color();

void set_ZNexag(exag) float exag;
{
    ZNexag = exag;
}

void get_ZNexag(exag) float *exag;
{
    *exag = ZNexag;
}

void fdraw_polys(
    D_spec) struct dspec *D_spec; /*structure containing interactive input */
{
    int x, y, z;
    int t, p; /* LOOP COUNTER */
    double xadd, yadd, zadd;
    poly_info *Polyfax; /* local pointer */
    float tmp_vect[3];
    Cube_data Cube;     /*structure containing poly info */
    cube_info *cubefax; /* local pointer */
    int curr;
    short color[3];

    cubefax = Cube.data;
    glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
    glEnable(GL_COLOR_MATERIAL);

    t = D_spec->Thresh;
    for (z = 0; z < Headfax.zdim; z++) {
        zadd = z * Headfax.ydim;
        for (y = 0; y < Headfax.ydim; y++) {
            yadd = y * D_spec->yscale;
            for (x = 0; x < Headfax.xdim; x++) {
                if (!read_cube(&Cube, &Headfax))
                    continue;
                if (!((x > (D_spec->B[X] - 1)) && (x < D_spec->E[X]) &&
                      (y > (D_spec->B[Y] - 1)) && (y < D_spec->E[Y]) &&
                      (z > (D_spec->B[Z] - 1)) && (z < D_spec->E[Z])))
                    continue;

                xadd = x * D_spec->xscale;

                for (t = 0; t < Cube.n_thresh; t++) {
                    if (cubefax[t].t_ndx == D_spec->Thresh) {
                        curr = D_spec->Thresh;
                        get_cat_color(Headfax.linefax.tvalue[curr],
                                      D_spec->ctable, color);
                        glColor3sv(color);

                        for (p = 0; p < cubefax[t].npoly; p++) {
                            /* center data */
                            Polyfax = &(cubefax[t].poly[p]);

                            glBegin(GL_POLYGON);

                            tmp_vect[0] =
                                X_sign * G_sign * (Polyfax->n1[0] / 127. - 1.);
                            tmp_vect[1] =
                                X_sign * G_sign * (Polyfax->n1[1] / 127. - 1.);
                            tmp_vect[2] =
                                X_sign * G_sign * (Polyfax->n1[2] / 127. - 1.);
                            glNormal3fv(tmp_vect);
                            tmp_vect[0] =
                                Polyfax->v1[0] / 255. * D_spec->xscale + xadd;
                            tmp_vect[1] =
                                Polyfax->v1[1] / 255. * D_spec->yscale + yadd;
                            tmp_vect[2] =
                                Polyfax->v1[2] / 255. * D_spec->zscale + zadd;
                            glVertex3fv(tmp_vect);

                            tmp_vect[0] =
                                Polyfax->v2[0] / 255. * D_spec->xscale + xadd;
                            tmp_vect[1] =
                                Polyfax->v2[1] / 255. * D_spec->yscale + yadd;
                            tmp_vect[2] =
                                Polyfax->v2[2] / 255. * D_spec->zscale + zadd;
                            glVertex3fv(tmp_vect);

                            tmp_vect[0] =
                                Polyfax->v3[0] / 255. * D_spec->xscale + xadd;
                            tmp_vect[1] =
                                Polyfax->v3[1] / 255. * D_spec->yscale + yadd;
                            tmp_vect[2] =
                                Polyfax->v3[2] / 255. * D_spec->zscale + zadd;
                            glVertex3fv(tmp_vect);

                            glEnd();
                        }

                        break; /* dont bother w/ other thresholds then */
                    }
                }
            }
        }
    }
    glDisable(GL_COLOR_MATERIAL);
}

void normalize(v) float v[];
{
    float len;

    len = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
    v[0] /= len;
    v[1] /= len;
    v[2] /= len;
}

/******************************** gdraw_polys *********************************/
/* this subroutine draws polygons from a dspf file using 3 different normals */

void gdraw_polys(D_spec) struct dspec *D_spec;
{
    int x, y, z;
    int t, p; /* LOOP COUNTER */
    float xadd, yadd, zadd;
    poly_info *Polyfax; /* local pointer */
    cube_info *cubefax; /* local pointer */

    int curr;
    float xres, yres, zres, norm[3];
    Cube_data Cube; /*structure containing poly info */
    file_info chead;
    int color_on = 0;
    int xdim = 0, ydim = 0;
    float *slice[2];
    float *tmp;
    int i;
    int level, row[2], col[2];
    short color[3];
    short data[8][3];

    glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
    glEnable(GL_COLOR_MATERIAL);
    /* a color data file has been requested */
    if (D_spec->cfile != NULL) {
        chead.datainfp = D_spec->cfile;
        /* TO CHANGE
           if (r3read_header (&chead) < 0)
           fprintf (stderr, "Can't read color file\n");
           else
           color_on = 1;
         */
    }

    if (color_on) {
        xdim = chead.xdim;
        ydim = chead.ydim;
        for (i = 0; i < 2; i++) {
            if ((slice[i] = (float *)G_malloc(sizeof(float) * xdim * ydim)) ==
                NULL) {
                fprintf(stderr, "error in allocating memory\n");
                fprintf(stderr, "unable to use colortable\n");
                xdim = ydim = 0;
                color_on = 0;
                break;
            }
        }
    }
    curr = D_spec->Thresh;
    if (color_on) {
        level = get_level(&Headfax, &chead, 0);
        row[1] = get_row(&Headfax, &chead, 0);
        col[1] = get_col(&Headfax, &chead, 0);
        if (level < 0 || row[1] < 0 || col[1] < 0) {
            fprintf(stderr, "Bounds of colorfile do not match data file\n");
            color_on = 0;
        }
        else
            r3read_level(&chead, slice[1], level); /*read in data */
    }
    if (!color_on) {
        get_cat_color(Headfax.linefax.tvalue[curr], D_spec->ctable, color);
        glColor3sv(color);
        glColor3ub(color[0], color[1], color[2]);
    }

    xres = D_spec->xscale;
    yres = D_spec->yscale;
    zres = D_spec->zscale * ZNexag;

    cubefax = Cube.data;

    for (z = 0; z < Headfax.zdim; z++) {
        zadd = z * D_spec->zscale;
        if (color_on) {
            if (0 > (level = get_level(&Headfax, &chead, z + 1))) {
                fprintf(stderr, "LEVEL out of bounds for z = %d\n", z);
                /*
                   color_on = 0;
                   glColor3ub(0xff,0xff,0xff);
                 */
            }
            else {
                tmp = slice[0];
                slice[0] = slice[1];
                slice[1] = tmp;
                r3read_level(&chead, slice[1], level); /*read in data */
            }
        }
        for (y = 0; y < Headfax.ydim; y++) {
            yadd = y * D_spec->yscale;
            if (color_on) {
                if (y)
                    row[0] = row[1];
                else
                    row[0] = get_row(&Headfax, &chead, 0);
                if (0 > (row[1] = get_row(&Headfax, &chead, y + 1))) {
                    row[1] = row[0];
                    fprintf(stderr, " ROW out of bounds for y = %d\n", y);
                    /*
                       color_on = 0;
                       glColor3ub(0xff,0xff,0xff);
                     */
                }
            }
            for (x = 0; x < Headfax.xdim; x++) {
                if (color_on) {
                    if (x)
                        col[0] = col[1];
                    else
                        col[1] = get_col(&Headfax, &chead, 0);
                    if (0 > (col[1] = get_col(&Headfax, &chead, x + 1))) {
                        col[1] = col[0];
                        fprintf(stderr, " COL out of bounds for x = %d\n", x);
                        /*
                           color_on = 0;
                           glColor3ub(0xff,0xff,0xff);
                         */
                    }
                }
                if (!read_cube(&Cube, &Headfax))
                    continue;

                if (!((x > (D_spec->B[X] - 1)) && (x < D_spec->E[X]) &&
                      (y > (D_spec->B[Y] - 1)) && (y < D_spec->E[Y]) &&
                      (z > (D_spec->B[Z] - 1)) && (z < D_spec->E[Z])))
                    continue;

                xadd = x * D_spec->xscale;

                for (t = 0; t < Cube.n_thresh; t++) {
                    if (cubefax[t].t_ndx == D_spec->Thresh) {

                        if (D_spec->in_out == INSIDE) {
                            if (D_spec->Thresh == D_spec->low) {
                                G_sign = -1;
                            }
                            else {
                                G_sign = 1;
                            }
                        }
                        else { /* outside */

                            if (D_spec->Thresh == D_spec->low) {
                                G_sign = 1;
                            }
                            else {
                                G_sign = -1;
                            }
                            /* TODO.  What happens for outside, w/ Thresh == max
                             * thresh? */
                        }
                        if (color_on && cubefax[t].npoly)
                            fill_data_cube(data, slice, row, col,
                                           D_spec->ctable, xdim);

                        for (p = 0; p < cubefax[t].npoly; p++) {
                            /* center data */
                            Polyfax = &(cubefax[t].poly[p]);

                            {
                                glBegin(GL_POLYGON);

                                Polyfax->v1[0] = Polyfax->v1[0] / 255.0;
                                Polyfax->v1[1] = Polyfax->v1[1] / 255.0;
                                Polyfax->v1[2] = Polyfax->v1[2] / 255.0;

                                if (color_on) {
                                    get_vert_color(data, Polyfax->v1,
                                                   D_spec->ctable, color);
                                    glColor3sv(color);
                                }

                                norm[0] = (X_sign * G_sign *
                                           (Polyfax->n1[0] / 127. - 1.)) /
                                          xres;
                                norm[1] = (X_sign * G_sign *
                                           (Polyfax->n1[1] / 127. - 1.)) /
                                          yres;
                                norm[2] = (X_sign * G_sign *
                                           (Polyfax->n1[2] / 127. - 1.)) /
                                          zres;
                                normalize(norm);
                                glNormal3fv(norm);

                                Polyfax->v1[0] =
                                    Polyfax->v1[0] * D_spec->xscale + xadd;
                                Polyfax->v1[1] =
                                    Polyfax->v1[1] * D_spec->yscale + yadd;
                                Polyfax->v1[2] =
                                    Polyfax->v1[2] * D_spec->zscale + zadd;
                                glVertex3fv(Polyfax->v1);
                                Polyfax->v2[0] = Polyfax->v2[0] / 255.0;
                                Polyfax->v2[1] = Polyfax->v2[1] / 255.0;
                                Polyfax->v2[2] = Polyfax->v2[2] / 255.0;

                                if (color_on) {
                                    get_vert_color(D_spec, data, Polyfax->v2,
                                                   D_spec->ctable, color);
                                    glColor3sv(color);
                                }

                                norm[0] = (X_sign * G_sign *
                                           (Polyfax->n2[0] / 127. - 1.)) /
                                          xres;
                                norm[1] = (X_sign * G_sign *
                                           (Polyfax->n2[1] / 127. - 1.)) /
                                          yres;
                                norm[2] = (X_sign * G_sign *
                                           (Polyfax->n2[2] / 127. - 1.)) /
                                          zres;
                                normalize(norm);
                                glNormal3fv(norm);

                                Polyfax->v2[0] =
                                    Polyfax->v2[0] * D_spec->xscale + xadd;
                                Polyfax->v2[1] =
                                    Polyfax->v2[1] * D_spec->yscale + yadd;
                                Polyfax->v2[2] =
                                    Polyfax->v2[2] * D_spec->zscale + zadd;
                                glVertex3fv(Polyfax->v2);

                                Polyfax->v3[1] = Polyfax->v3[1] / 255.0;
                                Polyfax->v3[2] = Polyfax->v3[2] / 255.0;
                                Polyfax->v3[0] = Polyfax->v3[0] / 255.0;

                                if (color_on) {
                                    get_vert_color(D_spec, data, Polyfax->v3,
                                                   D_spec->ctable, color);
                                    glColor3sv(color);
                                }
                                norm[0] = (X_sign * G_sign *
                                           (Polyfax->n3[0] / 127. - 1.)) /
                                          xres;
                                norm[1] = (X_sign * G_sign *
                                           (Polyfax->n3[1] / 127. - 1.)) /
                                          yres;
                                norm[2] = (X_sign * G_sign *
                                           (Polyfax->n3[2] / 127. - 1.)) /
                                          zres;
                                normalize(norm);
                                glNormal3fv(norm);

                                Polyfax->v3[0] =
                                    Polyfax->v3[0] * D_spec->xscale + xadd;
                                Polyfax->v3[1] =
                                    Polyfax->v3[1] * D_spec->yscale + yadd;
                                Polyfax->v3[2] =
                                    Polyfax->v3[2] * D_spec->zscale + zadd;
                                glVertex3fv(Polyfax->v3);

                                glEnd();
                            }
                        }
                        break;
                    }
                }
            }
        }
    }
    if (color_on) {
        G_free(slice[0]);
        G_free(slice[1]);
    }
    glDisable(GL_COLOR_MATERIAL);
}

int get_level(head, chead, z)
file_info *head, *chead;
int z;
{
    int level;

    level = (z * head->tb_res + head->bottom - chead->bottom) / chead->tb_res;
    if (level < 0 || level >= chead->zdim)
        level = -1;
    return (level);
}

int get_row(head, chead, y)
file_info *head, *chead;
int y;
{
    int row;

    row = (y * head->ns_res + head->south - chead->south) / chead->ns_res;
    if (row < 0 || row >= chead->ydim)
        row = -1;
    return (row);
}

int get_col(head, chead, x)
file_info *head, *chead;
int x;
{
    int col;

    col = (x * head->ew_res + head->west - chead->west) / chead->ew_res;
    if (col < 0 || col >= chead->xdim)
        col = -1;
    return (col);
}

void fill_data_cube(data, slice, row, col, ctable, xdim) short data[8][3];
float *slice[2];
int row[2], col[2];
struct color_entry *ctable;
int xdim;
{
    int x, y, z;
    int i = 0;
    float cat, *tmp;

    for (x = 0; x < 2; x++)
        for (y = 0; y < 2; y++)
            for (z = 0; z < 2; z++) {
                tmp = slice[z];
                cat = *(tmp + row[y] * xdim + col[x]);
                get_cat_color(cat, ctable, data[i]);
                i++;
            }
}

void get_vert_color(data, vert, ctable, color) short data[8][3];
float vert[3];
struct color_entry *ctable;
short color[3];
{
    short x[4][3], y[2][3];
    float dx, dy, dz;
    int i, j;

    dx = vert[0];
    dy = vert[1];
    dz = vert[2];

    if (dx == 0.0)
        for (i = 0; i < 4; i++)
            for (j = 0; j < 3; j++)
                x[i][j] = data[i][j];
    else if (dx == 1.0)
        for (i = 0; i < 4; i++)
            for (j = 0; j < 3; j++)
                x[i][j] = data[i + 4][j];
    else
        for (i = 0; i < 4; i++)
            for (j = 0; j < 3; j++)
                x[i][j] = (data[i][j] * (1 - dx) + data[i + 4][j] * dx);

    if (dy == 0.0)
        for (j = 0; j < 3; j++) {
            y[0][j] = x[0][j];
            y[1][j] = x[1][j];
        }
    if (dy == 1.0)
        for (j = 0; j < 3; j++) {
            y[0][j] = x[2][j];
            y[1][j] = x[3][j];
        }
    else
        for (i = 0; i < 2; i++)
            for (j = 0; j < 3; j++) {
                y[i][j] = (x[i][j] * (1 - dy) + x[i + 2][j] * dy);
            }

    if (dz == 0.0)
        for (j = 0; j < 3; j++)
            color[j] = y[0][j];
    else if (dz == 1.0)
        for (j = 0; j < 3; j++)
            color[j] = y[1][j];
    else
        for (j = 0; j < 3; j++)
            color[j] = (y[0][j] * (1 - dz) + y[1][j] * dz);
}

void print_color_table(ctable) struct color_entry ctable[];
{
    int i;

    for (i = 0; ctable[i].color[0] > 0; i++) {
        fprintf(stderr, "%f:%hd:%hd:%hd", ctable[i].data, ctable[i].color[0],
                ctable[i].color[1], ctable[i].color[2]);
    }
}
