#ifndef _SERIALNUMBERPOOL_H_INCLUDED
#define _SERIALNUMBERPOOL_H_INCLUDED

namespace CryptoPro { namespace PKI
{
    template <typename BigIntT, int snLength = 20, int seedLen = 4, int seedOff = 8>
    class ISerialNumberPoolT
    {
    public:
	typedef BigIntT bigIntType;

	virtual const bigIntType& top() const = 0;
	virtual void allocateRange() = 0;
	virtual void lock() = 0;
	virtual void unlock() = 0;

	virtual ~ISerialNumberPoolT() {}

	enum
	{
	    serialNumberLength = snLength,
	    seedLength = seedLen,
	    seedOffset = seedOff
	};
    };

    template <typename IPoolT>
    class CSerialNumberPoolT: public IPoolT
    {
	typedef IPoolT base;
    public:
	inline CSerialNumberPoolT();
	inline CSerialNumberPoolT(
	    const typename base::bigIntType& old,
	    const typename base::bigIntType& seed,
	    const typename base::bigIntType& rangeSize);
	inline void initialize(
	    const typename base::bigIntType& old,
	    const typename base::bigIntType& seed,
	    const typename base::bigIntType& rangeSize);
	inline const typename base::bigIntType& top() const;
	inline void allocateRange();
	virtual inline void lock();
	virtual inline void unlock();
	inline void swap( CSerialNumberPoolT& obj) throw();
    private:
	// Запрещаем копирование до тех времен пока оно не понадобится
	CSerialNumberPoolT( const CSerialNumberPoolT&);
	CSerialNumberPoolT& operator=( const CSerialNumberPoolT&);

	typename base::bigIntType top_;
	typename base::bigIntType rangeSize_;
    };

    // Глобальный смысл SerialNumberRange - уменьшение простоя в
    // блокировках при получении нового серийного номера штампа.
    // Идея следующая: каждый рабочий поток получает диапазон серийных
    // номеров из пула и спокойно берет номера из него. Как только
    // диапазон исчерпан, рабочий поток блокирует пул серийных номеров
    // и запрашивает у него новый диапазон. Маловероятно, что несколько 
    // потоков одновременно запросят новые диапазоны, а следовательно 
    // встанут на блокировках.
    // --------------------------------------------------------------
    // SerialNumberRange (поле класса CTSPIsapiWorker) хранит указатель на
    // ISerialNumberPool (поле TSPSrvContext). CTSPIsapiWorker существует
    // пока загружен ISAPIExtension, а контекс [TSPSrvContext] живет между старт-стопом 
    // службы. Поэтому необходимо постоянно проверять, чтобы SerialNumberRange
    // хранил указатель на ISerialNumberPool из текущего контекста.
    // Проверка на "актуальность" указателя это костыль :), зато он лечит падение
    // службы минимальными изменениями в ней (см. jira: CADES-596)
    // ---------------------------------------------------------------
    // Правильное и долгое решение:
    // SerialNumberPool сделать полем TSPIsapiExtension, а доступ к нему будет обес-
    // печиваться через новый сервис (по аналогии с EventLog и SrvContext).
    // Тогда пул инициализируется только раз, при загрузке расширения ISAPI.
    // Рабочие потоки инициализируют свои SerialNumberRange при старте 
    // (CIsapiWorker::Initialize в который передается указатель на IIsapiExtension)
    //
    template <typename IPoolT>
    class CSerialNumberRangeT
    {
    public:
	inline CSerialNumberRangeT( IPoolT* pool = 0);

	inline void initialize( IPoolT* pool );
	//inline bool ready();
	inline typename IPoolT::bigIntType next();
    private:
	inline void nextRange();

	IPoolT* pool_;
	typename IPoolT::bigIntType nextFreeSerialNumber_;
	typename IPoolT::bigIntType rangeEnd_;
    };
} /* namespace PKI */ } /* namespace CryptoPro */

#include "SerialNumberPool.inl"

#endif // _SERIALNUMBERPOOL_H_INCLUDED
