#include <iostream>
#include <vector>
#include <conio.h>      // For _getch()
#include <algorithm>    // For std::find_if
#include <cstdlib>      // For system(), rand()
#include <ctime>        // For time()
#include <cmath>        // For abs(), sin(), cos(), M_PI
#include <string>

// --------------------
// Data Structures (Common)
// --------------------
struct Entity {
    int x, y;
    int health;
    char symbol;
};

struct Pickup {
    int x, y;
    char symbol;
    int value; // e.g., health restore
};

struct Weapon {
    std::string name;
    int range;     // effective shooting range (grid cells)
    int ammo;      // current ammo in clip
    int clipSize;  // max ammo in a clip
};

struct DestructibleObstacle {
    int x, y;
    int health;
    char symbol;
};

int score = 0;

// --------------------
// Battle Encounter System (for Campaigns 1-3)
// --------------------
void displayHealthBar(const std::string &name, int health, int maxHealth) {
    int barWidth = 20;
    int pos = (health * barWidth) / maxHealth;
    std::cout << name << " Health: [";
    for (int i = 0; i < barWidth; i++) {
        std::cout << (i < pos ? "#" : "-");
    }
    std::cout << "] " << health << "/" << maxHealth << std::endl;
}

bool battleEncounter(const std::string &battleName, int &playerHealth, int maxPlayerHealth, int enemyHealth) {
    std::cout << "\n=== Battle: " << battleName << " ===\n";
    while (playerHealth > 0 && enemyHealth > 0) {
        displayHealthBar("Player", playerHealth, maxPlayerHealth);
        std::cout << "Enemy Health: " << enemyHealth << std::endl;
        std::cout << "Press 's' to shoot: ";
        char action = _getch();
        if (action == 's' || action == 'S') {
            // Rifle now does 10-60 damage.
            int damage = 10 + rand() % 51;
            std::cout << "\nYou shoot the enemy and deal " << damage << " damage!\n";
            enemyHealth -= damage;
            if (enemyHealth <= 0) {
                std::cout << "Enemy defeated!\n";
                break;
            }
        } else {
            std::cout << "\nInvalid input, you lose your chance to shoot!\n";
        }
        int enemyDamage = 10 + rand() % 6;  // 10-15 damage
        std::cout << "Enemy shoots back and deals " << enemyDamage << " damage!\n";
        playerHealth -= enemyDamage;
        if (playerHealth <= 0) {
            std::cout << "You have been defeated in battle: " << battleName << "\n";
            return false;
        }
    }
    return true;
}

bool battleBunker(Entity &bunker, int &playerHealth) {
    int bunkerMaxHealth = bunker.health;
    std::cout << "\nInitiating Bunker Assault at (" << bunker.x << "," << bunker.y << "). Bunker Health: " << bunker.health << "\n";
    while (bunker.health > 0 && playerHealth > 0) {
        displayHealthBar("Player", playerHealth, 100);
        displayHealthBar("Bunker", bunker.health, bunkerMaxHealth);
        std::cout << "Press 's' to shoot the bunker: ";
        char action = _getch();
        if (action == 's' || action == 'S') {
            // Rifle damage now 10-60.
            int damage = 10 + rand() % 51;
            std::cout << "\nYou hit the bunker for " << damage << " damage!\n";
            bunker.health -= damage;
            if (bunker.health <= 0) {
                std::cout << "Bunker destroyed and captured!\n";
                return true;
            }
        } else {
            std::cout << "\nInvalid input, you missed your chance!\n";
        }
        int turretDamage = 5 + rand() % 6;  // 5-10 damage
        std::cout << "Bunker turret fires and deals " << turretDamage << " damage to you!\n";
        playerHealth -= turretDamage;
        if (playerHealth <= 0) {
            std::cout << "You have been killed by the bunker defenses!\n";
            return false;
        }
    }
    return true;
}

