#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>

const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;
const int CELL_SIZE = 10;
const int GRID_WIDTH = SCREEN_WIDTH / CELL_SIZE;
const int GRID_HEIGHT = (SCREEN_HEIGHT - 50) / CELL_SIZE;

class GameOfLife {
private:
    std::vector<std::vector<bool>> grid;
    std::vector<std::vector<bool>> nextGrid;
    bool running;
    bool stepRequested;
    int generation;
    
public:
    GameOfLife() : running(false), stepRequested(false), generation(0) {
        grid.resize(GRID_HEIGHT, std::vector<bool>(GRID_WIDTH, false));
        nextGrid.resize(GRID_HEIGHT, std::vector<bool>(GRID_WIDTH, false));
    }
    
    void randomize() {
        std::srand(std::time(nullptr));
        for (int y = 0; y < GRID_HEIGHT; y++) {
            for (int x = 0; x < GRID_WIDTH; x++) {
                grid[y][x] = (std::rand() % 4) == 0;
            }
        }
        generation = 0;
    }
    
    void clear() {
        for (int y = 0; y < GRID_HEIGHT; y++) {
            for (int x = 0; x < GRID_WIDTH; x++) {
                grid[y][x] = false;
            }
        }
        generation = 0;
    }
    
    void toggleCell(int x, int y) {
        if (x >= 0 && x < GRID_WIDTH && y >= 0 && y < GRID_HEIGHT) {
            grid[y][x] = !grid[y][x];
        }
    }
    
    int countAliveNeighbors(int x, int y) {
        int count = 0;
        for (int dy = -1; dy <= 1; dy++) {
            for (int dx = -1; dx <= 1; dx++) {
                if (dx == 0 && dy == 0) continue;
                
                int nx = (x + dx + GRID_WIDTH) % GRID_WIDTH;
                int ny = (y + dy + GRID_HEIGHT) % GRID_HEIGHT;
                
                if (grid[ny][nx]) {
                    count++;
                }
            }
        }
        return count;
    }
    
    void update() {
        if (!running && !stepRequested) return;
        
        for (int y = 0; y < GRID_HEIGHT; y++) {
            for (int x = 0; x < GRID_WIDTH; x++) {
                int aliveNeighbors = countAliveNeighbors(x, y);
                
                if (grid[y][x]) {
                    nextGrid[y][x] = (aliveNeighbors == 2 || aliveNeighbors == 3);
                } else {
                    nextGrid[y][x] = (aliveNeighbors == 3);
                }
            }
        }
        
        for (int y = 0; y < GRID_HEIGHT; y++) {
            for (int x = 0; x < GRID_WIDTH; x++) {
                grid[y][x] = nextGrid[y][x];
            }
        }
        
        generation++;
        stepRequested = false;
    }
    
    void render(SDL_Renderer* renderer, TTF_Font* font) {
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        SDL_RenderClear(renderer);
        
        SDL_SetRenderDrawColor(renderer, 50, 50, 50, 255);
        for (int x = 0; x <= GRID_WIDTH; x++) {
            SDL_RenderDrawLine(renderer, x * CELL_SIZE, 0, x * CELL_SIZE, GRID_HEIGHT * CELL_SIZE);
        }
        for (int y = 0; y <= GRID_HEIGHT; y++) {
            SDL_RenderDrawLine(renderer, 0, y * CELL_SIZE, GRID_WIDTH * CELL_SIZE, y * CELL_SIZE);
        }
        
        SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
        for (int y = 0; y < GRID_HEIGHT; y++) {
            for (int x = 0; x < GRID_WIDTH; x++) {
                if (grid[y][x]) {
                    SDL_Rect cellRect = {x * CELL_SIZE + 1, y * CELL_SIZE + 1, CELL_SIZE - 2, CELL_SIZE - 2};
                    SDL_RenderFillRect(renderer, &cellRect);
                }
            }
        }
        
        SDL_SetRenderDrawColor(renderer, 30, 30, 30, 255);
        SDL_Rect uiRect = {0, GRID_HEIGHT * CELL_SIZE, SCREEN_WIDTH, 50};
        SDL_RenderFillRect(renderer, &uiRect);
        
        SDL_Color textColor = {255, 255, 255, 255};
        std::string statusText = "Generation: " + std::to_string(generation) + 
                                " | State: " + (running ? "Running" : "Paused") +
                                " | Cells: " + std::to_string(countLivingCells());
        
        SDL_Surface* textSurface = TTF_RenderText_Solid(font, statusText.c_str(), textColor);
        if (textSurface) {
            SDL_Texture* textTexture = SDL_CreateTextureFromSurface(renderer, textSurface);
            SDL_Rect textRect = {10, GRID_HEIGHT * CELL_SIZE + 10, textSurface->w, textSurface->h};
            SDL_RenderCopy(renderer, textTexture, nullptr, &textRect);
            SDL_DestroyTexture(textTexture);
            SDL_FreeSurface(textSurface);
        }
        
        std::string instructions = "Space: Play/Pause | R: Random | C: Clear | Click: Toggle Cell | S: Single Step";
        SDL_Surface* instSurface = TTF_RenderText_Solid(font, instructions.c_str(), textColor);
        if (instSurface) {
            SDL_Texture* instTexture = SDL_CreateTextureFromSurface(renderer, instSurface);
            SDL_Rect instRect = {10, GRID_HEIGHT * CELL_SIZE + 30, instSurface->w, instSurface->h};
            SDL_RenderCopy(renderer, instTexture, nullptr, &instRect);
            SDL_DestroyTexture(instTexture);
            SDL_FreeSurface(instSurface);
        }
    }
    
