#include <iostream>
#include <cstdlib>
#include <conio.h>
#include <thread>
#include <chrono>
#include <windows.h>
#include <limits>
#include <vector>
#include <algorithm>

class Symbol {
public:
    Symbol(char character = ' ', int color = 10);

    char getCharacter() const;
    void setCharacter(char character);

    int getColor() const;
    void setColor(int color);

private:
    char character;
    int color;
};

Symbol::Symbol(char character, int color) : character(character), color(color) {}

char Symbol::getCharacter() const {
    return character;
}

void Symbol::setCharacter(char character) {
    this->character = character;
}

int Symbol::getColor() const {
    return color;
}

void Symbol::setColor(int color) {
    this->color = color;
}

class Line {
public:
    Line(int length, bool epilepsyMode);

    void move();
    void draw(HANDLE hConsole, int position, int consoleWidth, int startPosition) const;

private:
    int length;
    std::vector<Symbol> symbols;
    bool epilepsyMode;
    int lineColor;

    void generateNewSymbol(int index);
    static char getRandomCharacter();
    static int getRandomColor();
    static void setColor(int color);
    char getCharacter(int index) const;
    int getColor(int index) const;
};

Line::Line(int length, bool epilepsyMode) : length(length), epilepsyMode(epilepsyMode) {
    if (epilepsyMode) {
        lineColor = getRandomColor();
    } else {
        lineColor = 10; // Зеленый по умолчанию
    }

    for (int i = 0; i < length; ++i) {
        char randomChar = getRandomCharacter();
        int color = epilepsyMode ? lineColor : 10;
        symbols.push_back(Symbol(randomChar, color));
    }
}

void Line::move() {
    symbols[0] = Symbol(' ', 7); // Удаление первого символа
    for (int i = 1; i < length - 1; ++i) {
        symbols[i] = symbols[i + 1];
    }
    generateNewSymbol(length - 1);
}

void Line::draw(HANDLE hConsole, int position, int consoleWidth, int startPosition) const {
    COORD cursorPos;

    if (position - 1 >= 0 && position - 1 < consoleWidth) {
        cursorPos.X = static_cast<SHORT>(position - 1);
        cursorPos.Y = startPosition;
        SetConsoleCursorPosition(hConsole, cursorPos);
        std::cout << " ";
    }

    int startIndex = std::max(0, -position);
    int visibleLength = std::min(length - startIndex, consoleWidth - position);

    for (int i = startIndex; i < startIndex + visibleLength; ++i) {
        setColor(getColor(i));
        cursorPos.X = static_cast<SHORT>(position + i);
        cursorPos.Y = startPosition;
        SetConsoleCursorPosition(hConsole, cursorPos);
        std::cout << getCharacter(i);
    }
}

void Line::generateNewSymbol(int index) {
    char newChar = getRandomCharacter();
    int color = epilepsyMode ? lineColor : 10;
    symbols[index] = Symbol(newChar, color);
}

char Line::getRandomCharacter() {
    const char characters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+[]{}|;':\",.<>?~";
    return characters[rand() % (sizeof(characters) - 1)];
}

int Line::getRandomColor() {
    return 1 + rand() % 15;
}

void Line::setColor(int color) {
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
}

char Line::getCharacter(int index) const {
    if (index >= 0 && index < symbols.size()) {
        return symbols[index].getCharacter();
    }
    return ' ';
}

int Line::getColor(int index) const {
    if (index >= 0 && index < symbols.size()) {
        return symbols[index].getColor();
    }
    return 7;
}

class AppManager {
public:
    AppManager();
    void start(int argc, char* argv[]);

private:
    int speed;
    int length;
    bool epilepsyMode;
    bool running;
    int consoleWidth;
    int consoleHeight;

    void configureSettings();
    bool parseCommandLineArgs(char* argv[]);
    void hideConsoleCursor();
    void getConsoleSize();
};

AppManager::AppManager() : speed(1), length(10), epilepsyMode(false), running(true) {
    srand(static_cast<unsigned int>(time(nullptr)));
    getConsoleSize();
}

void AppManager::start(int argc, char* argv[]) {
    if (argc == 4) {
        if (!parseCommandLineArgs(argv)) {
            return;
        }
    } else {
        configureSettings();
    }

    hideConsoleCursor();

    system("cls");
    std::cout << "Matrix Simulation is starting..." << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));

    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);

    while (running) {
        int startPosition = rand() % (consoleHeight - 1);
        Line line(length, epilepsyMode);
        int position = -length;

        while (position < consoleWidth) {
            line.draw(hConsole, position, consoleWidth, startPosition);
            std::this_thread::sleep_for(std::chrono::milliseconds(1000 / speed));
            line.move();
            ++position;

            if (_kbhit() && _getch() == 'q') {
                running = false;
                break;
            }
        }

        if (epilepsyMode) {
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
        }
    }
}

