#include <mpi.h>
#include <adios2.h>
#include <thread>
#include <fides/DataSetReader.h>

#include <vtkm/cont/Algorithm.h>
#include <vtkm/worklet/DispatcherMapTopology.h>
#include <vtkm/worklet/WorkletMapTopology.h>
#include <vtkm/worklet/ScatterPermutation.h>
#include <vtkm/rendering/Camera.h>
#include <vtkm/rendering/Scene.h>
#include <vtkm/rendering/MapperRayTracer.h>
#include <vtkm/rendering/CanvasRayTracer.h>
#include <vtkm/rendering/View3D.h>
#include <vtkm/filter/contour/Contour.h>
#include <vtkm/cont/ColorTable.h>


using FieldInfoType = fides::metadata::Vector<fides::metadata::FieldInformation>;

int main(int argc, char** argv) {
    MPI_Init(&argc, &argv);

    adios2::ADIOS adios(MPI_COMM_WORLD);
    const std::string source_name = "the-source";

    fides::io::DataSetReader fides_reader("fides-gray-scott.json");
    std::unordered_map<std::string, std::string> paths;
    paths[source_name] = std::string("gs.bp");
    fides::DataSourceParams params;
    params["engine_type"] = "SST";
    fides_reader.SetDataSourceParameters(source_name, std::move(params));

    size_t step = 0;
    while(true) {
        auto status = fides_reader.PrepareNextStep(paths);
        fides::metadata::MetaData metaData = fides_reader.ReadMetaData(paths);
        if (status == fides::StepStatus::EndOfStream)
        {
          break;
        }

        fides::metadata::MetaData selections;
        fides::metadata::Vector<size_t> blockSelection;
        blockSelection.Data.push_back(0);
        selections.Set(fides::keys::BLOCK_SELECTION(), blockSelection);

        FieldInfoType fieldSelection;
        fieldSelection.Data.push_back(fides::metadata::FieldInformation("U", vtkm::cont::Field::Association::Points));
        selections.Set(fides::keys::FIELDS(), fieldSelection);


        vtkm::cont::PartitionedDataSet output = fides_reader.ReadDataSet(paths, selections);
        auto inputData = output.GetPartition(0);

        auto pointFieldU = inputData.GetPointField("U");

        vtkm::Float64 isovalue = 0.240858;

        // Create an isosurface filter
        vtkm::filter::contour::Contour filter;
        filter.SetIsoValue(isovalue);
        filter.SetActiveField("U");
        vtkm::cont::DataSet outputData = filter.Execute(inputData);

        vtkm::Bounds coordsBounds = inputData.GetCoordinateSystem().GetBounds();

        vtkm::Vec<vtkm::Float64,3> totalExtent(coordsBounds.X.Length(),
                                               coordsBounds.Y.Length(),
                                               coordsBounds.Z.Length() );
        vtkm::Float64 mag = vtkm::Magnitude(totalExtent);
        vtkm::Normalize(totalExtent);
        vtkm::rendering::Camera camera;
        camera.ResetToBounds(coordsBounds);

        camera.SetLookAt(totalExtent*(mag * .5f));
        camera.SetViewUp(vtkm::make_Vec(0.f, 0.f, 1.f));
        camera.SetClippingRange(1.f, 100.f);
        camera.SetFieldOfView(60.f);
        auto camera_position = totalExtent*(mag * 1.0f);
        camera_position[0] = 4;
        camera_position[1] = 4;
        camera_position[2] = 3.5;
        camera.SetPosition(camera_position);
        vtkm::cont::ColorTable colorTable("inferno");

        // Create a mapper, canvas and view that will be used to render the scene
        vtkm::rendering::Scene scene;
        vtkm::rendering::MapperRayTracer mapper;
        vtkm::rendering::CanvasRayTracer canvas(256, 256);
        vtkm::rendering::Color bg(0.2f, 0.2f, 0.2f, 1.0f);

        // Render an image of the output isosurface
        scene.AddActor(vtkm::rendering::Actor(outputData.GetCellSet(),
                                              outputData.GetCoordinateSystem(),
                                              outputData.GetField("U"),
                                              colorTable));
        vtkm::rendering::View3D view(scene, mapper, canvas, camera, bg);
        //view.Initialize();
        view.Paint();
        std::string filename = "gray_scott_step_" + std::to_string(step++) + ".png";
        view.SaveAs(filename);
        std::cout << __FILE__ << ":" << __LINE__ << ": File " << filename << " written, going to next iteration of infinite loop." << std::endl;
    }

    MPI_Finalize();
}
