//# Applicator.h: interface to parallelization infrastructure
//# Copyright (C) 1999,2000
//# Associated Universities, Inc. Washington DC, USA.
//#
//# This library is free software; you can redistribute it and/or modify it
//# under the terms of the GNU Library General Public License as published by
//# the Free Software Foundation; either version 2 of the License, or (at your
//# option) any later version.
//#
//# This library 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 Library General Public
//# License for more details.
//#
//# You should have received a copy of the GNU Library General Public License
//# along with this library; 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: aips2-request@nrao.edu.
//#        Postal address: AIPS++ Project Office
//#                        National Radio Astronomy Observatory
//#                        520 Edgemont Road
//#                        Charlottesville, VA 22903-2475 USA
//#
//#
//# $Id$

#ifndef SYNTHESIS_APPLICATOR_H
#define SYNTHESIS_APPLICATOR_H

//# Includes

#include <map>
#include <casacore/casa/aips.h>
#include <casacore/casa/Arrays/Vector.h>
#include <casacore/lattices/Lattices/Lattice.h>
#include <casacore/casa/Containers/Record.h>
#include <synthesis/Parallel/PTransport.h>

namespace casa { //# NAMESPACE CASA - BEGIN

//# Forward Declarations
class Algorithm;

// <summary>
// Class which provides an interface to the parallelization infrastructure
// </summary>

// <use visibility=local>

// <reviewed reviewer="" date="yyyy/mm/dd" tests="" demos="">
// </reviewed>

// <prerequisite>
//   <li> Algorithm
//   <li> PTransport
// </prerequisite>
//
// <etymology>
// Applies or controls the execution of parallelized algorithms.
// </etymology>
//
// <synopsis>
// The Applicator class provides the interface to parallel communication.
// It holds the parallel transport layer, and controls the execution of
// parallelized algorithms.
// </synopsis>
//
// <example>
// </example>
//
// <motivation>
// To provide a simple programming interface to parallelization, and
// to encapsulate the transport layer and parallel process control.
// </motivation>
//
//# <thrown>
//#    <li>
//#    <li>
//# </thrown>
//
//# <todo asof="1999/12/21">
//#   <li> Document
//# </todo>


class Applicator {
 public:
  // Enum to define the process status table
  enum Status {FREE, ASSIGNED};

  // Recognized signals
  enum Signals {STOP = 0, DONE = -1};

  // Default constructor, and destructor
  Applicator();
  ~Applicator();

  // Initialization (includes parallel transport initialization)
  void init(casacore::Int argc, casacore::Char *argv[]);
  void initThreads(casacore::Int argc, casacore::Char *argv[]);
  void initThreads();
  // release workers from loop
  void destroyThreads();
  // tells you if initThreads(argc,  argv) version has been called already
  // MPI does not like more than 1 call to MPI_Init or MPI_InitThread
  bool initialized();
  // define an Algorithm if we need too; takes ownership of the alg pointer
  void defineAlgorithm(Algorithm *);

  // Status functions to indicate whether this Applicator is
  // executing as a controller or worker process
  casacore::Bool isController();
  casacore::Bool isWorker();

  // true if executing serially
  casacore::Bool isSerial() {return serial;};

  // Return the number of processes
  casacore::Int numProcs() {return nProcs;};

  // Assign the next free worker process to a specified Algorithm
  casacore::Bool nextAvailProcess(Algorithm &a, casacore::Int &rank);

  // Return the rank of the next process to complete the specified Algorithm
  casacore::Int nextProcessDone(Algorithm &a, casacore::Bool &allDone);

  // Signal that a worker process is done
  void done();

  // Execute an algorithm directly
  void apply(Algorithm &a);

  // Put and get methods to be executed on the parallel transport layer
  casacore::Int put(const casacore::Array<casacore::Float> &an) {return comm->put(an);};
  casacore::Int put(const casacore::Array<casacore::Double> &an) {return comm->put(an);};
  casacore::Int put(const casacore::Array<casacore::Int> &an) {return comm->put(an);};
  casacore::Int put(const casacore::Array<casacore::Complex> &an) {return comm->put(an);};
  casacore::Int put(const casacore::Array<casacore::DComplex> &an) {return comm->put(an);};
  casacore::Int put(const casacore::Float &n) {return comm->put(n);};
  casacore::Int put(const casacore::Complex &n) {return comm->put(n);};
  casacore::Int put(const casacore::DComplex &n) {return comm->put(n);};
  casacore::Int put(const casacore::Double &n) {return comm->put(n);};
  casacore::Int put(const casacore::Int &n) {return comm->put(n);};
  casacore::Int put(const casacore::Bool &b) {return comm->put(b);};
  casacore::Int put(const casacore::String &s) {return comm->put(s);};
  casacore::Int put(const casacore::Record &r) {return comm->put(r);};

  casacore::Int get(casacore::Array<casacore::Float> &an) {return comm->get(an);};
  casacore::Int get(casacore::Array<casacore::Double> &an) {return comm->get(an);};
  casacore::Int get(casacore::Array<casacore::Complex> &an) {return comm->get(an);};
  casacore::Int get(casacore::Array<casacore::DComplex> &an) {return comm->get(an);};
  casacore::Int get(casacore::Array<casacore::Int> &an) {return comm->get(an);};
  casacore::Int get(casacore::Float &n) {return comm->get(n);};
  casacore::Int get(casacore::Double &n) {return comm->get(n);};
  casacore::Int get(casacore::Complex &n) {return comm->get(n);};
  casacore::Int get(casacore::DComplex &n) {return comm->get(n);};
  casacore::Int get(casacore::Int &n) {return comm->get(n);};
  casacore::Int get(casacore::Bool &b) {return comm->get(b);};
  casacore::Int get(casacore::String &s) {return comm->get(s);};
  casacore::Int get(casacore::Record &r) {return comm->get(r);};

 private:
  // Pointer to the parallel transport
  PTransport *comm;

  // casacore::Map of known algorithm names and id.'s
  std::map<casacore::String, casacore::Int> algorithmIds;
  std::map<casacore::Int, Algorithm*> knownAlgorithms;

  // ID for the last Algorithm defined.
  casacore::Int LastID;

  // true if no more processes are free
  casacore::Bool usedAllThreads;

  // true if executing in serial
  casacore::Bool serial;

  // Number of processes
  casacore::Int nProcs;

  // Process status list
  casacore::Vector<casacore::Int> procStatus;

  // Executed by worker process waiting for an assigned task
  void loop();

  // Fill algorithm map
  void defineAlgorithms();

  // Utility functions for the current list of processes, and their status
  void setupProcStatus();
  casacore::Int findFreeProc(casacore::Bool &lastOne);

  bool debug_p = false;
  bool initialized_p;
  casacore::Int donesig_p; //have to keep in context in serial case
};


} //# NAMESPACE CASA - END

#endif