/*
   Copyright 2013-2014 EditShare, 2013-2015 Skytechnology sp. z o.o.

   This file is part of LizardFS.

   LizardFS 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 3.

   LizardFS 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 LizardFS. If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include "common/platform.h"

#include <cstdint>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include "common/exception.h"
#include "common/io_limits_config_loader.h"
#include "common/serialization_macros.h"
#include "common/token_bucket.h"

/**
 * Simple class to make serialization easier
 */
LIZARDFS_DEFINE_SERIALIZABLE_CLASS(IoGroupAndLimit,
		std::string, group,
		uint64_t   , limit);

/**
 * This class is responsible for limiting resources for every group. It gives a user a possibility
 * to define how does he want to limit each group and provides an interface for acquiring these
 * resources. It does that by having a separate TokenBucket rate limiter for every GroupID,
 * initializing it with proper values and passing requests for a given GroupID to an appropriate
 * TokenBucket.
 */
class IoLimitsDatabase {
public:
	typedef std::string GroupId;

	// An exception that is thrown if a user requestes an assignment for a group
	// that cannot be served
	LIZARDFS_CREATE_EXCEPTION_CLASS_MSG(InvalidGroupIdException, Exception,
			"invalid group id");

	// default constructor, all limits are zero
	IoLimitsDatabase() {}

	// set limits (as generated by IoLimitsConfigLoader)
	//
	// all limits in bytes per second
	void setLimits(SteadyTimePoint now, const IoLimitsConfigLoader::LimitsMap& limits,
			uint32_t accumulate_ms);

	// get a list of all groups
	std::vector<std::string> getGroups() const;

	// get a vector of all groups with their limits in bytes per second
	std::vector<IoGroupAndLimit> getGroupsAndLimits() const;

	// try to satisfy client's request to change limit in given I/O group, return assigned limit
	uint64_t request(SteadyTimePoint now, const GroupId& groupId, uint64_t bytes);

private:
	typedef std::map<GroupId, TokenBucket> Groups;
	Groups groups_;
};
