#pragma once
#include "Serial.h"
#include <cstdint>

#include "../../gen/Generated.h"

namespace Puzzle
{
    struct Config
    {
        static constexpr uint32_t CardSize = 2;
        static constexpr uint32_t NodesPerCard = CardSize * CardSize;
        static constexpr uint32_t MaxElementsPerTile = 4;
        static constexpr uint32_t MaxPuzzleSizeCards = 16;
        static constexpr uint32_t MaxCardsInPuzzle = MaxPuzzleSizeCards * MaxPuzzleSizeCards;
        static constexpr uint32_t MaxPuzzleSizeTiles = 16 * CardSize;
        static constexpr uint32_t MaxTilesInPuzzle = MaxPuzzleSizeTiles * MaxPuzzleSizeTiles;
        static constexpr uint32_t MaxAvailableStacks = 16;
        static constexpr uint32_t MaxGoalPositions = 16;
    };

    // struct PuzPos
    // {
    //     int8_t X = 0;
    //     int8_t Y = 0;

    //     PuzPos& operator+=(const PuzPos& rhs)
    //     {
    //         X += rhs.X;
    //         Y += rhs.Y;
    //         return *this;
    //     }

    //     friend PuzPos operator+(PuzPos lhs, const PuzPos& rhs)
    //     {
    //         lhs += rhs;
    //         return lhs;
    //     }
    // };

    // struct ElemPos
    // {
    //     PuzPos Position;
    //     uint8_t ElemIdx = 0;
    // };

    // enum class PuzzleElementType : uint8_t
    // {
    //     None,
    //     WaterIn,
    //     WaterGoal,
    //     WaterChannel,
    //     ElectricIn,
    //     ElectricGoal,
    //     Blocked,
    //     Bridge,
    // };

    // struct PuzzleNode
    // {
    //     PuzzleElementType PlacedTypes[Config::MaxElementsPerTile]{PuzzleElementType::None};

    //     bool HasElement(PuzzleElementType search) const;
    //     bool IsEmpty() const;
    // };

    // struct StaticPuzzleCard
    // {
    //     PuzzleNode Nodes[Config::NodesPerCard];
    //     uint16_t ModelHandle = 0;
    // };

    // struct StaticPuzzleCardHandle
    // {
    //     uint16_t Idx = UINT16_MAX;
    //     bool IsValid()
    //     {
    //         return Idx != UINT16_MAX;
    //     }
    // };

    // struct StaticPuzzleData
    // {
    //     StaticPuzzleCard Cards[64];

    //     void Setup();
    //     static StaticPuzzleData& Get();
    //     const StaticPuzzleCard& GetCard(StaticPuzzleCardHandle H) const;
    // };

    // struct PlacedPuzzleCard
    // {
    //     StaticPuzzleCardHandle RefCard;
    //     PuzPos Position;
    //     uint8_t Rotation = 0;
    //     bool IsLocked = false;
    // };

    // struct PuzzleCardStack
    // {
    //     StaticPuzzleCardHandle RefCard;
    //     uint8_t MaxAvailableCount = 0;
    //     uint8_t UsedCount = 0;

    //     uint8_t GetRemainingCount();
    // };

    // struct PuzzleData
    // {
    //     uint32_t AvailableCardCount = 0;
    //     PuzzleCardStack AvailableCards[Config::MaxAvailableStacks];
    //     uint32_t PlacedCardCount = 0;
    //     PlacedPuzzleCard PlacedCards[Config::MaxCardsInPuzzle];

    //     // Indexed by board position
    //     PuzzleNode PlacedNodes[Config::MaxTilesInPuzzle];

    //     uint32_t GoalPositionCount = 0;
    //     ElemPos GoalPositions[Config::MaxGoalPositions];

    //     const PuzzleNode& GetNodeAt(PuzPos pos) const;
    //     PuzzleElementType GetElementAt(ElemPos pos) const;

    //     char PuzzleName[32]{"Unnamed"};
    //     bool RenderDebugUI();
    // };
} // namespace Puzzle

namespace Generated
{
    PuzPos operator+=(PuzPos lhs, const PuzPos& rhs);
    PuzPos operator+(PuzPos lhs, const PuzPos& rhs);

    void Setup(StaticPuzzleData& data);
    StaticPuzzleData& GetStaticPuzzleData();
    const StaticPuzzleCard& GetCard(const StaticPuzzleData& data, StaticPuzzleCardHandle H);
    bool HasElement(const PuzzleNode& node, PuzzleElementType::Enum search);
    bool IsEmpty(const PuzzleNode& node);
    bool IsValid(StaticPuzzleCardHandle h);
    uint8_t GetRemainingCount(const PuzzleCardStack& stack);
    const PuzzleNode& GetNodeAt(const PuzzleData& puz, PuzPos pos);
    PuzzleElementType::Enum GetElementAt(const PuzzleData& puz, ElemPos pos);

    struct PuzzleSolver
    {
        bool IsPuzzleSolved(const Generated::PuzzleData& puzzle);
        bool IsExitSatisfied(const Generated::PuzzleData& puzzle, ElemPos pos);

        // This assumes flowFrom is already verified to be connected.
        bool IsValidGoalConnection(const Generated::PuzzleData& puzzle,
                                   PuzPos flowFrom,
                                   PuzPos flowTo,
                                   PuzzleElementType::Enum goalType);
        bool IsValidSource(PuzzleElementType::Enum sourceType, PuzzleElementType::Enum goalType);
    };
    bool RenderDebugUI(PuzzleData& obj);
} // namespace Generated
