#include <SDL2/SDL.h>
#include <vector>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <algorithm>

const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;
const int ROWS = 40;   // latitude divisions
const int COLS = 80;   // longitude divisions
const float SPHERE_RADIUS = 250.0f;

struct Cell {
    bool alive;
    float energy; // 0.0 to 1.0 for fading
};

// 2D grid of cells
std::vector<std::vector<Cell>> grid(ROWS, std::vector<Cell>(COLS));
std::vector<std::vector<Cell>> tempGrid(ROWS, std::vector<Cell>(COLS));

void initGrid() {
    srand(time(0));
    for(int r = 0; r < ROWS; ++r)
        for(int c = 0; c < COLS; ++c)
            grid[r][c] = {rand() % 2, 0.0f};
}

int countNeighbors(int r, int c) {
    int count = 0;
    for(int dr = -1; dr <= 1; ++dr) {
        for(int dc = -1; dc <= 1; ++dc) {
            if(dr == 0 && dc == 0) continue;
            int nr = (r + dr + ROWS) % ROWS;
            int nc = (c + dc + COLS) % COLS;
            if(grid[nr][nc].alive) count++;
        }
    }
    return count;
}

void updateLife() {
    for(int r = 0; r < ROWS; ++r) {
        for(int c = 0; c < COLS; ++c) {
            int neighbors = countNeighbors(r, c);
            bool alive = grid[r][c].alive;
            bool nextAlive = alive ? (neighbors == 2 || neighbors == 3) : (neighbors == 3);
            tempGrid[r][c].alive = nextAlive;

            // Energy for fading effect
            if(nextAlive)
                tempGrid[r][c].energy = std::min(1.0f, grid[r][c].energy + 0.2f);
            else
                tempGrid[r][c].energy = std::max(0.0f, grid[r][c].energy - 0.05f);
        }
    }
    grid.swap(tempGrid);
}

// Convert spherical coordinates to screen coordinates with perspective
void drawSphere(SDL_Renderer* renderer, float rotationAngle) {
    SDL_SetRenderDrawColor(renderer, 0,0,0,255);
    SDL_RenderClear(renderer);

    float cx = SCREEN_WIDTH/2;
    float cy = SCREEN_HEIGHT/2;
    float fov = 400.0f; // field of view for perspective

    for(int r = 0; r < ROWS; ++r) {
        float theta = ((float)r / ROWS - 0.5f) * M_PI; // latitude [-π/2, π/2]
        for(int c = 0; c < COLS; ++c) {
            float phi = ((float)c / COLS) * 2.0f * M_PI + rotationAngle; // longitude

            Cell &cell = grid[r][c];
            if(cell.energy < 0.01f) continue;

            // 3D coordinates
            float x = SPHERE_RADIUS * cos(theta) * cos(phi);
            float y = SPHERE_RADIUS * cos(theta) * sin(phi);
            float z = SPHERE_RADIUS * sin(theta);

            // Perspective projection
            float scale = fov / (fov + z);
            int sx = (int)(cx + x * scale);
            int sy = (int)(cy + y * scale);

            Uint8 brightness = (Uint8)(255 * cell.energy);
            SDL_SetRenderDrawColor(renderer, brightness, 255-brightness, 150, 255);
            SDL_RenderDrawPoint(renderer, sx, sy);
        }
    }

    SDL_RenderPresent(renderer);
}

int main() {
    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window* window = SDL_CreateWindow("Life on a Sphere",
        SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);

    initGrid();

    bool running = true;
    SDL_Event event;
    float rotation = 0.0f;

    while(running) {
        while(SDL_PollEvent(&event)) {
            if(event.type == SDL_QUIT) running = false;
            if(event.type == SDL_KEYDOWN) {
                if(event.key.keysym.sym == SDLK_r) initGrid(); // reset
            }
        }

        rotation += 0.01f; // rotate sphere slowly
        updateLife();
        drawSphere(renderer, rotation);
        SDL_Delay(50); // control speed
    }

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
    return 0;
}
