#ifndef _SERIALNUMBERPOOL_INL_INCLUDED
#define _SERIALNUMBERPOOL_INL_INCLUDED

#ifndef _SERIALNUMBERPOOL_H_INCLUDED
#error SerialNumberPool.inl requires SerialNumberPool.h to be included first
#endif

#ifndef UNIX
#include <tchar.h>
#else
#include "reader/tchar.h"
#endif

#include <atlexcept.h>

namespace CryptoPro { namespace PKI
{

    /* BigInteger {
	unsigned char* pbData;
	unsigned long cbData;
	void resize_to( unsigned long length);
	unsigned char add( const BigInteger& value);
	void swap( BigInteger& value);
	BigInteger abs() const;
	void increment( int start = 0);
	BigInteger( const BigInteger& src);
	BigInteger& operator=( const BigInteger& src);
    }
    bool operator==( const BigInteger& lhs, const BigInteger& rhs);

    Byte order in BigInteger is big-endian (so pbData[0] is the most
    significant byte).
    */

    template <typename IPoolT>
    inline CSerialNumberPoolT<IPoolT>::CSerialNumberPoolT()
    {
    }

    template <typename IPoolT>
    inline CSerialNumberPoolT<IPoolT>::CSerialNumberPoolT(
	const typename base::bigIntType& old,
	const typename base::bigIntType& seed,
	const typename base::bigIntType& rangeSize)
    {
	initialize(old,seed,rangeSize);
    }

    template <typename IPoolT>
    inline void CSerialNumberPoolT<IPoolT>::initialize(
	const typename base::bigIntType& old,
	const typename base::bigIntType& seed,
	const typename base::bigIntType& rangeSize)
    {
	if( old.cbData() != base::serialNumberLength 
	    || seed.cbData() != base::seedLength
	    || old.cbData() < base::seedLength + base::seedOffset
	    || old.cbData() != rangeSize.cbData() )
	    AtlThrow(E_INVALIDARG);

	top_.resize_to(base::serialNumberLength);
	::memset(top_.pbData(),0,top_.cbData());
	::memcpy(top_.pbData() + base::seedOffset,seed.pbData(),seed.cbData());
	top_.add(old);
	rangeSize_ = rangeSize;
    }

    template <typename IPoolT>
    inline const typename CSerialNumberPoolT<IPoolT>::base::bigIntType& CSerialNumberPoolT<IPoolT>::top() const
    {
	if(!top_.cbData())
	    AtlThrow(E_INVALIDARG);
	return top_;
    }

    template <typename IPoolT>
    inline void CSerialNumberPoolT<IPoolT>::allocateRange()
    {
	if(!top_.cbData())
	    AtlThrow(E_INVALIDARG);
	top_.add(rangeSize_);
    }

    template <typename IPoolT>
    inline void CSerialNumberPoolT<IPoolT>::lock()
    {
    }

    template <typename IPoolT>
    inline void CSerialNumberPoolT<IPoolT>::unlock()
    {
    }

    template <typename IPoolT>
    inline void CSerialNumberPoolT<IPoolT>::swap( CSerialNumberPoolT& obj) throw()
    {
	top_.swap(obj.top_);
	rangeSize_.swap(obj.rangeSize_);
    }

    // class CSerialNumberRangeT

    template <typename IPoolT>
    inline CSerialNumberRangeT<IPoolT>::CSerialNumberRangeT( 
	IPoolT* pool)
	: pool_(pool)
    {
	if(pool_)
	    nextRange();
    }

    template <typename IPoolT>
    inline void CSerialNumberRangeT<IPoolT>::initialize( IPoolT* pool )
    {        
	if(pool_ != pool)
        {
            pool_ = pool;
	    nextRange();
        }
    }

 //   template <typename IPoolT>
 //   inline bool CSerialNumberRangeT<IPoolT>::ready()
 //   {
	//return (pool_ != 0);
 //   }

    template <typename IPoolT>
    inline typename IPoolT::bigIntType CSerialNumberRangeT<IPoolT>::next()
    {
	if(!pool_)
	    AtlThrow(E_INVALIDARG);
	// делаем все серийные номера положительными
	typename IPoolT::bigIntType sn = nextFreeSerialNumber_.abs();
	nextFreeSerialNumber_.increment();
	if(nextFreeSerialNumber_ == rangeEnd_)
	    nextRange();
	return sn;
    }

    template <typename IPoolT>
    inline void CSerialNumberRangeT<IPoolT>::nextRange()
    {
	if(!pool_)
	    AtlThrow(E_INVALIDARG);
	pool_->lock();
	nextFreeSerialNumber_ = pool_->top();
	pool_->allocateRange();
	rangeEnd_ = pool_->top();
	pool_->unlock();
    }

} /* namespace PKI */ } /* namespace CryptoPro */

#endif //_SERIALNUMBERPOOL_INL_INCLUDED
