#ifndef TEXTURE_HPP
#define TEXTURE_HPP
#include <Eigen/Core>
#include <memory>
#include "warping.hpp"
using Color = Eigen::Vector3f;
struct texture{
    virtual Eigen::Vector3f eval(const Eigen::Vector2f& uv)const{
        return Eigen::Vector3f(1,1,1);
    }
};
struct uni_texture : texture{
    Color c;
    uni_texture(const Color& _c) : c(_c){}
    virtual Eigen::Vector3f eval(const Eigen::Vector2f& uv)const override{
        return c;
    }
};
struct checkerboard_texture : texture{
    Color c1;
    Color c2;
    checkerboard_texture(const Color& _c1, const Color& _c2) : c1(_c1), c2(_c2){}
    virtual Eigen::Vector3f eval(const Eigen::Vector2f& uv)const override{
        int i = uv(0) * 100.f;
        int j = uv(1) * 100.f;
        return ((i ^ j) & 1) ? c1 : c2;
    }
};

struct quad_checkerboard_texture : texture{
    Color c1;
    Color c2;
    Color c3;
    Color c4;
    quad_checkerboard_texture(const Color& _c1, const Color& _c2, const Color& _c3, const Color& _c4) : c1(_c1), c2(_c2), c3(_c3), c4(_c4){}
    virtual Eigen::Vector3f eval(const Eigen::Vector2f& uv)const override{
        int i = uv(0) * 100.f;
        int j = uv(1) * 100.f;
        int id = ((i ^ j) & 3);
        switch(id){
            case 0:
            return c1;
            case 1:
            return c2;
            case 2:
            return c3;
            case 3:
            default:
            return c4;
        }
    }
};
struct bsdf{
    virtual Color eval(const Eigen::Vector3f& in, const Eigen::Vector3f& out, const Eigen::Vector3f& normal)const{
        throw 5;
        return Eigen::Vector3f(1,1,1);
    }
};
struct lambertian_bsdf : bsdf{
    virtual Color eval(const Eigen::Vector3f& in, const Eigen::Vector3f& out, const Eigen::Vector3f& normal)const override{
        float x = in.dot(-normal);
        return Eigen::Vector3f(x,x,x);
    }
};
struct microfacet_bsdf : bsdf{
    const float alpha;
    microfacet_bsdf() : alpha(0.1f){}
    microfacet_bsdf(float _alpha) : alpha(_alpha){}
    virtual Color eval(const Eigen::Vector3f& in, const Eigen::Vector3f& out, const Eigen::Vector3f& normal)const override{
        //float x = in.dot(-normal);
        //return Eigen::Vector3f(x,x,x);
        Eigen::Vector3f mirror_ref = in + normal * (in.dot(normal));
        //float x = beckmann_eval(mirror_ref, alpha);
        float x = 1.0f * std::exp(mirror_ref.dot(out) - 1);
        return Color(x,x,x);
    }
};
struct material{
    std::unique_ptr<texture> tex;
    std::unique_ptr<bsdf> m_bsdf;
    material() : material(Color(1,1,1)){

    }
    material(const Color& c, std::unique_ptr<bsdf>&& bsdf) : tex(std::make_unique<uni_texture>(c)), m_bsdf(std::move(bsdf)){
        
    }
    material(const Color& c) : tex(std::make_unique<uni_texture>(c)), m_bsdf(std::make_unique<lambertian_bsdf>()){
        
    }
};
#endif