/*
 *  <Short Description>
 *  Copyright (C) 2024  Brett Terpstra
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#ifndef GP_IMAGE_TEST_IMAGE_H
#define GP_IMAGE_TEST_IMAGE_H

#define NO_BLT_NAMESPACE_ON_TYPES

#include <blt/std/types.h>
#include <array>
#include <optional>
#include "blt/std/assert.h"

inline constexpr i32 width = 1024, height = 1024;

class image
{
    private:
        std::array<blt::vec4, width * height> data;
    public:
        image() = default;
        
        const blt::vec4& get(i32 x, i32 y)
        {
            return data[y * height + x];
        }
        
        void set(const blt::vec4& c, i32 x, i32 y)
        {
            data[y * height + x] = c;
        }
};

enum class function_t
{
    // FUNC     // inputs
    ADD,        // 2
    SUB,        // 2
    MUL,        // 2
    DIV,        // 2
    EXP,        // 2
    
    LOG,        // 1
    SQRT,       // 1
    QUAD,       // 1
    
    RANDOM,     // 0
    NOISE,      // 0
    COLOR,      // 0
    SCALAR      // 0
};

inline i32 inputs(function_t op)
{
    // don't like this but it'll get compiled out
    // and we get warnings when new enum is added
    switch (op)
    {
        case function_t::ADD:
        case function_t::SUB:
        case function_t::MUL:
        case function_t::DIV:
        case function_t::EXP:
            return 2;
        case function_t::LOG:
        case function_t::SQRT:
        case function_t::QUAD:
            return 1;
        case function_t::RANDOM:
        case function_t::NOISE:
        case function_t::COLOR:
        case function_t::SCALAR:
            return 0;
    }
    BLT_THROW(std::runtime_error("If you are seeing this the universe has broken. Enjoy whatever hellhole is left"));
}

struct op_con
{
    const function_t op;
    const std::optional<image*> i1, i2;
    
    op_con(function_t op, const std::optional<image*>& i1, const std::optional<image*>& i2): op(op), i1(i1), i2(i2)
    {}
    
    [[nodiscard]] bool has_both() const noexcept
    {
        return i1.has_value() && i2.has_value();
    }
    
    [[nodiscard]] bool has_one() const noexcept
    {
        return i1.has_value() || i2.has_value();
    }
    
    [[nodiscard]] image* getOne() const
    {
        if (i1)
            return i1.value();
        if (i2)
            return i2.value();
        BLT_THROW(std::runtime_error("Unable to get when one is missing! You have an error in your GA!"));
    }
};

#endif //GP_IMAGE_TEST_IMAGE_H
