/*
  This file is part of CDO. CDO is a collection of Operators to
  manipulate and analyse Climate model Data.

  Copyright (C) 2003-2020 Uwe Schulzweida, <uwe.schulzweida AT mpimet.mpg.de>
  See COPYING file for copying and redistribution conditions.

  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; version 2 of the License.

  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.
*/

#ifndef PROCESS_H
#define PROCESS_H

#include "cdoStream.h"
#include "modules.h"

#include <vector>
#include <iostream>
#include <string>
#include <set>

constexpr int MAX_PROCESS = 128;
constexpr int MAX_OPERATOR = 128;

enum class ProcessStatus
{
  Ok = 0,
  UnlimitedIOCounts = -1,
  MissInput = -2,
  MissOutput = -3,
  TooManyStreams = -4,
  TooFewStreams = -5,
};

class oper_t
{
public:
  int f1 = 0;
  int f2 = 0;
  const char *name = nullptr;
  const char *enter = nullptr;

  oper_t() {}
  oper_t(int _f1, int _f2, const char *_name, const char *_enter) : f1(_f1), f2(_f2), name(_name), enter(_enter) {}
};

class Process
{
public:
  int m_ID;
  int m_posInParent;
#ifdef HAVE_LIBPTHREAD
  pthread_t threadID;
#endif
  short nchild = 0;
  std::vector<Process *> childProcesses;
  std::vector<Process *> parentProcesses;
  std::vector<CdoStreamID> inputStreams;
  std::vector<CdoStreamID> outputStreams;
  int nChildActive = 0;

  double startTime;

  short nvars = 0;

  int ntimesteps = 0;
  int m_streamCnt = 0;
  char const *m_operatorCommand = "UNINITALIZED";
  char const *operatorName;
  const char *m_obase;
  char prompt[64];

  short m_noper = 0;
  bool m_isActive;

  module_t m_module;
  std::vector<std::string> m_oargv;
  int m_oargc = 0;
  oper_t oper[MAX_OPERATOR];

  Process(int p_ID, const char *p_operatorNamme, const char *operatorCommand);

  pthread_t run();
  ProcessStatus m_status = ProcessStatus::Ok;

  /**
   * returns the number of in streams this process currently has.
   **/
  int getInStreamCnt();
  /**
   * returns the number of out streams this process currently has.
   */
  int getOutStreamCnt();

  /**
   * returns full command through which this process was created including operator name and operator arguments
   */
  const char *getCommand();

  /**
   * Adds a Process as child and creates and adds a new pipe stream.
   */
  void addChild(Process *child_process);
  /**
   * Adds a Process as parent and adds the parents input stream to out streams.
   */
  void addParent(Process *parent_process);
  /**
   * Compares the wanted and current stream counts.
   * @return if the wanted count is -1 this function always returns false.
   * Are the current and wanted stream counts equal 1 and if they differ false.
   */
  bool hasAllInputs();
  /**
   * Adds and creates a new file pstream to the in streams
   */
  void addFileInStream(std::string file);
  /**
   * Adds and creates a new file pstream to the out streams
   */
  void addFileOutStream(std::string file);
  /**
   * Adds and creates a new pipe pstream to the in streams
   */
  void addPipeInStream();
  /**
   * Adds and creates a new file pstream to the out streams
   */
  void addPipeOutStream();
  /**
   * Adds an operator to the process
   */
  int operatorAdd(const char *name, int f1, int f2, const char *enter);
  /**
   * returns the operatorID of the currently in use operator
   */
  int getOperatorID();
  void setInactive();
  const char *inqPrompt() const;
  void printProcessedValues();
  void printBenchmarks(double p_walltime, const char *p_memstring);
  void checkStreamCnt();
  void validate();
  void handleProcessErr();

  bool hasOutStream(const CdoStreamID p_streamPtr);
  bool hasInStream(const CdoStreamID p_streamPtr);
  bool hasNoPipes();
  size_t inqNvals();
  const char *getOutStreamName();
  size_t getOperArgc();
  std::string getArgv(int idx);
  std::string getObase();
  bool inputIsVariable();

  void initProcess(const char *p_operatorName, const char *operatorCommand);

private:
  Process();
  void defPrompt();
  /**
   * Splits the operator arguments stores them.
   * Operator arguments are stored as char arrays and appended with a zero termination.
   */
  void setOperatorArgv(const char *operatorArguments);
};

void setProcessNum(int p_num);

#endif /* PROCESS_H */
