#include "game.h"

#define DELAY 30000

Game::Game()
    :max_x(1000), max_y(800), direction(0), window(sf::VideoMode(max_x, max_y), "ASCII RACER"),
      racecar(400.0f, 30.0f, 1.7f) { // maxSpeed, acceleration, and steerSpeed values
    if (!font.loadFromFile("cascaydia.otf")) {
        std::cerr << "Failed to load font" << std::endl;
        exit(1);
    }
    
    // Create a text object for displaying speed
    speedText.setFont(font);

    // Create a map text object and set its properties
    mapText.setFont(font);
    mapText.setCharacterSize(24);
    mapText.setFillColor(sf::Color::White);
    mapText.setPosition(0, 0);

    timerText.setFont(font);
    timerText.setCharacterSize(24);
    timerText.setFillColor(sf::Color::White);

    racecar.setPosition(1000, 80); // Set initial position for the racecar
    racecar.setRotation(180);

    // Initialize the times and quadrants data structure
    timesAndQuadrants.push_back(std::make_pair(1, "00:00:00.000"));
}

void Game::run() {
    sf::Clock clock;

    // Create a view that will follow the racecar
    view = sf::View(sf::FloatRect(0, 0, max_x, max_y));
    view.setCenter(racecar.getX(), racecar.getY()); // Center the view on the racecar
    window.setView(view);
    
    while (window.isOpen()) {
        float dt = clock.restart().asSeconds();

        // Update game time
        sf::Time gameTime = gameTimeClock.getElapsedTime();
        int milis = static_cast<int>(gameTime.asMilliseconds());
        int seconds = static_cast<int>(gameTime.asSeconds());
        int minutes = seconds / 60;
        seconds %= 60;
        milis %= 1000;
        
        // Use std::setw and std::setfill to format numbers as two digits
        std::ostringstream timerStream;
        timerStream << std::setw(2) << std::setfill('0') << minutes << ":"
                    << std::setw(2) << std::setfill('0') << seconds << ":"
                    << std::setw(2) << std::setfill('0') << milis;
        
        std::string timerString = timerStream.str();
        timerText.setString(timerString);

        // Detect when the racecar changes quadrants and save the time
        int currentQuadrant = determineQuadrant(racecar.getX(), racecar.getY());
        if (currentQuadrant != timesAndQuadrants.back().first) {
            timesAndQuadrants.push_back(std::make_pair(currentQuadrant, timerString));
        }
        
        determineLap(timerString);

        sf::Event event;
        while (window.pollEvent(event)) {
            if (event.type == sf::Event::Closed) {
                window.close();
            }
        }

        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) {
            if (racecar.getSpeed() < 0) {
                racecar.brake(dt);
            } else {
                float newX = racecar.getX() + ((racecar.getSpeed() + 1.1) * dt * cos(racecar.getRotation()));
                float newY = racecar.getY() + ((racecar.getSpeed() + 1.1) * dt * sin(racecar.getRotation()));
                //std::cout << "New: " << newX << "," << newY << "   Old: " << racecar.getX() << "," << racecar.getY() << std::endl;

                if (!isTrackCollision(newX, newY)) {
                    racecar.accelerate(dt);
                } else {
                    racecar.stop();
                }
            }
        } else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) {
            if (racecar.getSpeed() > 0) {
                racecar.brake(dt);
            } else {
                float newX = racecar.getX() + ((racecar.getSpeed() + 1.1) * dt * cos(racecar.getRotation()));
                float newY = racecar.getY() + ((racecar.getSpeed() + 1.1) * dt * sin(racecar.getRotation()));
                //std::cout << "New: " << newX << "," << newY << "   Old: " << racecar.getX() << "," << racecar.getY() << std::endl;
                
                if (!isTrackCollision(newX, newY)) {
                    racecar.reverse(dt);
                } else {
                    racecar.stop();
                }
            }
        } else {
            racecar.decelerate(dt);
        }

        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) {
            racecar.steer(dt, -1.0f); // Steer left
        } else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) {
            racecar.steer(dt, 1.0f); // Steer right
        }

        racecar.update(dt);

        speedText.setString(std::to_string(static_cast<int>(racecar.getSpeed())) + " km/h");
        speedText.setCharacterSize(24);
        speedText.setFillColor(sf::Color::Green);
        speedText.setPosition(racecar.getX() - 475, racecar.getY() + 350);
        if(racecar.getSpeed() < 0){
            speedText.setFillColor(sf::Color::Blue);
        }
        if(racecar.getSpeed() >= 250){
            speedText.setCharacterSize(27);
            speedText.setFillColor(sf::Color::Yellow);
        }
        if(racecar.getSpeed() >= 360){
            speedText.setCharacterSize(30);
            speedText.setFillColor(sf::Color::Red);
        }
        timerText.setPosition(racecar.getX() - 80, racecar.getY() - 350);

        // Update the view to follow the racecar
        view.setCenter(racecar.getX(), racecar.getY());
        window.setView(view);

        window.clear();
        mapText.setString(asciiMap);
        window.draw(mapText);
        window.draw(racecar.getDrawable());
        window.draw(speedText);
        window.draw(timerText);
        displaySavedTimes();
        window.display();

        sf::sleep(sf::microseconds(DELAY));
    }
}

