/* Distributed under the Apache License, Version 2.0.
   See accompanying NOTICE file for details.*/

#include <iostream>
#include <fstream>
#include "LBM.h"
#include "utils/RunUtils.h"
#ifdef VTK_EXTENSION
  #include "VTKUtils.h"
#endif
#include "LBMVerification.h"
#include "utils/TimingProfile.h"

int main(int argc, char* argv[])
{
  //LBM::VTK::TestCSVPlot();

  Logger log("./LBMVerificationDriver.log");
  // Specify which CUDA device to use, or leave black to automatically choose the largest device found.
  if (!cuda_device_check(log))
  {
    log.Error("CUDA device check failed, ensure you have specified a valid CUDA device id.");
    return 1;
  }
  LBM::RunConfig runset;
  LBM::VerificationReport verification_report(&log);
  std::string out_dir = "./verification_runs/";

  std::vector<LBM::RunIO> runs;
  if (argc >=2)
  {
    for (int i = 1; i < argc; i++)
    {
      if (std::string(argv[i]).find(".set") != std::string::npos)
      {
        std::string arg = runset.GetVerificationDir() + "/" + argv[i];
        log.Info("Processing : " + arg);
        std::string line;
        std::ifstream cFile(arg, std::ios::in);
        while (std::getline(cFile, line))
        {
          if (line.empty() || line.find("#") != std::string::npos)
            continue;// Skip a comment
          for (LBM::RunIO run : runset.GetVerificationDirRuns("/" + line))
            runs.push_back(run);
        }
      }
      else
      {// Must be a directory, look for files in it
        std::string specific_run = argv[i];
        log.Info("Processing : " + std::string());
        for (LBM::RunIO run : runset.GetVerificationDirRuns("/"+ specific_run))
          runs.push_back(run);
      }
    }
  }
  else
  {
    // You can set this string to be a specific run directory under the verification directory
    // This will run that specific run only, if you want to debug it
    std::string specific_run = "";// "/cylinder0_spacing=0.001";
    for (LBM::RunIO run : runset.GetVerificationDirRuns(specific_run))
      runs.push_back(run);
  }
  
  try
  {
    TimingProfile timer;
    for (LBM::RunIO run : runs)
    {
      timer.Start("Run");
      LBM::Run lbm;
      std::string name = run.output_base_path;
      if(name[name.length()-1] == '/')// if the last character is a '/', remove it
        name = name.substr(0, run.output_base_path.length()-1);
      name = name.substr(name.find_last_of("/") + 1);
      log.Info("Running " + name);
      if (!LBM::LoadConfiguration(run.input, lbm))
      {
        verification_report.MissingRun(name);
        continue;
      }
      if (lbm.cfg.name.empty())
        lbm.cfg.name = name;
      switch (lbm.in.geometry_type)
      {
      case LBM::GeometryType::Brick:
      {
        LBM::BrickVerification brick(verification_report);
        brick.RunComparison(runset.GetVerificationDir()+"/"+name+"/", run.output_base_path, lbm);
        break;
      }
      case LBM::GeometryType::Cylinder:
      {
        LBM::CylinderVerification cylinder(verification_report);
        cylinder.RunComparison(runset.GetVerificationDir()+"/"+name+"/", run.output_base_path, lbm);
        break;
      }
      default:
        break;
      }
      verification_report.RecordRuntime(name, timer.GetElapsedTime_s("Run"));
    }
  }
  catch (std::exception ex)
  {
    log.Error(ex.what());
    return 1;
  }


  verification_report.WriteReport(out_dir);
  return 0;
}
