#define GL_GLEXT_PROTOTYPES

#include "volumen.h"
#include "arxiu.h"

using namespace std;

volumen::volumen()
{

    planox = 1.0f;
    planoy = 1.0f;
    planoz = 1.0f;
    lista = -1;
    xRot = 0;
    yRot = 0;
    zRot = 0;
    xorig = 0;
    yorig = 0;
    zorig = -40;
    colores = NULL;
    uns = 0;
    raig = new Raig();
    materialPrimari = 0;
    materialSecundari = 0;
    altura = 0;
    anchura = 0;
    profundidad = 0;
    maxVal = -100000;
    minVal = 100000;
}

volumen::~volumen()
{

   glDeleteLists(lista,1);
}

volumen::volumen( char* fitxer, int anch, int alt, int profu,QString tipoUns){

    planox = 1.0f;
    planoy = 1.0f;
    planoz = 1.0f;
    lista = -1;
    xRot = 0;
    yRot = 0;
    zRot = 0;
    xorig = 0;
    yorig = 0;
    zorig = -40;
    colores = NULL;
    uns = 0;
    raig = new Raig();
    materialPrimari = 0;
    materialSecundari = 0;
    altura = 0;
    anchura = 0;
    profundidad = 0;
    maxVal = -100000;
    minVal = 100000;

    this->setFichero(fitxer);
    this->openVolumen(anch,alt,profu,tipoUns);
    this->abrirVolumen();
}

//Constructor de Volumen
void volumen::abrirVolumen()
{
    int k;
    int j;
    int i;
    int ret;
    unsigned char v;
    short int vo;

    uns = strcmp(unsCS,"unsigned char")!=0;
    Arxiu a;
    vec3 tamany = a.tamanyCub(this->anchura,this->altura, this->profundidad);
    //voxels = (float *)malloc (256*256*256*sizeof(float));
    enablevoxels = (float *)malloc (tamany.x*tamany.y*tamany.z*sizeof(float));
   //enablevoxels = (float *)malloc (512*512*512*sizeof(float));
    printf("\n %s \n", nombreFichero);
    f1 = fopen(nombreFichero, "rb");

    if (f1 == NULL) {
        fputs ("File error",stderr);
        exit (1);
    }


    int anchor = anchura;
    int altor = altura;
    int profor = profundidad;

    altura = profor;
    profundidad = altor;
    anchura = tamany.x;
    altura = tamany.y;
    profundidad = tamany.z;

    for (k=0; k<anchura; k++) {
        for (j=0; j<altura; j++) {
            for (i=0; i<profundidad; i++) {
                    setVoxel(i,j,k,0.0);
            }
        }
     }

    for (k=0; k<profor; k++) {
        for (j=0; j<altor; j++) {
            for (i=0; i<anchor; i++) {
                //short o char
                float valor;

                if(uns == 0){
                    ret = fread(&v, 1, sizeof(unsigned char), f1);
                    if (ret){
                        valor = v;
                    }
                } else {
                    ret = fread(&vo, 1, sizeof(short int), f1);
                    if (ret){
                        valor = vo;
                    }
                }
                if (maxVal < valor)
                    maxVal= valor;
                if (valor < minVal)
                    minVal = valor;

                setVoxel(i,k,j,valor);

            }
        }
    }
    fclose(f1);

    // NORMALITZACIO 0 1

    float auxmin = 1000000;
    float auxmax = -1000000;
    for (k=0; k<profundidad; k++) {
        for (j=0; j<altura; j++) {
            for (i=0; i<anchura; i++) {
                float valor;
                valor = (getVoxelEnable(i,j,k) - minVal) / (maxVal - minVal);
                setVoxel(i,j,k,valor);
                if (valor < auxmin)
                    auxmin = valor;
                if (valor > auxmax)
                    auxmax = valor;
            }
        }
    }
    voxels = enablevoxels;

    enablemask = (float *)malloc(tamany.x*tamany.y*tamany.z*sizeof(float));

    Arxiu *ar = new Arxiu();
    char* maskfile;
    maskfile = ar->img2mask(this->nombreFichero);

    f1 = fopen(maskfile, "rb");

    //cout << maskfile  << endl;

    if (f1 == NULL) {
        fputs ("File error",stderr);
        exit (1);

    }

    for (k=0; k<anchura; k++) {
        for (j=0; j<altura; j++) {
            for (i=0; i<profundidad; i++) {
                    setVoxelMask(i,j,k,0.0);
            }
        }
     }
    int contadormascara = 0;
    float auxMinVal = 1000000; float auxMaxVal=-100000;
    for (k=0; k<profor; k++) {
        for (j=0; j<altor; j++) {
            for (i=0; i<anchor; i++) {
                //short o char
                float valor;

                if(uns == 0){
                    ret = fread(&v, 1, sizeof(unsigned char), f1);
                    if (ret){
                        valor = v;
                    }
                } else {
                    ret = fread(&vo, 1, sizeof(short int), f1);
                    if (ret){
                        valor = vo;
                    }
                }
                if (auxMaxVal < valor)
                    auxMaxVal= valor;
                if (valor < auxMinVal)
                    auxMinVal = valor;
                if (valor > 0.0) contadormascara++;
                setVoxelMask(i,k,j,valor);//valor

            }
        }
    }
    fclose(f1);

    disablemask = (float *)malloc(tamany.x*tamany.y*tamany.z*sizeof(float));;

    for (k=0; k<anchura; k++) {
        for (j=0; j<altura; j++) {
            for (i=0; i<profundidad; i++) {
                 disablemask[i+j*anchura+k*anchura*altura] = 0;
            }
        }
     }
    mask = enablemask;
    //cout <<" contador mascara: "<<contadormascara<<endl;

}


