#include "Intact.hpp"

auto const root_dir = std::string(ROOT_DIR);

int main() {
  // Setup logging
  Intact::setupLogging(0);

  // Load L-bracket geometry
  auto geometry = Intact::MeshModel(root_dir + "L-bracket.stl");
  geometry.instance_id = "L_bracket";

  // Define aluminum material
  auto material = std::make_shared<Intact::IsotropicMaterialDescriptor>();
  material->density = 2700.0;       // kg/m^3
  material->poisson_ratio = 0.33;
  material->youngs_modulus = 69.0e9; // Pa

  // Define boundary conditions
  auto fixed_bc1 = std::make_shared<Intact::FixedBoundaryDescriptor>();
  fixed_bc1->boundary = Intact::MeshModel(root_dir + "L-bracket-bc1.stl");

  auto fixed_bc2 = std::make_shared<Intact::FixedBoundaryDescriptor>();
  fixed_bc2->boundary = Intact::MeshModel(root_dir + "L-bracket-bc2.stl");

  auto load = std::make_shared<Intact::VectorForceDescriptor>();
  load->boundary = Intact::MeshModel(root_dir + "L-bracket-load.stl");
  load->direction = {0.0, 0.0, -1.0};
  load->magnitude = 1000.0;
  load->units = Intact::UnitSystemType::MeterKilogramSecond;

  // Setup the simulation scenario
  Intact::LinearElasticScenarioDescriptor scenario;
  scenario.materials = {{"Aluminum", material}};
  scenario.boundary_conditions = {fixed_bc1, fixed_bc2, load};
  scenario.metadata.units = Intact::UnitSystemType::MeterKilogramSecond;
  scenario.metadata.resolution = 100000;

  // Enable Adaptive Mesh Refinement (AMR) - Beta
  // AMR is only compatible with linear elasticity.
  scenario.metadata.integrator_override = Intact::IntegratorType::AdaptiveLinearElasticity;
  Intact::AmrMetadata amr_config;
  amr_config.refine_energy_threshold = 0.5;
  amr_config.max_iterations = 5;
  amr_config.max_refinement_depth = 3;
  amr_config.max_resolution = 5000000;
  scenario.metadata.amr = amr_config;

  // Associate material with geometry
  auto component = Intact::MaterialDomain(geometry, "Aluminum", scenario);
  Intact::Assembly assembly = {component};

  // Initialize and run the simulation
  Intact::StressSimulator simulator(assembly, scenario);
  simulator.solve();

  // Query results for stress distribution
  Intact::QueryResult results(assembly);
  Intact::FieldQuery query(Intact::FieldType::VonMisesStress);
  simulator.sample(query, results);
  results.writeVTK("amr.vtu", Intact::UnitSystemType::MeterKilogramSecond);

  return EXIT_SUCCESS;
}