// --------------------
// Map-Based Gameplay for Campaigns 1-4 (Standard & German D-Day)
// --------------------
void render(const std::vector<std::vector<char>> &baseMap, 
            const Entity &player, 
            const std::vector<Entity> &enemies,
            const std::vector<Pickup> &pickups,
            const std::vector<Entity> &bunkers,
            const Weapon &weapon) {
    system("cls");
    std::vector<std::vector<char>> displayMap = baseMap;
    for (const auto &p : pickups)
        displayMap[p.y][p.x] = p.symbol;
    for (const auto &b : bunkers)
        displayMap[b.y][b.x] = b.symbol;
    for (const auto &e : enemies)
        displayMap[e.y][e.x] = e.symbol;
    displayMap[player.y][player.x] = player.symbol;
    for (const auto &row : displayMap) {
        for (char c : row)
            std::cout << c << ' ';
        std::cout << std::endl;
    }
    std::cout << "Player Health: " << player.health;
    if (player.health < 30)
        std::cout << "  [WARNING: LOW HEALTH]";
    std::cout << "\nScore: " << score;
    std::cout << "\nWeapon: " << weapon.name << " | Ammo: " << weapon.ammo << "/" << weapon.clipSize << std::endl;
    std::cout << "\nControls:\n  Movement: w/a/s/d and diagonals (q, e, z, c)\n  Shooting: i/j/k/l for up/left/down/right\n  Grenade: g   Reload: r" << std::endl;
}

void addObstacles(std::vector<std::vector<char>> &baseMap, int count) {
    int height = baseMap.size(), width = baseMap[0].size();
    int placed = 0;
    while (placed < count) {
        int x = rand() % width, y = rand() % height;
        if (baseMap[y][x] == '.') {
            baseMap[y][x] = '#';
            placed++;
        }
    }
}

// --------------------
// Campaign 5 Rendering (Tank, Obstacles, Enemies)
// --------------------
void renderCampaign5(const int MAP_WIDTH, const int MAP_HEIGHT, 
                     const std::vector<std::vector<char>> &grid) {
    system("cls");
    for (const auto &row : grid) {
        for (char c : row)
            std::cout << c << ' ';
        std::cout << std::endl;
    }
}

// --------------------
// Campaign 5: German Aden Offensive (Panther Assault)
// --------------------
struct Tank {
    float x, y;
    float angle;  // degrees, 0 = right.
    float speed;
    int health;
};

float degToRad(float deg) {
    return deg * M_PI / 180.0f;
}

float angleDiff(float a, float b) {
    float diff = fabs(a - b);
    return diff > 180 ? 360 - diff : diff;
}

void addObstaclesDestructible(std::vector<std::vector<char>> &grid, std::vector<DestructibleObstacle> &obs, int count) {
    int height = grid.size(), width = grid[0].size();
    int placed = 0;
    while (placed < count) {
        int x = rand() % width, y = rand() % height;
        if (grid[y][x] == '.') {
            grid[y][x] = '#';
            // Give each obstacle 100 health.
            obs.push_back({x, y, 100, '#'});
            placed++;
        }
    }
}

void renderCampaign5Grid(const int MAP_WIDTH, const int MAP_HEIGHT,
                         const Tank &playerTank, const std::vector<DestructibleObstacle> &obs,
                         const std::vector<Entity> &enemies, const std::vector<Entity> &crew) {
    // Create a grid.
    std::vector<std::vector<char>> grid(MAP_HEIGHT, std::vector<char>(MAP_WIDTH, '.'));
    // Place obstacles.
    for (const auto &o : obs)
        grid[o.y][o.x] = o.symbol;
    // Place enemies.
    for (const auto &e : enemies)
        grid[e.y][e.x] = e.symbol;
    // Place crew. We'll represent them with 'C'.
    for (const auto &c : crew)
        grid[c.y][c.x] = c.symbol;
    // Place the tank (represented as 'P').
    int tx = (int)round(playerTank.x), ty = (int)round(playerTank.y);
    grid[ty][tx] = 'P';
    renderCampaign5(MAP_WIDTH, MAP_HEIGHT, grid);
}

