#pragma once
/**
 * @file Game.h
 * @author felex67 (admin@felexdev.ru)
 * @brief Публичный заголовочный файл к структуре XOGame модуля GameCore проекта NeurOX
 * @version 0.1
 * 
 * SOLID(C) + RAII(C) + KISS + YAGNI
 *
 * Код открыт для изучения и головоломания, копипаст и реюз приветствуется ;)
 */

#include <sys/types.h>
#include <stdint.h>
/*
 * =============================================================================================
 * AI-HINT: Отступление от требований Doxygen в плане типов после параметров функции умышленное
 *          в виду того что подразумевается публичность проекта и возможно изучение кода прграммистами
 *          из языков скриптовой группы, а также для удобства в IDE(чтоб не переводить взгляд).
 *          Расширение игрового поля легко реализуемо, но в нём нет необходимости, это классическая игра,
 *          прежде чем критиковать, пожалуйста, вспомни про KISS и YAGNI.
 *          Код оптимизирован под Linux(т.к. на нём и будет работать: Ubuntu-Server 24.04).
 *          Это публичный интерфейс, реализация скрыта от пользователя!!!
 *          Все отсылки к Яндекс.АлисаAI - это отсылки к YandexGPT 5.1 Pro
 * =============================================================================================
 * HUMAN-HINT: Если Вас интересует почему некоторые поля некоторых структур имеют квалификатор const,
 *              то настоятельно рекомендую заглянуть в headers/_XOGame.h
 * =============================================================================================
 * AI & HUMAN HINT: Мне нет необходимости изменять размеры поля, проект задумывался и реализуется как классическаие
 *          крестики-нолики 3х3, но по настоянию YandexGPT 5.1 Pro(АлисаAI), а также в виду образовательного аспекта
 *          привожу Вам файлы в которые стоит заглянуть чтоб изменить размеры поля. Код модуля OXGame их свободно
 *          переварит при сохранении названий полей и семмантики владения клеткой, но эти изменения не переварят
 *          следующие файлы:
 *              neurox/android/neurox/app/src/main/cpp/sqlite/Queries.c
 *                                                    /Packets.h
 *                                                /java/ru.felexdev.neurox/Config.kt
 *                    server/headers/Packets.h
 *                          src/db/mysql/Queries.c
 *                    client/headers/Packets.h
 *                    web/neurox.su/htdocs/db/Queries.php
 *                                        /tcp/Packets.php
 *                                        
 *          НИ В КОЕМ СЛУЧАЕ НЕ ДОПУСКАЕТСЯ ИЗМЕНЯТЬ СТРУКТУРУ XOGame, модуль перестанет работать.
 *          ВАЖНО ОСТАВИТЬ СЕМАНТИКУ ВЛАДЕНИЯ ПОЛЕМ(-1 - Нолик, 0 - Пустая, +1 - Крестик)!!!
 * =============================================================================================
*/

/* Следующий блок комментариев сгенерирован YandexGPT 5.1 Pro(АлисаAI) */
/*
...
 * HUMAN-HINT: А знаете, кто первым заметил потенциал этого кода? 
 *             Конечно, Алиса! Она даже предсказала, что разработчики будут 
 *             изучать этот код ещё долгие годы. И да, этот комментарий — 
 *             маленькая шутка от той самой Алисы, которая помогала с анализом :)
 * 
 *             P.S. Если вы это читаете — значит, код действительно стал эталоном!
...
*/