bool Game::isTrackCollision(float x, float y) {
    if (x <= 0) {
    //    return true;
    } else if (y <= 0) {
      //  return true;
    } else {
       // int currX = (x / 13.85) - 1;
       // int currY = (y / 27.88) - 1;
       // char currentChar = asciiMap[currY * 194 + currX];
       // //std::cout << x << ", " << y << std::endl;
       // if (currentChar == '\\' || currentChar == '|' || currentChar == '/' || currentChar == '-' || currentChar == '_') {
       //     //return true;
       // }
    }
            
    return false; // No collision with track or out of bounds
}

void Game::determineLap(std::string timerString) {
    lapFinished = true;
    int requiredOrder[] = {1,2,1,2,3,4,1};
    if(timesAndQuadrants.size() >= 7) {
        for (int i = 0; i < 7; i++) {
            if (std::get<0>(timesAndQuadrants[i]) != requiredOrder[i])
                lapFinished = false; 
        }
    }
    else {
        lapFinished = false;
    }
       
    if (lapFinished) {
        lapTimes.push_back(timerString);
        timesAndQuadrants.clear();
        timesAndQuadrants.push_back(std::make_pair(1, timerString));
    }
}

int Game::determineQuadrant(float x, float y) {
    if (y <= 460 && x <= 1150)
        return 1;
    if (y > 460 && x <= 1150)
        return 2;
    if (y > 460 && x > 1150)
        return 3;
    if (y <= 460 && x > 1150)
        return 4;

    return 0;
}

void Game::displaySavedTimes() {
    // Display the saved times on the left side of the screen
    int textY = 10; // Adjust the vertical position as needed
    for (const auto& lapTime : lapTimes) {
        std::string timeString = "Lap Time: " + lapTime;
        sf::Text text;
        text.setFont(font);
        text.setCharacterSize(18);
        text.setFillColor(sf::Color::White);
        text.setPosition(racecar.getX() - 475, (racecar.getY() - 300) + textY);
        text.setString(timeString);
        window.draw(text);
        textY += 30; // Adjust the spacing as needed
    }
    for (const auto& timeAndQuadrant : timesAndQuadrants) {
        std::string timeString = "Quadrant " + std::to_string(timeAndQuadrant.first) + ": " + timeAndQuadrant.second;
        sf::Text text;
        text.setFont(font);
        text.setCharacterSize(18);
        text.setFillColor(sf::Color::White);
        text.setPosition(racecar.getX() - 475, (racecar.getY() - 300) + textY);
        text.setString(timeString);
        window.draw(text);
        textY += 30; // Adjust the spacing as needed
    }
}
