#ifndef __ASN1_BLOB_H
#define __ASN1_BLOB_H

#ifdef _WIN32
#include <windows.h>
#include <WinCrypt.h>
#include <time.h>
#else
#include "CSP_WinCrypt.h"
#endif

#include <list>
#include "ASN1Exceptions.h"
#include <string.h>
namespace asn1data {
struct ASN1T_Name;
}

#ifdef ANDROID
namespace std {
	typedef basic_string<wchar_t> wstring;
}
#endif

// 4 Kb
#define CACMPT_BLOB_MEM_UNIT_SIZE ( 4 * 1024 )

struct CACMPT_BLOB {

    CACMPT_BLOB() : cbData(0), pbData(0), assignedSize( 0 )
    {
    }

    CACMPT_BLOB( const BYTE *ptr, DWORD l ) : cbData(0), pbData(0), assignedSize( 0 )
    {
	assign( ptr, l );
    }

    CACMPT_BLOB( const CACMPT_BLOB &src ) : cbData( 0 ), pbData( 0 ), assignedSize( 0 )
    {
	assign( src.pbData, src.cbData );
    }

    CACMPT_BLOB( DWORD l ) : cbData(0), pbData(0), assignedSize(0)
    {
	resize_to( l, FALSE );
    }

    ~CACMPT_BLOB()
    {
	delete[] pbData;
    }

    void resize_to( DWORD l, BOOL doCopy ) 
    { 
	if( l <= assignedSize )
	{
	    cbData = l;
	    return;
	}

	DWORD newAssignedSize = ( assignedSize == 0 ) ? CACMPT_BLOB_MEM_UNIT_SIZE : assignedSize;
	while( l > newAssignedSize )
	{
	    newAssignedSize *= 2;
	}

	BYTE *p = new BYTE[ newAssignedSize ];

	if( cbData && doCopy )
	    memcpy( p, pbData, cbData );
	delete[] pbData;
	pbData = p;
	cbData = l;
	assignedSize = newAssignedSize;
    }

    void resize_to( DWORD l ) 
    {
	resize_to( l, TRUE );
    }

    void set_size( DWORD l ) 
    {
	resize_to( l, FALSE );
    }

    void swap( CACMPT_BLOB &right ) throw()
    { 
	std::swap ( pbData, right.pbData );
	std::swap ( cbData, right.cbData );
    }

    void reverse()
    {
	for( DWORD i = 0; i < cbData / 2; i++ )
	{ 
	    BYTE tmp = pbData[i]; 
	    pbData[i] = pbData[cbData - i - 1];
	    pbData[cbData - i - 1] = tmp; 
	}
    }

    void append( const BYTE *ptr, DWORD l )
    { 
	resize_to( cbData + l );
	if( l )
	{
	    memcpy( pbData + cbData - l, ptr, l );
	}
    }

    CACMPT_BLOB& operator+=( const CACMPT_BLOB &right )
    { 
	resize_to( cbData + right.cbData );
	if( right.cbData )
	    memcpy( pbData + cbData - right.cbData, right.pbData, right.cbData );
	return *this;
    }

    void clear()
    {
	resize_to( 0 );
    }

    CACMPT_BLOB& operator=( const CACMPT_BLOB &src )
    {
	assign( src.pbData, src.cbData );
	return *this;
    }

    bool operator==( const CACMPT_BLOB &src ) const
    { return src.cbData == cbData && ( !cbData ||
	!memcmp( src.pbData, pbData, cbData ) ); }
    bool operator!=( const CACMPT_BLOB &src ) const
    { return !( *this == src ); }

    // CPCSP-5790
    bool operator<( const CACMPT_BLOB &src ) const
    { return cbData < src.cbData || (cbData == src.cbData && cbData
    && memcmp( pbData, src.pbData, cbData ) < 0); }
    bool operator<=( const CACMPT_BLOB &src ) const
    { return cbData < src.cbData || (cbData == src.cbData && cbData
    && memcmp( pbData, src.pbData, cbData ) <= 0); }
    bool operator>( const CACMPT_BLOB &src ) const
    { return cbData < src.cbData || (cbData == src.cbData && cbData
    && memcmp( pbData, src.pbData, cbData ) > 0); }
    bool operator>=( const CACMPT_BLOB &src ) const
    { return cbData < src.cbData || (cbData == src.cbData && cbData
    && memcmp( pbData, src.pbData, cbData ) >= 0); }
    int compare( const CACMPT_BLOB &src ) const
    { 
	if( cbData == src.cbData && cbData )
	    return memcmp( pbData, src.pbData, cbData );
	else
	    return cbData - src.cbData;
    }

    void assign( const BYTE *ptr, DWORD l )
    {
	if( l == 0 )
	{
	    delete[] pbData;
	    cbData = 0;
	    assignedSize = 0;
	    pbData = NULL;
	}
	else // l > 0
	{
	    resize_to( l );
	    memcpy( pbData, ptr, l );
	}
    }

    void readFromFile( const char *name );
    void readFromFile( const std::string &name ) { readFromFile( name.c_str() ); }
    void writeToFile( const char *name ) const;
    void writeToFile( const std::string &name ) const
    { writeToFile( name.c_str() ); }
    void readFromHexString( const char *str );
    void readFromHexString( const char *str, size_t str_len);
    std::string writeToHexString() const;
    CACMPT_BLOB toBase64(DWORD dwFlags) const;
    CACMPT_BLOB fromBase64() const;
    CACMPT_BLOB toBase64Hdr( const char *header, 
	const char *footer ) const;
    CACMPT_BLOB fromBase64Hdr() const;

    static const char *Base64CertificateHeader;
    static const char *Base64CertificateFooter;
    static const char *Base64RequestHeader;
    static const char *Base64RequestFooter;
    static const char *Base64CRLHeader;
    static const char *Base64CRLFooter;
    DWORD cbData;
    BYTE *pbData;
    DWORD assignedSize;
};

struct CACMPT_AlgorithmIdentifier
{
    CACMPT_AlgorithmIdentifier() { *algorithm = 0; }

    CACMPT_AlgorithmIdentifier(const CACMPT_AlgorithmIdentifier &src)
    {
	*algorithm = 0;
	assign(src);
    }

    CACMPT_AlgorithmIdentifier &operator=(const CACMPT_AlgorithmIdentifier &src)
    {
	assign(src);
	return *this;
    }

    // Строка - ASN1T_AlgorithmIdentifier.ASN1TObjId algorithm
    char algorithm[254];
    // Двоичный - ASN1T_AlgorithmIdentifier.ASN1TOpenType parameters
    CACMPT_BLOB parameters;

private:
    void assign( const CACMPT_AlgorithmIdentifier &src )
    {
	::strncpy(algorithm, src.algorithm, sizeof(algorithm));
	parameters = src.parameters;
    }
};

typedef std::string CACMPT_OID;

typedef char	CACMPT_SERIAL [128];

struct CACMPT_BigInteger : public CACMPT_BLOB
{
public:
    CACMPT_BigInteger() : CACMPT_BLOB() { }
    CACMPT_BigInteger( const unsigned char *ptr, unsigned long l ) 
	: CACMPT_BLOB() { assign( ptr, l );  }
    CACMPT_BigInteger( const CACMPT_BigInteger &src )
	: CACMPT_BLOB() { assign( src.pbData, src.cbData );  }
    CACMPT_BigInteger( unsigned long l ) 
	: CACMPT_BLOB(l) { }

    void increment( int start);
    CACMPT_BigInteger& operator++();
    CACMPT_BigInteger operator++(int);
};

#endif // __ASN1_BLOB_H