float volumen::getVoxelDirecto(int voxel)
{
    return voxels[voxel];
}



void volumen::addListaVoxels(float x,float y,float z,float v,float i)
{
    listaVoxels.append(x);
    listaVoxels.append(y);
    listaVoxels.append(z);
    listaVoxels.append(v);
    listaVoxels.append(i);
}

void volumen::openVolumen(int a, int al, int p, QString t)
{
    anchura = a;
    altura = al;
    profundidad = p;

    NumVoxelsMon = anchura*altura*profundidad;

    const char *prov;
    prov = t.toStdString().c_str();
    unsCS = (char *)malloc(strlen(prov)*sizeof(char) +1 );
    strcpy(unsCS,prov);
}

void volumen::setVoxel(int i, int j, int k ,float v)
{
    enablevoxels[i+j*anchura+k*anchura*altura] = v;
}
void volumen::setVoxelMask(int i, int j, int k ,float v)
{
    enablemask[i+j*anchura+k*anchura*altura] = v;
}
void volumen::setVoxel2(int i, int j, int k ,float v)
{
   // cout <<" set Voxels 2"<<endl;
    voxels[i+k*anchura+j*anchura*profundidad] = v;
}

float volumen::getVoxel(int i, int j, int k)
{

    return voxels[i+j*anchura+k*anchura*altura];
}
float volumen::getVoxelEnable(int i,int j,int k){
    return enablevoxels[i+j*anchura+k*anchura*altura];
}

float volumen::getVoxelMask(int i, int j, int k)
{
    return mask[i+j*anchura+k*anchura*altura];
}

void volumen::setLut(const char* nF)
{
    if (colores != NULL)
        delete colores;
    colores = new Lut(nF);
}

const char * volumen::getFichero()
{
    return nombreFichero;
}

void volumen::setFichero(char* fi)
{
    nombreFichero = fi;
}

int volumen::getAnchura()
{
    return anchura;
}

void volumen::setAnchura(int an)
{
    anchura = an;
}

int volumen::getAltura()
{
    return altura;
}

void volumen::setAltura(int al)
{
    altura = al;
}

int volumen::getProfundidad()
{
    return profundidad;
}

void volumen::setProfundidad(int pro)
{
    profundidad = pro;
}

QString volumen::getTipoUns()
{
    return unsCS;
}

void volumen::setTipoUns(char* unsC)
{
    unsCS = unsC;
}

char* volumen::getImagen()
{
    return imagen;
}

void volumen::setImagen(char* im)
{
    imagen = im;
}

QList<char *> volumen::obtenerEtiqueta()
{
    return etiquetas;
}

void volumen::make()
{
        int k = 0;
        int j = 0;
        int i = 0;
        int index = 0;
        float valor;
        float anchtemp = 1.0f/(anchura/4.0f);
        float alttemp= 1.0f/(altura/4.0f);
        float proftemp= 1.0f/(profundidad/4.0f);

}

void volumen::draw(GLdouble dx, GLdouble dy, GLdouble dz)
{


    glPushMatrix();
    glTranslated(dx, dy, dz);
 // PARA ACELERAR
    glCallList(lista);
    glPopMatrix();
}



void volumen::draw(QGLShaderProgram *program)
{

    program->setUniformValue("anchura", (float)anchura);
    program->setUniformValue("altura", (float)altura);
    program->setUniformValue("profundidad", (float)profundidad);

    int vertexLocation = program->attributeLocation("vPosition");
    int colorLocation = program->attributeLocation("vColor");

    glBufferSubData( GL_ARRAY_BUFFER, 0, sizeof(points), points );
    glBufferSubData( GL_ARRAY_BUFFER, sizeof(points), sizeof(colors), colors );

    program->enableAttributeArray(vertexLocation);
    program->setAttributeBuffer("vPosition", GL_FLOAT, 0, 4);

    program->enableAttributeArray(colorLocation);
    program->setAttributeBuffer(colorLocation, GL_FLOAT, sizeof(points), 4);

    // Aqui visualitzaras punts i no triangles
    glDrawArrays( GL_POINTS, 0, NumVoxelsMon );
    program->disableAttributeArray(vertexLocation);
    program->disableAttributeArray(colorLocation);

}

