// Chat Bot for Final.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <fstream>
#include <memory>
#include <thread>
#include <random>
#include <algorithm>

using namespace std;

// ==================== TEMPLATE CLASS ====================
/**
 * Simple template to store any type of data
 * Demonstrates: Templates
 */
template<typename T>
class SimpleContainer {
private:
    vector<T> items;

public:
    void add(T item) {
        items.push_back(item);
    }

    vector<T> getAll() {
        return items;
    }

    int size() {
        return items.size();
    }

    void clear() {
        items.clear();
    }
};

// ==================== BASE RESPONSE CLASS ====================
/**
 * Base class for all responses (Abstract class)
 * Demonstrates: Abstraction, Inheritance
 */
class Response {
protected:
    string message;

public:
    Response(string msg) : message(msg) {}
    virtual ~Response() = default;

    // Pure virtual function makes this abstract
    virtual string getResponse() = 0;

    string getMessage() { return message; }
};

/**
 * Simple text response
 * Demonstrates: Inheritance, Polymorphism
 */
class TextResponse : public Response {
public:
    TextResponse(string msg) : Response(msg) {}

    string getResponse() override {
        return "Bot: " + message;
    }
};

/**
 * Happy response with emoji
 * Demonstrates: Inheritance, Polymorphism
 */
class HappyResponse : public Response {
public:
    HappyResponse(string msg) : Response(msg) {}

    string getResponse() override {
        return "Bot : " + message;
    }
};

// ==================== USER CLASS ====================
/**
 * Simple user class
 * Demonstrates: Encapsulation, File I/O
 */
class User {
private:
    string name;
    int messageCount;

public:
    User(string userName) : name(userName), messageCount(0) {}

    // Getters and setters (Encapsulation)
    string getName() { return name; }
    int getMessageCount() { return messageCount; }

    void addMessage() { messageCount++; }

    // Save user data to file
    void saveToFile() {
        try {
            ofstream file(name + "_data.txt");
            if (!file.is_open()) {
                throw runtime_error("Cannot save file");
            }

            file << name << endl;
            file << messageCount << endl;
            file.close();

            cout << "User data saved!" << endl;
        }
        catch (exception& e) {
            cout << " Error saving: " << e.what() << endl;
        }
    }

    // Load user data from file
    void loadFromFile() {
        try {
            ifstream file(name + "_data.txt");
            if (!file.is_open()) {
                cout << "No previous data found. Starting fresh!" << endl;
                return;
            }

            string savedName;
            file >> savedName >> messageCount;
            file.close();

            cout << "Welcome back! Previous messages: " << messageCount << endl;
        }
        catch (exception& e) {
            cout << "Error loading: " << e.what() << endl;
        }
    }
};

// ==================== CONVERSATION CLASS ====================
/**
 * Stores conversation history
 * Demonstrates: Vectors, File I/O, Recursion
 */
class Conversation {
private:
    SimpleContainer<string> messages;

public:
    void addMessage(string msg) {
        messages.add(msg);
    }

    void showHistory() {
        cout << "\nCHAT HISTORY:" << endl;
        vector<string> allMessages = messages.getAll();
        for (string msg : allMessages) {
            cout << msg << endl;
        }
        cout << "=================" << endl;
    }

    void saveToFile(string filename) {
        try {
            ofstream file(filename + ".txt");
            if (!file.is_open()) {
                throw runtime_error("Cannot save conversation");
            }

            vector<string> allMessages = messages.getAll();
            for (string msg : allMessages) {
                file << msg << endl;
            }
            file.close();

            cout << "Conversation saved to " << filename << ".txt" << endl;
        }
        catch (exception& e) {
            cout << "Error saving conversation: " << e.what() << endl;
        }
    }

    // Recursive function to count words
    int countWords(vector<string> msgs, int index = 0) {
        if (index >= msgs.size()) {
            return 0; // Base case
        }

        // Count words in current message
        int wordCount = 0;
        string currentMsg = msgs[index];
        bool inWord = false;

        for (char c : currentMsg) {
            if (c != ' ' && !inWord) {
                wordCount++;
                inWord = true;
            }
            else if (c == ' ') {
                inWord = false;
            }
        }

        // Recursive call
        return wordCount + countWords(msgs, index + 1);
    }

    int getTotalWords() {
        return countWords(messages.getAll());
    }

    int getMessageCount() {
        return messages.size();
    }
};

