#ifndef __ARC_AREX_JOB_H__
#define __ARC_AREX_JOB_H__

#include <string>
#include <list>

#include <arc/User.h>
#include <arc/XMLNode.h>
#include <arc/FileAccess.h>
#include <arc/message/MessageAuth.h>
#include <arc/message/Message.h>
#include "grid-manager/files/ControlFileContent.h"
#include "tools.h"

namespace ARex {

class GMConfig;

#define JOB_POLICY_OPERATION_URN "http://www.nordugrid.org/schemas/policy-arc/types/a-rex/joboperation"
#define JOB_POLICY_OPERATION_CREATE "Create"
#define JOB_POLICY_OPERATION_MODIFY "Modify"
#define JOB_POLICY_OPERATION_READ   "Read"
#define JOB_POLICY_OPERATION_UNDEFINED "Undefined"

class ARexGMConfig {
 private:
  const GMConfig& config_;
  Arc::User user_;
  bool readonly_;
  std::string grid_name_; // temporary solution
  std::string service_endpoint_; // temporary solution
  std::list<Arc::MessageAuth*> auths_;
  // Separate lists outside GMConfig as they can be substituted per user
  std::vector<std::string> session_roots_;
  std::vector<std::string> session_roots_non_draining_;
 protected:
  static Arc::Logger logger;
 public:
  ARexGMConfig(const GMConfig& config,const std::string& uname,const std::string& grid_name,const std::string& service_endpoint);
  operator bool(void) const { return (bool)user_; };
  bool operator!(void) const { return !(bool)user_; };
  const Arc::User& User(void) const { return user_; };
  const GMConfig& GmConfig() const { return config_; };
  bool ReadOnly(void) const { return readonly_; };
  const std::string& GridName(void) const { return grid_name_; };
  const std::string& Endpoint(void) const { return service_endpoint_; };
  void AddAuth(Arc::MessageAuth* auth) { auths_.push_back(auth); };
  void ClearAuths(void) { auths_.clear(); };
  std::list<Arc::MessageAuth*>::iterator beginAuth(void) { return auths_.begin(); };
  std::list<Arc::MessageAuth*>::iterator endAuth(void) { return auths_.end(); };
  std::vector<std::string> SessionRootsNonDraining(void) { return session_roots_non_draining_; };
  std::vector<std::string> SessionRoots(void) { return session_roots_; };
};

class ARexConfigContext:public Arc::MessageContextElement, public ARexGMConfig {
 public:
  ARexConfigContext(GMConfig& config,const std::string& uname,const std::string& grid_name,const std::string& service_endpoint):
    ARexGMConfig(config,uname,grid_name,service_endpoint) { };
  virtual ~ARexConfigContext(void) { };
  static ARexConfigContext* GetRutimeConfiguration(Arc::Message& inmsg, GMConfig& gmconfig,
                  std::string const & default_uname, std::string const & default_endpoint);
  // Authorization methods
  enum OperationType {
    OperationServiceInfo, // information about service
    OperationJobInfo,     // information about job
    OperationJobCreate,   // creation of new job
    OperationJobCancel,   // canceling existing job
    OperationJobDelete,   // removing existing job
    OperationDataInfo,    // getting information about file in session
    OperationDataWrite,   // writing file to session
    OperationDataRead,    // reading file from session
  };
  static bool CheckOperationAllowed(OperationType op, ARexConfigContext* config, std::string& msg);
};

typedef enum {
  ARexJobNoError,
  ARexJobInternalError, // Failed during some internal operation - like writing some file
  ARexJobConfigurationError, // Problem detected which can be fixed by adjusting configuration of service
  ARexJobDescriptionUnsupportedError, // Job asks for feature or combination not supported by service
  ARexJobDescriptionMissingError, // Job is missing optional but needed for this service element
  ARexJobDescriptionSyntaxError, // Job description is malformed - missing elements, wrong names, etc.
  ARexJobDescriptionLogicalError // Job request otherwise corect has some values out of scope of service
} ARexJobFailure;

/** This class represents convenience interface to manage jobs 
  handled by Grid Manager. It works mostly through corresponding
  classes and functions of Grid Manager. */
class ARexJob {
 private:
  std::string id_;
  std::string failure_;
  ARexJobFailure failure_type_;
  bool allowed_to_see_;
  bool allowed_to_maintain_;
  Arc::Logger& logger_;
  /** Returns true if job exists and authorization was checked 
    without errors. Fills information about authorization in 
    this instance. */ 
  bool is_allowed(bool fast = false);
  ARexGMConfig& config_;
  uid_t uid_; /* local user id this job is mapped to - not always same as in config_.user_ */
  gid_t gid_;
  JobLocalDescription job_;
  bool make_job_id();
  static std::size_t make_job_id(ARexGMConfig& config_, Arc::Logger& logger_, std::vector<std::string>& ids);
  bool delete_job_id();
  static bool delete_job_id(ARexGMConfig& config_, Arc::User user_, std::string const& sessiondir, std::vector<std::string>& ids, std::size_t offset = 0);
  bool update_credentials(const std::string& credentials);
  static void make_new_job(ARexGMConfig& config_, Arc::Logger& logger_, int& min_jobs, int& max_jobs, std::string const& job_desc_str,
                           const std::string& delegid,const std::string& queue,const std::string& clientid,JobIDGenerator& idgenerator,
                           std::vector<std::string>& ids, JobLocalDescription& job_, ARexJobFailure& failure_type_, std::string& failure_);
 public:
  static bool Generate(Arc::XMLNode xmljobdesc,int& min_jobs,int& max_jobs,ARexGMConfig& config,const std::string& delegid,const std::string& queue,const std::string& clientid,Arc::Logger& logger,JobIDGenerator& idgenerator,std::vector<std::string>& ids,std::string& failure);
  static bool Generate(std::string const& job_desc_str,int min_jobs,int max_jobs,ARexGMConfig& config,const std::string& delegid,const std::string& queue,const std::string& clientid,Arc::Logger& logger,JobIDGenerator& idgenerator,std::vector<std::string>& ids,std::string& failure);
  /** Create instance which is an interface to existing job */
  ARexJob(const std::string& id,ARexGMConfig& config,Arc::Logger& logger,bool fast_auth_check = false);
  /** Create new job with provided description */
  ARexJob(Arc::XMLNode xmljobdesc,ARexGMConfig& config,const std::string& delegid,const std::string& queue,const std::string& clientid,Arc::Logger& logger,JobIDGenerator& idgenerator);
  /** Create new job with provided textual description */
  ARexJob(std::string const& job_desc_str,ARexGMConfig& config,const std::string& delegid,const std::string& queue,const std::string& clientid,Arc::Logger& logger,JobIDGenerator& idgenerator);
  operator bool(void) { return !id_.empty(); };
  bool operator!(void) { return id_.empty(); };
  /** Returns textual description of failure of last operation */
  std::string Failure(void) { std::string r=failure_; failure_=""; failure_type_=ARexJobNoError; return r; };
  operator ARexJobFailure(void) { return failure_type_; };
  /** Return ID assigned to job */
  std::string ID(void) { return id_; };
  /** Return local user id assigned to job */
  uid_t UID(void) { return uid_; };
  /** Return local user group assigned to job */
  gid_t GID(void) { return gid_; };
  /** Fills provided xml container with job description */
  bool GetDescription(Arc::XMLNode& xmljobdesc);
  /** Cancel processing/execution of job */
  bool Cancel(void);
  /** Remove job from local pool */
  bool Clean(void);
  /** Resume execution of job after error */
  bool Resume(void);
  /** Returns current state of job */
  std::string State(void);
  /** Returns current state of job and sets job_pending to 
     true if job is pending due to external limits */
  std::string State(bool& job_pending);
  /** Returns true if job has failed */
  bool Failed(void);
  /** Returns state at which job failed and sets cause to 
     information what caused job failure: "internal" for server
     initiated and "client" for canceled on client request. */
  std::string FailedState(std::string& cause);
  /** Returns time when job was created. */
  Arc::Time Created(void);
  /** Returns time when job state was last modified. */
  Arc::Time Modified(void);
  /** Returns path to session directory */
  std::string SessionDir(void);
  /** Returns name of virtual log directory */
  std::string LogDir(void);
  /** Return number of jobs associated with this configuration.
      TODO: total for all user configurations. */
  static int TotalJobs(ARexGMConfig& config,Arc::Logger& logger);
  /** Returns list of user's jobs. Fine-grained ACL is ignored. */
  static std::list<std::string> Jobs(ARexGMConfig& config,Arc::Logger& logger);
  /** Creates file in job's session directory and returns handler */
  Arc::FileAccess* CreateFile(const std::string& filename);
  /** Opens file in job's session directory and returns handler */
  Arc::FileAccess* OpenFile(const std::string& filename,bool for_read,bool for_write);
  std::string GetFilePath(const std::string& filename);
  bool ReportFileComplete(const std::string& filename);
  bool ReportFilesComplete();
  /** Opens log file in control directory */
  int OpenLogFile(const std::string& name);
  std::string GetLogFilePath(const std::string& name);
  /** Opens directory inside session directory */
  Arc::FileAccess* OpenDir(const std::string& dirname);
  /** Returns list of existing log files */
  std::list<std::string> LogFiles(void);
  /** Updates job credentials */
  bool UpdateCredentials(const std::string& credentials);
  /** Select a session dir to use for this job */
  static bool ChooseSessionDir(ARexGMConfig& config_,Arc::Logger& logger_,std::string& sessiondir);
};

} // namespace ARex

#endif