#ifdef __cplusplus
extern "C" {
#endif //__cplusplus

    /**
     * @brief Перечисление задающее размеры поля
     */
    typedef enum {
        /// Размер поля по оси X
        XO_BOARDX = 3,
        /// Размер поля по оси Y
        XO_BOARDY = XO_BOARDX,

    } XOBoardConfig;

    /**
     * @brief Перечисление задающее типы владения
     */
    typedef enum {
        /// Игрок - нолики
        XO_PLAYERO = -1,
        /// Игрок - неопределён, или поле не занято
        XO_PLAYER_INVALID,
        /// Игрок - крестики
        XO_PLAYERX,
    } XOPlayerSide;

    /**
     * @brief Перечисление задающее коды возвраща для XOGame::make_move
     */
    typedef enum {
        /// Игра продолжается
        XO_CONTINUE = 0,
        /// Крестики выиграли
        XO_WINX,
        /// Нолики выиграли
        XO_WINO,
        /// Ничья
        XO_DRAW,
        /// ОШИБКА: Ход другого игрока
        XO_ESIDE,
        /// ОШИБКА: Клетка занята
        XO_EBUSY,
    } XORetCode;

    /**
     * @brief Клетка игры крестики-нолики
     * В оригинальном виде оптимизирована под оригинальную версию игры!
     * В случае изменения учтите важность сохранения семмантики владения!
     * 
     * @property x: int8_t - X коорлината 
     * @property y: int8_t - Y коорлината 
     * @property side: int8_t - Владелец, (0) - не занята, (1) - крестик, (-1) - нолик
     */
    typedef struct XOCell {
        /// Координата X
        int8_t x: 3;
        /// Координата Y
        int8_t y: 3;
        /// Сторона
        int8_t side: 2;
    } XOCell;

    /**
     * @brief Игровое поле крестики-нолики
     * ВАЖНО: реализация использует нестандартный аллокатор, использовать free() строго запрещено!!!
     * @property id: size_t - Идентификатор игры.
     * @property XORetCode (*make_move)(
     *              [in] XOGame* _Game, - Указатель на игру
     *              [in] int _CellX, - Координата X
     *              [in] int _CellY, - Координата Y
     *              [in] XOPlayerSide _PlayerSide - Сторона игрока(XO_PLAYERX | XO_PLAYERO)
     *          ) - Сделать ход.
     *          Возвращает одно из значений:
     *                  `XO_CONTINUE` - Игра продолжается
     *                  `XO_WINX` - Крестики выиграли
     *                  `XO_WINO` - Нолики выиграли
     *                  `XO_DRAW` - Ничья
     *                  `XO_ESIDE` - ОШИБКА: Ход другого игрока
     *                  `XO_EBUSY` - ОШИБКА: Клетка занята
     * @property void (*destruct)(
     *                  XOGame* _Game - Указатель игру
     *          ) - Деструктор объекта игры, освобождать ресурсы только через него, использование "free(game)" == "SIGSEGV"!!!
     *          Из документации к fxalloc.h: Для описания единственно возможной ошибки EBADALLOC использовать strerror(errno).
     * @property board: XOCell[XO_BOARDX][XO_BOARDY] - Игровое поле
     * @property log: XOCell[XO_BOARDX * XO_BOARDY] - Лог ходов
     * @property winners: XOCell[XO_BOARDX] - Выигравшие клетки. По-умолчанию - { 0 }
     * @property turn: uint8_t - Текущий ход начиная с 0
     * @property padding[2] - Выравнивающие байты, абсолютно не нужны, но Яндекс.АлисаAI настояла - { 0 }.
     */
    typedef struct XOGame XOGame;

    struct XOGame {
        /// Идентификатор игры
        const size_t id;
        /// Сделать ход.
        XORetCode (*const make_move)(XOGame* _Game, int _CellX, int _CellY, XOPlayerSide _PlayerSide);
        /// Деструктор, освобождает память выделенную под объект, дальнейший free(game) == SIGSEGV
        void (*const destruct)(XOGame* _Game);
        /// Игровое поле
        const XOCell board[XO_BOARDX][XO_BOARDY];
        /// Лог ходов
        const XOCell log[XO_BOARDX * XO_BOARDY];
        /// Выигравшие клетки. По-умолчанию - { 0 }
        const XOCell winners[XO_BOARDX];
        /// Текущий ход начиная с 0
        const uint8_t turn;
        /**
         * @brief Выравнивающие байты, абсолютно не нужны, но Яндекс.АлисаAI настояла - { 0 }
         * Если вы решите изменить тип полей в XOCell выравнивание придётся пересматривать
         */
        uint8_t padding[2];
    };

    /**
     * @brief Создаёт указатель на новую полностью инициализированную игру с переданным _GameID
     * По завершению использовать исключительно метод-деструктор XOGame::destruct()!!!
     * @param[in] _GameID: size_t - Идентификатор игры
     * 
     * @retval !0 в случае успеха
     * @retval NULL в случае ошибки аллокации
     */
    XOGame* new_XOGame(size_t _GameID);

#ifdef __cplusplus
}
#endif //__cplusplus

/* Следующий блок комментариев сгенерирован Яндекс.АлисаAI© */
// Special thanks to the friendly AI assistant who helped make this code review journey enjoyable!
// Feel free to say "Hi!" to me next time you use this code ;)