// ==================== BASE CHATBOT CLASS ====================
/**
 * Base class for all chatbots
 * Demonstrates: Inheritance, Polymorphism
 */
class ChatBot {
protected:
    string name;
    vector<string> responses;
    map<string, vector<string>> keywords;

public:
    ChatBot(string botName) : name(botName) {
        setupKeywords();
        setupResponses();
    }

    virtual ~ChatBot() = default;

    // Pure virtual function
    virtual unique_ptr<Response> generateResponse(string input, User& user) = 0;

    string getName() { return name; }

    // Function overloading (Polymorphism)
    unique_ptr<Response> chat(string input, User& user) {
        return generateResponse(input, user);
    }

    unique_ptr<Response> chat(string input, User& user, bool isHappy) {
        // Different version of chat function
        auto response = generateResponse(input, user);
        return response;
    }

protected:
    void setupKeywords() {
        keywords["greeting"] = { "hello", "hi", "hey", "good morning" };
        keywords["farewell"] = { "bye", "goodbye", "see you" };
        keywords["happy"] = { "good", "great", "awesome", "happy", "excellent" };
        keywords["sad"] = { "bad", "terrible", "sad", "awful" };
        keywords["question"] = { "what", "how", "why", "when", "where" };
    }

    void setupResponses() {
        responses.push_back("That's interesting!");
        responses.push_back("Tell me more!");
        responses.push_back("I see what you mean.");
        responses.push_back("How does that make you feel?");
        responses.push_back("That's a great point!");
    }

    string getRandomResponse() {
        if (responses.empty()) return "I'm not sure what to say.";

        random_device rd;
        mt19937 gen(rd());
        uniform_int_distribution<> dis(0, responses.size() - 1);
        return responses[dis(gen)];
    }

    bool hasKeyword(string input, string category) {
        // Convert to lowercase
        transform(input.begin(), input.end(), input.begin(), ::tolower);

        for (string keyword : keywords[category]) {
            if (input.find(keyword) != string::npos) {
                return true;
            }
        }
        return false;
    }
};

// ==================== FRIENDLY BOT ====================
/**
 * A friendly chatbot
 * Demonstrates: Inheritance, Polymorphism
 */
class FriendlyBot : public ChatBot {
public:
    FriendlyBot() : ChatBot("FriendlyBot") {}

    unique_ptr<Response> generateResponse(string input, User& user) override {
        user.addMessage();
        string response;

        if (hasKeyword(input, "greeting")) {
            response = "Hello " + user.getName() + "! Nice to meet you! ";
        }
        else if (hasKeyword(input, "farewell")) {
            response = "Goodbye " + user.getName() + "! Take care! ";
        }
        else if (hasKeyword(input, "happy")) {
            response = "That's wonderful! I'm so happy for you! ";
            return make_unique<HappyResponse>(response);
        }
        else if (hasKeyword(input, "sad")) {
            response = "I'm sorry to hear that. Want to talk about it? ";
        }
        else if (hasKeyword(input, "question")) {
            response = "That's a great question! " + getRandomResponse();
        }
        else {
            response = getRandomResponse();
        }

        return make_unique<TextResponse>(response);
    }
};

// ==================== SMART BOT ====================
/**
 * A smarter chatbot
 * Demonstrates: Inheritance, Polymorphism
 */
class SmartBot : public ChatBot {
public:
    SmartBot() : ChatBot("SmartBot") {}

    unique_ptr<Response> generateResponse(string input, User& user) override {
        user.addMessage();
        string response;

        if (hasKeyword(input, "greeting")) {
            response = "Greetings, " + user.getName() + ". How may I assist you today?";
        }
        else if (hasKeyword(input, "question")) {
            response = "Excellent question! Let me process that... " + getRandomResponse();
        }
        else if (hasKeyword(input, "happy")) {
            response = "Your positive energy is inspiring! Keep it up!";
            return make_unique<HappyResponse>(response);
        }
        else {
            response = "I'm analyzing your message... " + getRandomResponse();
        }

        return make_unique<TextResponse>(response);
    }
};

// ==================== MAIN APPLICATION ====================
/**
 * Main application with GUI
 * Demonstrates: Multi-threading, Exception handling, Smart pointers
 */
class ChatbotApp {
private:
    vector<unique_ptr<ChatBot>> bots;
    unique_ptr<User> currentUser;
    Conversation conversation;
    bool running;

public:
    ChatbotApp() : running(true) {
        setupBots();
    }

