#include <GL/glut.h>
#include <iostream>
#include <cmath>

const int TEX_SIZE = 128;
GLubyte chessboard[TEX_SIZE][TEX_SIZE][3]; // Textura en memoria
GLuint textureID; // Identificador de textura

float angleX = 0.0f, angleY = 0.0f; // Ángulos de rotación
float velocityX = 0.0f, velocityY = 0.0f; // Velocidad de rotación
float friction = 0.98f; // Factor de frenado para la inercia

int lastX, lastY; // Última posición del mouse
bool isDragging = false; // Indica si el mouse está presionado

void generateChessboardTexture() {
    for (int i = 0; i < TEX_SIZE; i++) {
        for (int j = 0; j < TEX_SIZE; j++) {
            int color = ((i / 16 + j / 16) % 2) * 255; // Cuadrados más pequeños
            chessboard[i][j][0] = color;
            chessboard[i][j][1] = color;
            chessboard[i][j][2] = color;
        }
    }
    
    glGenTextures(1, &textureID);
    glBindTexture(GL_TEXTURE_2D, textureID);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, TEX_SIZE, TEX_SIZE, 
								0, GL_RGB, GL_UNSIGNED_BYTE, chessboard);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}

void setupLighting() {
    GLfloat light_pos[] = {1.0f, 1.0f, 1.0f, 0.0f}; // Luz direccional
    GLfloat ambient_light[] = {0.3f, 0.3f, 0.3f, 1.0f};
    GLfloat diffuse_light[] = {0.8f, 0.8f, 0.8f, 1.0f};
    GLfloat specular_light[] = {1.0f, 1.0f, 1.0f, 1.0f};

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    
    glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_light);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_light);
    glLightfv(GL_LIGHT0, GL_SPECULAR, specular_light);

    glEnable(GL_COLOR_MATERIAL);
    glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);

    GLfloat mat_specular[] = {1.0f, 1.0f, 1.0f, 1.0f};
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialf(GL_FRONT, GL_SHININESS, 50.0f);
}

void display() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    glTranslatef(0.0f, 0.0f, -5.0f);

    glRotatef(angleX, 1, 0, 0);
    glRotatef(angleY, 0, 1, 0);

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, textureID);

    GLUquadric* quad = gluNewQuadric();
    gluQuadricTexture(quad, GL_TRUE);
    gluSphere(quad, 1.0, 64, 64);

    gluDeleteQuadric(quad);
    glDisable(GL_TEXTURE_2D);

    glutSwapBuffers();
}

// Función de inercia, llamada cada frame
void idle() {
    if (!isDragging) {
        velocityX *= friction;
        velocityY *= friction;
        angleX += velocityX;
        angleY += velocityY;

        if (fabs(velocityX) < 0.01f) velocityX = 0.0f;
        if (fabs(velocityY) < 0.01f) velocityY = 0.0f;
    }
    glutPostRedisplay();
}

// Movimiento del mouse con botón presionado
void mouseMotion(int x, int y) {
    if (isDragging) {
        velocityY = (x - lastX) * 0.5f;
        velocityX = (y - lastY) * 0.5f;
        angleY += velocityY;
        angleX += velocityX;
        lastX = x;
        lastY = y;
    }
}

// Detección del botón del mouse
void mouse(int button, int state, int x, int y) {
    if (button == GLUT_LEFT_BUTTON) {
        if (state == GLUT_DOWN) {
            isDragging = true;
            lastX = x;
            lastY = y;
            velocityX = 0.0f;
            velocityY = 0.0f;
        } else {
            isDragging = false;
        }
    }
}

void reshape(int w, int h) {
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, (float)w / (float)h, 1.0, 100.0);
    glMatrixMode(GL_MODELVIEW);
}

void init() {
    glEnable(GL_DEPTH_TEST);
    generateChessboardTexture();
    setupLighting();
}

int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(800, 600);
    glutCreateWindow("Esfera con textura de ajedrez, inercia y luz");

    init();
    
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutMouseFunc(mouse);
    glutMotionFunc(mouseMotion);
    glutIdleFunc(idle); // Ejecuta la inercia cuando el programa está inactivo
    
    glutMainLoop();
    return 0;
}

