//# tNewMSSimulator.cc: Tests the MS Simulator
//# Copyright (C) 1995,1999,2000,2001,2016
//# Associated Universities, Inc. Washington DC, USA.
//#
//# This program is free software; you can redistribute it and/or modify it
//# under the terms of the GNU General Public License as published by the Free
//# Software Foundation; either version 2 of the License, or (at your option)
//# any later version.
//#
//# This program is distributed in the hope that it will be useful, but WITHOUT
//# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
//# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
//# more details.
//#
//# You should have received a copy of the GNU General Public License along
//# with this program; if not, write to the Free Software Foundation, Inc.,
//# 675 Massachusetts Ave, Cambridge, MA 02139, USA.
//#
//# Correspondence concerning AIPS++ should be addressed as follows:
//#        Internet email: casa-feedback@nrao.edu.
//#        Postal address: AIPS++ Project Office
//#                        National Radio Astronomy Observatory
//#                        520 Edgemont Road
//#                        Charlottesville, VA 22903-2475 USA

#define _POSIX_C_SOURCE 200809L //For mkdtemp(), stpcpy(), nftw()
#include <casacore/casa/aips.h>
#include <ftw.h>
#include <stdio.h>
#include <random>
#include <string>
#include <casacore/ms/MSOper/NewMSSimulator.h>
#include <casacore/ms/MeasurementSets/MeasurementSet.h>
#include <casacore/measures/Measures/MeasTable.h>
#include <casacore/casa/Exceptions/Error.h>

void test_NewMSSimulator_Constructors();

void test_NewMSSimulator_RandomAntenna();

int removeFile(const char *fpath, const struct stat *sb, int typeflag, 
               struct FTW* ftwbuf);

int main() {

  try {
    test_NewMSSimulator_Constructors();
    test_NewMSSimulator_RandomAntenna();
  }
  catch (const std::exception& x) {
    std::cerr << "Exception : " << x.what() << std::endl;
    return 1;
  }
  std::cout<<"OK"<<std::endl;
  return 0;
}

/*
 * This function will be called by nftw() with a path arguments and will remove the file in the path
 */
int removeFile(const char *fpath, const struct stat *sb, int typeflag, 
               struct FTW* ftwbuf)
{
  (void)sb;  //Unused vars
  (void)typeflag;
  (void)ftwbuf;
    
  int rv = remove(fpath);
  if(rv)
    perror(fpath);
  return rv;
}

/*
 * This class helps testing the NewMSSimulator class. It holds a reference
 * to the simulator and the destructor will take care of deleting
 * the whole MS directory generated by the simulator. 
 */
class NewMSSimulatorTester
{
public:
  
  NewMSSimulatorTester(const std::string& ms_name) :
     msName_p(ms_name)
  {
    simulator_p.reset(new casacore::NewMSSimulator(ms_name));
  }

  NewMSSimulatorTester(casacore::MeasurementSet& otherMs) :
     msName_p(otherMs.tableName())
  {
    simulator_p.reset(new casacore::NewMSSimulator(otherMs));
  }

  ~NewMSSimulatorTester()
  {
    //We have to delete the underlying ms before removing the directory
    simulator_p.reset(0);
    //This will recursively remove everything in the MS directory
    nftw(msName_p.c_str(), removeFile, 64, FTW_DEPTH | FTW_PHYS);
  }

  std::unique_ptr<casacore::NewMSSimulator> simulator_p;
  std::string msName_p; 

};

/*
 * The most simple test on the simulator, just contruct it with defaults
 */
void test_NewMSSimulator_Constructors()
{
  //Simple constructor
  NewMSSimulatorTester simulatorTester("newsim");
  
  //This shared pointer gets deleted before the shared pointer inside simulator_p
  std::shared_ptr<casacore::MeasurementSet> ms = simulatorTester.simulator_p->getMs();
  
  //Constructor from existing MS. Not working. Has to be copied first?
  //NewMSSimulatorTester simulatorTesterFromMS(*ms);

  //Simple constructor
  NewMSSimulatorTester simulatorTester2("newsim2");
  
  //This shared pointer gets deleted before the shared pointer inside simulator_p
  std::shared_ptr<casacore::MeasurementSet> ms2 = simulatorTester.simulator_p->getMs();
  
  //simulator is destroyed, but we keep a shared_ptr of the ms, so it should be fine
  simulatorTester2.simulator_p.reset(0);

}

/*
 * Filling random antenna information
 */
void test_NewMSSimulator_RandomAntenna()
{
  std::mt19937 rng;
  
  int nAntMax = 10;
  for(int nAnt = 2 ; nAnt <= nAntMax; ++nAnt)
  {
    casacore::Vector<casacore::Double> x(nAnt), y(nAnt), z(nAnt), diam(nAnt), offset(nAnt);
    casacore::Vector<casacore::String> mount(nAnt), name(nAnt), pad(nAnt);

    std::uniform_real_distribution<> dist(0., 100.);
    std::generate(x.begin(), x.end(), [&] () { return dist(rng); });
    std::generate(y.begin(), y.end(), [&] () { return dist(rng); });
    std::generate(z.begin(), z.end(), [&] () { return dist(rng); });
    std::generate(diam.begin(), diam.end(), [&] () { return dist(rng); });
    std::generate(offset.begin(), offset.end(), [&] () { return dist(rng); });
    
    std::fill(mount.begin(), mount.end(), "ALT-AZ");
    std::fill(name.begin(), name.end(), "NAME");
    std::fill(pad.begin(), pad.end(), "PAD");

    casacore::MPosition vlaPosition;
    casacore::MeasTable::Observatory(vlaPosition, "VLA");

    NewMSSimulatorTester simulatorTester("newsim_"+std::to_string(nAnt));

    simulatorTester.simulator_p->initAnt("Simulated", x, y, z, diam, offset,
        mount, name, pad, "global", vlaPosition);

    //This shared pointer gets deleted before the shared pointer inside simulator_p
    auto ms = simulatorTester.simulator_p->getMs();
    
  }
}