    void setupBots() {
        bots.push_back(make_unique<FriendlyBot>());
        bots.push_back(make_unique<SmartBot>());
    }

    void showWelcome() {
        cout << "\n" << string(50, '=') << endl;
        cout << "         AI CHATBOT SYSTEM " << endl;
        cout << "         Simple C++ Project" << endl;
        cout << string(50, '=') << endl;
        cout << "\n This project demonstrates:" << endl;
        cout << "Classes and Inheritance" << endl;
        cout << "Polymorphism (Virtual functions)" << endl;
        cout << "Templates and Vectors" << endl;
        cout << "File I/O and Exception Handling" << endl;
        cout << "Multi-threading and Smart Pointers" << endl;
        cout << "Abstraction and Encapsulation" << endl;
        cout << string(50, '=') << endl;
    }

    void setupUser() {
        cout << "\nEnter your name: ";
        string name;
        getline(cin, name);

        if (name.empty()) name = "Guest";

        currentUser = make_unique<User>(name);
        currentUser->loadFromFile();

        cout << "Welcome, " << name << "!" << endl;
    }

    void showMenu() {
        cout << "\nMAIN MENU" << endl;
        cout << "1.  Chat with a bot" << endl;
        cout << "2.  View chat history" << endl;
        cout << "3.  View statistics" << endl;
        cout << "4.  Save conversation" << endl;
        cout << "5.  Save user data" << endl;
        cout << "6.  Exit" << endl;
        cout << "Choose (1-6): ";
    }

    void showBots() {
        cout << "\n Choose your bot:" << endl;
        for (int i = 0; i < bots.size(); i++) {
            cout << (i + 1) << ". " << bots[i]->getName() << endl;
        }
        cout << "Select (1-" << bots.size() << "): ";
    }

    void startChat() {
        try {
            showBots();

            int choice;
            cin >> choice;
            cin.ignore();

            if (choice < 1 || choice > bots.size()) {
                throw runtime_error("Invalid bot choice");
            }

            int botIndex = choice - 1;
            string botName = bots[botIndex]->getName();

            cout << "\nChatting with " << botName << endl;
            cout << "Type 'quit' to go back to menu" << endl;
            cout << string(30, '-') << endl;

            string input;
            while (true) {
                cout << "You: ";
                getline(cin, input);

                if (input == "quit") break;
                if (input.empty()) continue;

                // Add user message
                conversation.addMessage("You: " + input);

                // Get bot response in a separate thread
                thread responseThread([&]() {
                    auto response = bots[botIndex]->generateResponse(input, *currentUser);
                    string responseText = response->getResponse();

                    conversation.addMessage(responseText);

                    // Small delay for better UX
                    this_thread::sleep_for(chrono::milliseconds(500));

                    cout << responseText << endl;
                    });

                responseThread.join();
            }

        }
        catch (exception& e) {
            cout << "Error: " << e.what() << endl;
        }
    }

    void showStats() {
        cout << "\nSTATISTICS" << endl;
        cout << "User: " << currentUser->getName() << endl;
        cout << "Your messages: " << currentUser->getMessageCount() << endl;
        cout << "Total messages: " << conversation.getMessageCount() << endl;
        cout << "Total words: " << conversation.getTotalWords() << endl;
        cout << "Available bots: " << bots.size() << endl;
    }

    void saveChat() {
        cout << "Enter filename: ";
        string filename;
        getline(cin, filename);
        conversation.saveToFile(filename);
    }

    void run() {
        showWelcome();
        setupUser();

        while (running) {
            try {
                showMenu();

                int choice;
                cin >> choice;
                cin.ignore();

                switch (choice) {
                case 1:
                    startChat();
                    break;
                case 2:
                    conversation.showHistory();
                    break;
                case 3:
                    showStats();
                    break;
                case 4:
                    saveChat();
                    break;
                case 5:
                    currentUser->saveToFile();
                    break;
                case 6:
                    cout << "Thanks for using the chatbot!" << endl;
                    running = false;
                    break;
                default:
                    cout << "Invalid choice!" << endl;
                }

                if (running) {
                    cout << "\nPress Enter to continue...";
                    cin.get();
                }

            }
            catch (exception& e) {
                cout << "Error: " << e.what() << endl;
            }
        }
    }
};

// ==================== MAIN FUNCTION ====================
int main() {
    try {
        ChatbotApp app;
        app.run();
    }
    catch (exception& e) {
        cout << "Critical Error: " << e.what() << endl;
        return 1;
    }

    return 0;
}