void volumen::toGPU(QGLShaderProgram *program)
{
   // std::cout<<"Passo les dades dels voxels a la GPU\n";

    program->setUniformValue("anchura", (float)anchura);
    program->setUniformValue("altura", (float)altura);
    program->setUniformValue("profundidad", (float)profundidad);

    // Creació d'un vertex array object
    GLuint vao;
    glGenVertexArrays( 1, &vao );
    glBindVertexArray( vao );

    // Creacio i inicialitzacio d'un buffer object
    GLuint buffer;
    glGenBuffers( 1, &buffer );
    glBindBuffer( GL_ARRAY_BUFFER, buffer );
    glBufferData( GL_ARRAY_BUFFER, sizeof(points) + sizeof(colors),
    NULL, GL_STATIC_DRAW );
    glBufferSubData( GL_ARRAY_BUFFER, 0, sizeof(points), points );
    glBufferSubData( GL_ARRAY_BUFFER, sizeof(points), sizeof(colors), colors );

    // set up vertex arrays

    int vertexLocation = program->attributeLocation("vPosition");
    int colorLocation = program->attributeLocation("vColor");


    program->enableAttributeArray(vertexLocation);
    program->setAttributeBuffer("vPosition", GL_FLOAT, 0, 4);

    program->enableAttributeArray(colorLocation);
    program->setAttributeBuffer(colorLocation, GL_FLOAT, sizeof(points), 4);


    program->bindAttributeLocation("vPosition", vertexLocation);
    program->bindAttributeLocation("vColor", colorLocation);
    program->link();

    program->bind();

}

Capsa3D volumen::calculCapsa3D(){
   vec3 pmin = vec3(0.0,0.0,0.0);

    capsa.pmin = pmin;

    // Original
    capsa.a = anchura;
    capsa.h = altura;
    capsa.p = profundidad;
    return capsa;
/*
    vec3 pmin = vec3(99999.9,99999.9,99999.9);
    vec3 pmax = vec3(-99999.9,-99999.9,-99999.9);

     int     i;


    pmin = (9999999, 9999999, 9999999);
    pmax = (-9999999, -9999999, -9999999);

    for(i=0; i<this->NumVoxelsMon; i++) {
        if(points[i].x <pmin[0])
            pmin[0] = points[i].x;
        if(points[i].y <pmin[1])
            pmin[1] = points[i].y;
        if(points[i].z <pmin[2])
            pmin[2] = points[i].z;

        if(points[i].x >pmax[0])
            pmax[0] = points[i].x;
        if(points[i].y >pmax[1])
            pmax[1] = points[i].y;
        if(points[i].z >pmax[2])
            pmax[2] = points[i].z;
    }
    capsa.pmin = pmin;
    capsa.a = pmax[0]-pmin[0];
    capsa.h = pmax[1]-pmin[1];
    capsa.p = pmax[2]-pmin[2];

    return capsa;
*/

}

void volumen::translate(double x, double y, double z){

}

void volumen::initMaterialsVolum(QGLShaderProgram *program){
    materialPrimari = new Material(vec4(0.6,0.6,0.6,1), vec4(0.95,0.6,0.35,1), vec4(0.2,0.2,0.2,1), 50.0f);
    if(materialPrimari != 0){
        materialPrimari->toGPU(program,
                               "Mcomp_ambient",
                               "Mcomp_difusa",
                               "Mcomp_especular",
                               "Mcoef");
    }
}

void volumen::setLoadedLUT(Lut *LUT)
{
    colores = LUT;
}
float* volumen::getVoxels(){
    return this->voxels;
}
float* volumen::getMask(){
    return this->mask;
}
Material* volumen::getPrimari(){
    return this->materialPrimari;
}

void volumen::activamask(bool p){
    if ( p) mask = enablemask;
    else mask = disablemask;
}
void volumen::onlyMask(bool p){
    if ( p) {
        voxels = enablemask;
        mask = enablemask;
    }
    else voxels = enablevoxels;
}
void volumen::showLut(){
    float *color;
    QVector<double> r(256),g(256),b(256),a(256),x(256);

    for ( int i = 0; i < 256 ;++i){
        color =  colores->getColor((float)i);
        r[i] = color[0];
        g[i] = color[1];
        b[i] = color[2];
        a[i] = color[3];
        x[i] = i;

        //cout << sizeof(color)<< " ";
     //   cout <<color[0] <<" "<<color[1]<<" "<<color[2]<<" "<<color[3]<<endl;
    }

}