void AppManager::configureSettings() {
    std::cout << "Welcome to the Matrix Simulation!" << std::endl;
    std::cout << "Press 'q' anytime to quit the simulation." << std::endl;

    std::cout << "Enter speed (1 - 30): ";
    while (!(std::cin >> speed) || speed < 1 || speed > 30) {
        std::cin.clear();
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        std::cout << "Invalid input. Please enter a number between 1 and 30: ";
    }

    std::cout << "Enter line length (1 - 30): ";
    while (!(std::cin >> length) || length < 1 || length > 30) {
        std::cin.clear();
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        std::cout << "Invalid input. Please enter a number between 1 and 30: ";
    }

    char input;
    std::cout << "Epilepsy mode (Y/N)? ";
    while (!(std::cin >> input) || (input != 'Y' && input != 'y' && input != 'N' && input != 'n')) {
        std::cin.clear();
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        std::cout << "Invalid input. Please enter 'Y' or 'N': ";
    }
    epilepsyMode = (input == 'Y' || input == 'y');
}

bool AppManager::parseCommandLineArgs(char* argv[]) {
    try {
        speed = std::stoi(argv[1]);
        length = std::stoi(argv[2]);
        char epilepsyChar = argv[3][0];

        if (speed < 1 || speed > 30 || length < 1 || length > 30 ||
            (epilepsyChar != 'Y' && epilepsyChar != 'y' && epilepsyChar != 'N' && epilepsyChar != 'n')) {
            std::cerr << "Invalid command line arguments." << std::endl;
            std::cerr << "Please, enter speed and length values (1-30). Epilepsy mode Y/N" << std::endl;
            return false;
        }

        epilepsyMode = (epilepsyChar == 'Y' || epilepsyChar == 'y');
        return true;
    } catch (...) {
        std::cerr << "Invalid command line arguments." << std::endl;
        std::cerr << "Please, enter speed and length values (1-30). Epilepsy mode Y/N" << std::endl;
        return false;
    }
помоги мне написать программу на C++ с применением методов ООП
вот мое тз:
написать программу "матрица"

пользователь вводит 4 параметра все они могут принимать значения от 1 до 30
частоту генераций линий, скорость линий, длину линий и флаг режима эпилепсии
необходимо предусмотреть ввод в консольном режиме (в cls matrix.exe 3 23 8 Y) и в диалоговом режиме при запуске программы, если через cls не были переданы все 4 корректных параметра
обеспечить защиту от дурака
программа должна делать симуляции матрицы

каждая отдельная линия должна выползать и случайной позиции по вертикали
движение линии должно осуществляться появлением нового символа справа и затиранием символа слева
по умолчанию цвет линий зеленый, при включенном режиме эпилепсии у каждой отдельной линии свой цвет
перед началом симуляции программа должна получать размеры консоли
для отрисовки линий используй метод implace_back
• Общее количество появившихся в секунду линий должно соответствовать заданной частоте появления.
• Если частота n линий в секунду, то генерируется n случайных временных промежутков в диапазоне от 0 до 1000 миллисекунд (одна секунда). Это определяет, в какой момент времени каждая линия начнёт своё движение.
• Например, если сгенерированы промежутки 200 мс, 700 мс и 400 мс, линии начнут двигаться в следующем порядке: первая линия через 200 мс от начала итерации, вторая через 400 мс от начала и третья - через 700 мс.
• Линии, как и в первой лабораторной, должны «выползать» из-за начальной границы экрана и двигаться до конца экрана.}



void AppManager::hideConsoleCursor() {
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_CURSOR_INFO cursorInfo;
    GetConsoleCursorInfo(hConsole, &cursorInfo);
    cursorInfo.bVisible = FALSE;
    SetConsoleCursorInfo(hConsole, &cursorInfo);
}

void AppManager::getConsoleSize() {
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
    consoleWidth = csbi.srWindow.Right - csbi.srWindow.Left + 1;
    consoleHeight = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
}

int main(int argc, char* argv[]) {
    AppManager appManager;
    appManager.start(argc, argv);

    return 0;
}