#include "classes.h"

#include "functions.h"
extern pangolin::GlSlProgram GetShaderProgram();

SurfaceRendererState::SurfaceRendererState(int total_num_faces,
                                           int num_viewpoints,
                                           float view_distance) {
    face_normal_test = std::vector<Eigen::Vector4f>(total_num_faces);
    for (size_t j = 0; j < total_num_faces; j++) {
        face_normal_test[j] = Eigen::Vector4f(0.0f, 0.0f, 0.0f, 0.0f);
    }
    face_observation_count = std::vector<int>(total_num_faces);
    for (size_t j = 0; j < total_num_faces; j++) {
        face_observation_count[j] = 0;
    }
    invisible_face_test = std::vector<bool>(total_num_faces);
    for (size_t j = 0; j < total_num_faces; j++) {
        invisible_face_test[j] = true;
    }
    observed_backface_test = std::vector<bool>(total_num_faces);
    for (size_t j = 0; j < total_num_faces; j++) {
        observed_backface_test[j] = false;
    }
    views = sample_points_on_unit_sphere(num_viewpoints, view_distance);
}
SurfaceRenderer::SurfaceRenderer(pangolin::Geometry& geometry, int width,
                                 int height, float camera_distance) {
    _width = width;
    _height = height;
    _geometry = pangolin::ToGlGeometry(geometry);
    _camera = pangolin::OpenGlRenderState(
        pangolin::ProjectionMatrixOrthographic(-camera_distance,
                                               camera_distance, camera_distance,
                                               -camera_distance, 0, 2.5),
        pangolin::ModelViewLookAt(0, 0, -1, 0, 0, 0, pangolin::AxisY));
}

void SurfaceRenderer::render(SurfaceRendererState& state,
                             pangolin::GlSlProgram& program,
                             pangolin::GlTexture& texture_normals,
                             pangolin::GlTexture& texture_vertices,
                             pangolin::GlFramebuffer& frame_buffer) {
    for (unsigned int view_index = 0; view_index < state.views.size();
         view_index++) {
        auto view = state.views[view_index];
        pangolin::TypedImage image_normals;
        pangolin::TypedImage image_vertices;
        this->render_from_view(
            view_index, view, program, texture_normals, texture_vertices,
            frame_buffer, image_normals, image_vertices, state.observed_normals,
            state.observed_points, state.observed_points_view_index,
            state.face_normal_test, state.invisible_face_test,
            state.observed_backface_test, state.face_observation_count);
    }
}
void SurfaceRenderer::render_from_view(
    int view_index, Eigen::Vector3f& view, pangolin::GlSlProgram& program,
    pangolin::GlTexture& texture_normals, pangolin::GlTexture& texture_vertices,
    pangolin::GlFramebuffer& frame_buffer, pangolin::TypedImage& image_normals,
    pangolin::TypedImage& image_vertices,
    std::vector<Eigen::Vector4f>& observed_normals,
    std::vector<Eigen::Vector4f>& observed_points,
    std::vector<int>& observed_points_view_index,
    std::vector<Eigen::Vector4f>& face_normal_test,
    std::vector<bool>& invisible_face_test,
    std::vector<bool>& observed_backface_test,
    std::vector<int>& face_observation_count) {
    _camera.SetModelViewMatrix(pangolin::ModelViewLookAt(
        view[0], view[1], view[2], 0, 0, 0, pangolin::AxisY));
    frame_buffer.Bind();
    glViewport(0, 0, _width, _height);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    program.Bind();
    program.SetUniform("MVP", _camera.GetProjectionModelViewMatrix());
    program.SetUniform("V", _camera.GetModelViewMatrix());
    pangolin::GlDraw(program, _geometry, nullptr);
    program.Unbind();
    frame_buffer.Unbind();

    texture_normals.Download(image_normals);
    texture_vertices.Download(image_vertices);

    find_invisible_faces(image_normals.UnsafeReinterpret<Eigen::Vector4f>(),
                         invisible_face_test);
    find_observed_backfaces(image_normals.UnsafeReinterpret<Eigen::Vector4f>(),
                            face_normal_test, observed_backface_test,
                            face_observation_count);

    get_valid_surface_points_and_normals_from_image(
        view_index, image_normals.UnsafeReinterpret<Eigen::Vector4f>(),
        image_vertices.UnsafeReinterpret<Eigen::Vector4f>(), observed_normals,
        observed_points, observed_points_view_index);
}