void playCampaign5() {
    const int MAP_WIDTH = 20, MAP_HEIGHT = 10;
    // Create a grid for campaign 5.
    std::vector<std::vector<char>> grid(MAP_HEIGHT, std::vector<char>(MAP_WIDTH, '.'));
    // Create destructible obstacles.
    std::vector<DestructibleObstacle> obstacles;
    addObstaclesDestructible(grid, obstacles, 5); // Place 5 obstacles.
    
    Tank playerTank = { MAP_WIDTH/2.0f, MAP_HEIGHT - 2.0f, 90.0f, 0.0f, 150 }; // Facing upward.
    // Weapons: main cannon and machine gun.
    int cannonAmmo = 4;      // main cannon ammo.
    int machineGunAmmo = 20; // machine gun ammo.
    // Crew members.
    std::vector<Entity> crew = { { (int)round(playerTank.x) - 1, (int)round(playerTank.y), 100, 'C' },
                                 { (int)round(playerTank.x) + 1, (int)round(playerTank.y), 100, 'C' } };
    
    int totalWaves = 8;
    for (int wave = 1; wave <= totalWaves; wave++) {
        std::cout << "\n--- Wave " << wave << " ---\n";
        // Spawn enemy infantry ('I') and enemy tanks ('T').
        std::vector<Entity> enemies;
        int numInfantry = 3 + rand() % 3;  // 3-5 infantry.
        int numEnemyTanks = 1 + rand() % 2;  // 1-2 tanks.
        for (int i = 0; i < numInfantry; i++) {
            enemies.push_back({ rand() % MAP_WIDTH, 0, 40, 'I' });
        }
        for (int i = 0; i < numEnemyTanks; i++) {
            enemies.push_back({ rand() % MAP_WIDTH, 0, 80, 'T' });
        }
        
        // Wave simulation loop.
        while (!enemies.empty()) {
            // Render current grid.
            // We update the grid with obstacles (they are stored in obstacles vector),
            // then place crew, then the tank, then enemies.
            // For simplicity, we create a fresh grid each turn.
            std::vector<std::vector<char>> currentGrid(MAP_HEIGHT, std::vector<char>(MAP_WIDTH, '.'));
            // Place obstacles.
            for (const auto &o : obstacles)
                currentGrid[o.y][o.x] = o.symbol;
            // Place enemies.
            for (const auto &e : enemies)
                currentGrid[e.y][e.x] = e.symbol;
            // Place crew.
            for (const auto &c : crew)
                currentGrid[c.y][c.x] = c.symbol;
            // Place the tank.
            int tx = (int)round(playerTank.x), ty = (int)round(playerTank.y);
            currentGrid[ty][tx] = 'P';
            renderCampaign5(MAP_WIDTH, MAP_HEIGHT, currentGrid);
            
            std::cout << "German Aden Offensive (Panther Assault) - Wave " << wave << " / " << totalWaves << "\n";
            std::cout << "Tank Health: " << playerTank.health << "\n";
            std::cout << "Crew: ";
            for (size_t i = 0; i < crew.size(); i++)
                std::cout << "Crew" << i+1 << "(" << crew[i].health << ") ";
            std::cout << "\nCannon Ammo: " << cannonAmmo << "   Machine Gun Ammo: " << machineGunAmmo << "\n";
            std::cout << "Enemies remaining: " << enemies.size() << "\n";
            std::cout << "Enter command: ";
            char cmd = _getch();
            
            // Driving controls.
            if (cmd == 'w') {
                playerTank.speed += 0.5f;
                if (playerTank.speed > 3.0f) playerTank.speed = 3.0f;
            } else if (cmd == 's') {
                playerTank.speed -= 0.5f;
                if (playerTank.speed < 0) playerTank.speed = 0;
            } else if (cmd == 'a') {
                playerTank.angle += 10.0f;
                if (playerTank.angle >= 360.0f) playerTank.angle -= 360.0f;
            } else if (cmd == 'd') {
                playerTank.angle -= 10.0f;
                if (playerTank.angle < 0) playerTank.angle += 360.0f;
            }
            // Weapon firing.
            else if (cmd == ' ') {
                if (cannonAmmo <= 0) {
                    std::cout << "\nOut of cannon ammo! Press r to reload.\n";
                } else {
                    cannonAmmo--;
                    std::cout << "\nFiring main cannon!\n";
                    float rad = degToRad(playerTank.angle);
                    int shotRange = 8;
                    int hitIndex = -1;
                    // Trace bullet path.
                    int bx = (int)round(playerTank.x), by = (int)round(playerTank.y);
                    for (int r = 0; r < shotRange; r++) {
                        bx += (int)round(cos(rad));
                        by -= (int)round(sin(rad));
                        // First, check if an obstacle is hit.
                        bool obstacleHit = false;
                        for (size_t i = 0; i < obstacles.size(); i++) {
                            if (obstacles[i].x == bx && obstacles[i].y == by) {
                                // Only main cannon can damage obstacles.
                                int damage = 200; // Base damage.
                                // Compute relative angle.
                                float dx = obstacles[i].x - playerTank.x;
                                float dy = playerTank.y - obstacles[i].y;
                                float targetAngle = atan2(dy, dx) * 180.0f / M_PI;
                                if (targetAngle < 0) targetAngle += 360;
                                float diff = angleDiff(playerTank.angle, targetAngle);
                                float multiplier = (diff < 45 ? 0.5f : (diff > 135 ? 1.5f : 1.0f));
                                int finalDamage = (int)(damage * multiplier);
                                obstacles[i].health -= finalDamage;
                                std::cout << "Main cannon hit an obstacle at (" << obstacles[i].x << "," << obstacles[i].y << ") for " << finalDamage << " damage.\n";
                                if (obstacles[i].health <= 0) {
                                    std::cout << "Obstacle destroyed!\n";
                                    obstacles.erase(obstacles.begin() + i);
                                    // Also update grid.
                                }
                                obstacleHit = true;
                                break;
                            }
                        }
                        if (obstacleHit) break;
                        // Check for an enemy.
                        for (size_t i = 0; i < enemies.size(); i++) {
                            if (enemies[i].x == bx && enemies[i].y == by) {
                                hitIndex = i;
                                break;
                            }
                        }
                        if (hitIndex != -1) break;
                    }
                    if (hitIndex != -1) {
                        int baseDamage = 200; // Main cannon base damage.
                        float dx = enemies[hitIndex].x - playerTank.x;
                        float dy = playerTank.y - enemies[hitIndex].y;
                        float targetAngle = atan2(dy, dx) * 180.0f / M_PI;
                        if (targetAngle < 0) targetAngle += 360;
                        float diff = angleDiff(playerTank.angle, targetAngle);
                        float multiplier = (diff < 45 ? 0.5f : (diff > 135 ? 1.5f : 1.0f));
                        int damage = (int)(baseDamage * multiplier);
                        enemies[hitIndex].health -= damage;
                        std::cout << "Main cannon hit enemy at (" << enemies[hitIndex].x << "," << enemies[hitIndex].y << ") for " << damage << " damage.\n";
                        if (enemies[hitIndex].health <= 0) {
                            std::cout << "Enemy destroyed!\n";
                            enemies.erase(enemies.begin() + hitIndex);
                            score += 150;
                        }
                    } else {
                        std::cout << "Main cannon missed.\n";
                    }
                }
            }
            else if (cmd == 'm') {
                if (machineGunAmmo <= 0) {
                    std::cout << "\nOut of machine gun ammo! Press r to reload.\n";
                } else {
                    machineGunAmmo--;
                    std::cout << "\nFiring machine gun!\n";
                    float rad = degToRad(playerTank.angle);
                    int shotRange = 6;
                    int hitIndex = -1;
                    int bx = (int)round(playerTank.x), by = (int)round(playerTank.y);
                    for (int r = 0; r < shotRange; r++) {
                        bx += (int)round(cos(rad));
                        by -= (int)round(sin(rad));
                        // Check if an obstacle is hit.
                        bool obstacleHit = false;
                        for (size_t i = 0; i < obstacles.size(); i++) {
                            if (obstacles[i].x == bx && obstacles[i].y == by) {
                                int damage = 30 + rand() % 11; // 30-40 damage.
                                obstacles[i].health -= damage;
                                std::cout << "Machine gun hit an obstacle at (" << obstacles[i].x << "," << obstacles[i].y << ") for " << damage << " damage.\n";
                                if (obstacles[i].health <= 0) {
                                    std::cout << "Obstacle destroyed!\n";
                                    obstacles.erase(obstacles.begin() + i);
                                }
                                obstacleHit = true;
                                break;
                            }
                        }
                        if (obstacleHit) break;
                        for (size_t i = 0; i < enemies.size(); i++) {
                            if (enemies[i].x == bx && enemies[i].y == by) { hitIndex = i; break; }
                        }
                        if (hitIndex != -1) break;
                    }
                    if (hitIndex != -1) {
                        int damage = 30 + rand() % 11;  // 30-40 damage.
                        enemies[hitIndex].health -= damage;
                        std::cout << "Machine gun hit enemy at (" << enemies[hitIndex].x << "," << enemies[hitIndex].y << ") for " << damage << " damage.\n";
                        if (enemies[hitIndex].health <= 0) {
                            std::cout << "Enemy destroyed!\n";
                            enemies.erase(enemies.begin() + hitIndex);
                            score += 100;
                        }
                    } else {
                        std::cout << "Machine gun missed.\n";
                    }
                }
            }
            else if (cmd == 'r') {
                cannonAmmo = 4;
                machineGunAmmo = 20;
                std::cout << "\nReloading all weapons...\n";
            }
            
            // Update tank physics.
            float rad = degToRad(playerTank.angle);
            playerTank.x += playerTank.speed * cos(rad);
            playerTank.y -= playerTank.speed * sin(rad);
            if (playerTank.x < 0) playerTank.x = 0;
            if (playerTank.x >= MAP_WIDTH) playerTank.x = MAP_WIDTH - 1;
            if (playerTank.y < 0) playerTank.y = 0;
            if (playerTank.y >= MAP_HEIGHT) playerTank.y = MAP_HEIGHT - 1;
            
            // Move enemies toward the tank.
            for (auto &enemy : enemies) {
                if (enemy.x < (int)round(playerTank.x)) enemy.x++;
                else if (enemy.x > (int)round(playerTank.x)) enemy.x--;
                if (enemy.y < (int)round(playerTank.y)) enemy.y++;
                else if (enemy.y > (int)round(playerTank.y)) enemy.y--;
                // Enemy fires with 30% chance.
                if (rand() % 100 < 30) {
                    int baseDmg = 15 + rand() % 6; // 15-20 damage.
                    float dx = enemy.x - playerTank.x;
                    float dy = playerTank.y - enemy.y;
                    float enemyAngle = atan2(dy, dx) * 180.0f / M_PI;
                    if (enemyAngle < 0) enemyAngle += 360;
                    float diff = angleDiff(playerTank.angle, enemyAngle);
                    float multiplier = (diff < 45 ? 0.5f : (diff > 135 ? 1.5f : 1.0f));
                    int dmg = (int)(baseDmg * multiplier);
                    playerTank.health -= dmg;
                    std::cout << "An enemy fires at your tank for " << dmg << " damage.\n";
                }
            }
            
            std::cout << "\nPress any key for next turn in this wave...";
            _getch();
            if (playerTank.health <= 0) break;
        }
        if (playerTank.health <= 0) break;
        std::cout << "\nWave " << wave << " cleared! Press any key for next wave...";
        _getch();
    }
    if (playerTank.health > 0)
        std::cout << "\nAll enemy defenses destroyed! German Aden Offensive mission accomplished.\n";
    else
        std::cout << "\nYour Panther tank and crew were destroyed. Mission failed.\n";
}

// --------------------
// Main
// --------------------
int main() {
    srand(static_cast<unsigned int>(time(0)));
    std::cout << "Battleground 1 - Enhanced Edition\n";
    std::cout << "Controls (Campaigns 1-4):\n  Movement: w/a/s/d and diagonals (q, e, z, c)\n  Shooting: i/j/k/l for up/left/down/right\n  Grenade: g   Reload: r\n";
    std::cout << "\nSelect Campaign:\n1. D-Day\n2. Aden Offensive\n3. Berlin Attack\n4. German D-Day (Machine Gun Defense)\n5. German Aden Offensive (Panther Assault - Drive the Tank)\nEnter choice (1-5): ";
    int choice;
    std::cin >> choice;
    if (choice >= 1 && choice <= 5) {
        if (choice == 5) {
            playCampaign5();
        } else {
            // Campaigns 1-4 use playCampaign.
            playCampaign(choice);
        }
    } else {
        std::cout << "Invalid choice." << std::endl;
    }
    std::cout << "\nPress any key to exit.";
    _getch();
    return 0;
}