    int countLivingCells() {
        int count = 0;
        for (int y = 0; y < GRID_HEIGHT; y++) {
            for (int x = 0; x < GRID_WIDTH; x++) {
                if (grid[y][x]) count++;
            }
        }
        return count;
    }
    
    void setRunning(bool run) { running = run; }
    bool isRunning() const { return running; }
    void step() { 
        stepRequested = true;
        update();
    }
};

TTF_Font* loadFont() {
    const char* fontPaths[] = {
        "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf",
        "/usr/share/fonts/truetype/freefont/FreeMono.ttf",
        "/usr/share/fonts/truetype/liberation/LiberationMono-Regular.ttf",
        "/usr/share/fonts/truetype/ubuntu/UbuntuMono-R.ttf",
        nullptr
    };
    
    for (int i = 0; fontPaths[i] != nullptr; i++) {
        TTF_Font* font = TTF_OpenFont(fontPaths[i], 14);
        if (font) return font;
    }
    
    std::cerr << "Failed to load any font!" << std::endl;
    return nullptr;
}

int main(int argc, char* argv[]) {
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl;
        return 1;
    }
    
    if (TTF_Init() == -1) {
        std::cerr << "SDL_ttf could not initialize! TTF_Error: " << TTF_GetError() << std::endl;
        SDL_Quit();
        return 1;
    }
    
    SDL_Window* window = SDL_CreateWindow("Conway's Game of Life",
                                         SDL_WINDOWPOS_UNDEFINED,
                                         SDL_WINDOWPOS_UNDEFINED,
                                         SCREEN_WIDTH,
                                         SCREEN_HEIGHT,
                                         SDL_WINDOW_SHOWN);
    
    if (!window) {
        std::cerr << "Window could not be created! SDL_Error: " << SDL_GetError() << std::endl;
        TTF_Quit();
        SDL_Quit();
        return 1;
    }
    
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    if (!renderer) {
        std::cerr << "Renderer could not be created! SDL_Error: " << SDL_GetError() << std::endl;
        SDL_DestroyWindow(window);
        TTF_Quit();
        SDL_Quit();
        return 1;
    }
    
    TTF_Font* font = loadFont();
    
    GameOfLife game;
    bool quit = false;
    SDL_Event e;
    
    Uint32 lastUpdate = 0;
    const Uint32 UPDATE_INTERVAL = 100;
    
    while (!quit) {
        Uint32 currentTime = SDL_GetTicks();
        
        while (SDL_PollEvent(&e) != 0) {
            if (e.type == SDL_QUIT) {
                quit = true;
            }
            else if (e.type == SDL_KEYDOWN) {
                switch (e.key.keysym.sym) {
                    case SDLK_SPACE:
                        game.setRunning(!game.isRunning());
                        break;
                    case SDLK_r:
                        game.randomize();
                        break;
                    case SDLK_c:
                        game.clear();
                        break;
                    case SDLK_s:
                        game.step();
                        break;
                    case SDLK_ESCAPE:
                        quit = true;
                        break;
                }
            }
            else if (e.type == SDL_MOUSEBUTTONDOWN) {
                if (e.button.button == SDL_BUTTON_LEFT) {
                    int mouseX = e.button.x / CELL_SIZE;
                    int mouseY = e.button.y / CELL_SIZE;
                    game.toggleCell(mouseX, mouseY);
                }
            }
        }
        
        if (currentTime - lastUpdate > UPDATE_INTERVAL) {
            game.update();
            lastUpdate = currentTime;
        }
        
        game.render(renderer, font);
        SDL_RenderPresent(renderer);
        
        SDL_Delay(16);
    }
    
    if (font) TTF_CloseFont(font);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    TTF_Quit();
    SDL_Quit();
    
    return 0;
}
