/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You may
 * obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************/

#ifndef _Condor_Accountant_H_
#define _Condor_Accountant_H_


#include "condor_classad.h"

#include "condor_state.h"

#include <memory>
#include <vector>
#include <map>
#include <set>
#include <string>

// this is the required minimum separation between two priorities for them
// to be considered distinct values
static const double PriorityDelta = 0.5;

// local working attribute to stash match-cost on resource ads that have a 
// request matched against them
#define CP_MATCH_COST "_cp_match_cost"

#include "AccountantDB.h"

struct GroupEntry;

class Accountant {

public:

  //--------------------------------------------------------
  // User Functions
  //--------------------------------------------------------

  Accountant();
  ~Accountant();

  void Initialize(GroupEntry* group);  // Configuration

  int   GetResourcesUsed(const std::string& CustomerName); // get # of used resources (unweighted by SlotWeight)
  double GetWeightedResourcesUsed(const std::string& CustomerName);
  double GetPriority(const std::string& CustomerName); // get priority for a customer
  int   GetCeiling(const std::string& CustomerName); // get Ceiling for a customer
  int   GetFloor(const std::string& CustomerName);  // get Floor for a customer
  void  SetPriority(const std::string& CustomerName, double Priority); // set priority for a customer
  void  SetCeiling(const std::string& CustomerName, int Ceiling); // set Ceiling for a customer
  void  SetFloor(const std::string& CustomerName, int Floor); // set Floor for a customer

  // Ceiling leases: temporarily override a submitter's ceiling for a bounded
  // duration, then revert to the prior value. Expiration is an absolute unix
  // time; an expiration of 0 (or attribute absent) means no lease is active.
  // Returns true on success; on failure sets err and leaves state unchanged.
  bool SetCeilingLease(const std::string& CustomerName, int Ceiling,
                       int DurationSeconds, std::string& err);
  bool CancelCeilingLease(const std::string& CustomerName, std::string& err);
  time_t GetCeilingLeaseExpiration(const std::string& CustomerName);
  // Called once per negotiation cycle: expires any lease whose time has passed.
  void CheckCeilingLeases();

  // Floor leases: same shape as ceiling leases above.
  bool SetFloorLease(const std::string& CustomerName, int Floor,
                     int DurationSeconds, std::string& err);
  bool CancelFloorLease(const std::string& CustomerName, std::string& err);
  time_t GetFloorLeaseExpiration(const std::string& CustomerName);
  void CheckFloorLeases();

  // Priority-factor leases: same shape, but the leased value is a double.
  bool SetPriorityFactorLease(const std::string& CustomerName, double PriorityFactor,
                              int DurationSeconds, std::string& err);
  bool CancelPriorityFactorLease(const std::string& CustomerName, std::string& err);
  time_t GetPriorityFactorLeaseExpiration(const std::string& CustomerName);
  void CheckPriorityFactorLeases();

  void SetAccumUsage(const std::string& CustomerName, double AccumUsage); // set accumulated usage for a customer
  void SetBeginTime(const std::string& CustomerName, int BeginTime); // set begin usage time for a customer
  void SetLastTime(const std::string& CustomerName, int LastTime); // set Last usage time for a customer
  double GetPriorityFactor(const std::string& CustomerName); // get priority factor for a customer

  void SetPriorityFactor(const std::string& CustomerName, double PriorityFactor);
  void ResetAccumulatedUsage(const std::string& CustomerName);
  void DeleteRecord(const std::string& CustomerName);
  void ResetAllUsage();

  void AddMatch(const std::string& CustomerName, ClassAd* ResourceAd); // Add new match
  void RemoveMatch(const std::string& ResourceName); // remove a match

  double GetSlotWeight(ClassAd *candidate) const;
  void UpdatePriorities(); // update all the priorities
  void UpdateOnePriority(time_t T, time_t TimePassed, double AgingFactor, const std::string& customerName, ClassAd *ad); // Help function for above

  void CheckMatches(std::vector<ClassAd *>& ResourceList);  // Remove matches that are not claimed

  time_t GetLastUpdateTime() const { return LastUpdateTime; }

  double GetLimit(const std::string& limit);
  double GetLimitMax(const std::string& limit);
  void ReportLimits(ClassAd *attrList);

  ClassAd* ReportState(bool rollup = false);
  ClassAd* ReportState(const std::string& CustomerName);
  bool ReportState(ClassAd& queryAd, ClassAdList & ads, bool rollup = false);

  void CheckResources(const std::string& CustomerName, int& NumResources, double& NumResourcesRW);

  void DisplayLog();
  void DisplayMatches();

  ClassAd* GetClassAd(AccountantTable table, const std::string& Key);

  // This maps submitter names to their assigned accounting group.
  // When called with a defined group name, it maps that group name to itself.
  GroupEntry* GetAssignedGroup(const std::string& CustomerName);
  GroupEntry* GetAssignedGroup(const std::string& CustomerName, bool& IsGroup);

  bool UsingWeightedSlots() const;

  struct ci_less {
      bool operator()(const std::string& a, const std::string& b) const {
          return strcasecmp(a.c_str(), b.c_str()) < 0;
      }
  };

private:

  //--------------------------------------------------------
  // Private methods Methods
  //--------------------------------------------------------
  
  void RemoveMatch(const std::string& ResourceName, time_t T);

  void LoadLimits(std::vector<ClassAd *> &resourceList);
  void ClearLimits();
  void DumpLimits();

  void IncrementLimit(const std::string& limit);
  void DecrementLimit(const std::string& limit);

  void IncrementLimits(const std::string& limits);
  void DecrementLimits(const std::string& limits);

  // Get group priority helper function.
  double getGroupPriorityFactor(const std::string& CustomerName);

  //--------------------------------------------------------
  // Configuration variables
  //--------------------------------------------------------

  double MinPriority;        // Minimum priority (if no resources used)
  double NiceUserPriorityFactor;
  double RemoteUserPriorityFactor;
  double DefaultPriorityFactor;
  std::string AccountantLocalDomain;
  double HalfLifePeriod;     // The time in sec in which the priority is halved by aging
  std::string LogFileName;      // Name of Log file
  int	MaxAcctLogSize;		// Max size of log file
  bool  DiscountSuspendedResources;
  bool  UseSlotWeights; 

  //--------------------------------------------------------
  // Data members
  //--------------------------------------------------------

  std::unique_ptr<AccountantDB> db;
  time_t LastUpdateTime;

  std::map<std::string, double> concurrencyLimits;

  GroupEntry* hgq_root_group;
  std::map<std::string, GroupEntry*, ci_less> hgq_submitter_group_map;

  //--------------------------------------------------------
  // Utility functions
  //--------------------------------------------------------

  static std::string GetResourceName(ClassAd* Resource);
  bool GetResourceState(ClassAd* Resource, State& state);
  int IsClaimed(ClassAd* ResourceAd, std::string& CustomerName);
  int CheckClaimedOrMatched(ClassAd* ResourceAd, const std::string& CustomerName);
  static std::string GetDomain(const std::string& CustomerName);

  void ReportGroups(GroupEntry* group, ClassAd* ad, bool rollup, std::map<std::string, int>& gnmap);
};


extern void parse_group_name(const std::string& gname, std::vector<std::string>& gpath);

#endif
