#ifndef __ASN1_UTIL_H
#define __ASN1_UTIL_H
#undef min
#undef max

#include "PKIXCMP.h"
#include "ASN1Blob.h"
#ifdef _WIN32
#include <windows.h>
#include <WinCrypt.h>
#else
#include "CSP_WinCrypt.h"
#endif	/* _WIN32 */

#include <iostream>

using namespace asn1data;
class ctxt_handle
{
public:
    ctxt_handle() {}
    ~ctxt_handle() { ::rtFreeContext(&ctxt_); }

    bool init() { return ::rtInitContext(&ctxt_, NULL) == ASN_OK; }
    ASN1CTXT* get() { return &ctxt_; }
private:
    ctxt_handle( const ctxt_handle&);
    ctxt_handle& operator=( const ctxt_handle&);

    ASN1CTXT ctxt_;
};

std::string strip_white_space( const std::string &src );

void str1cpy( char *dest, const ASN1OBJID &src, size_t len, const char*text );

int str2oid( const char *str, ASN1OBJID *oid );
ASN1OBJID str2oid( const char* str);

int oid2str( std::string& str, const ASN1OBJID *oid );

ASN1T_AlgorithmIdentifier &
ASN1T_AlgorithmIdentifier_set (
    ASN1CTXT* pctxt,
    const char * oid,
    const BYTE * pbParameters = 0,
    DWORD cbParameters = 0,
    DWORD dwGroupId = 0
    );

ASN1T_AlgorithmIdentifier &
ASN1T_AlgorithmIdentifier_set (
    ASN1CTXT* pctxt,
    const char * oid,
    const CACMPT_BLOB * parameters
    );

void
ASN1T_AlgorithmIdentifier_get (
    CACMPT_AlgorithmIdentifier& dest,
    const ASN1T_AlgorithmIdentifier& src
    );

ASN1T_AlgorithmIdentifier&
ASN1T_AlgorithmIdentifier_set (
    ASN1CTXT* pctxt,
    HCRYPTPROV hProv,
    DWORD dwKeySpec
    );

/* мутный набор, боязно причёсывать */
std::string SmallBitStringToString( const unsigned *set, 
    const char **strings, size_t length, unsigned src );
std::wstring SmallBitStringToWString( const unsigned *set,
    const wchar_t **strings, size_t length, unsigned src );
unsigned SmallBitStringFromString( const unsigned *set, 
    const char **strings, size_t length, const char *str,
    const char *zero = NULL );

/* и этих боязно */
std::string tostring( const wchar_t* );
std::wstring towstring( const char* );
inline std::string tostring( const std::wstring &src )
{ return tostring( src.c_str() ); }
inline std::wstring towstring( const std::string &src )
{ return towstring( src.c_str() ); }
std::wstring towstring( int j );
inline std::ostream& operator<<( std::ostream &o, const wchar_t *src )
{ return o << tostring( src ).c_str(); }

void fillIssuerAndSerialNumber(
    ASN1CTXT* pctxt,
    ASN1T_IssuerAndSerialNumber** ppIssuerAndSerialNumber,
    const CERT_NAME_BLOB& msIssuer,
    const CRYPT_INTEGER_BLOB& msSerialNumber);

template<class ASN1_T>
ASN1_T* asn1New( ASN1CTXT* pctxt)
{
    ASN1_T* p = ALLOC_ASN1ELEM(pctxt,ASN1_T);
    if(!p)
	throw CA_MEMORY_EXCEPTION;
    return p;
}

template<class ASN1_T>
ASN1_T* asn1NewArray( ASN1CTXT* pctxt, size_t size)
{
    ASN1_T* p = ALLOC_ASN1ARRAY2(pctxt, size, ASN1_T);
    if(!p)
	throw CA_MEMORY_EXCEPTION;
    return p;
}

// полезно переписать все шесть
void ASN1T_BigInt_set(CACMPT_SERIAL szSerial, ASN1OCTET* buf, int len);
void ASN1T_BigInt_get(const CACMPT_SERIAL szSerial, ASN1OCTET* buf, int len);
void ASN1T_BigInt_inc(ASN1OCTET* buf, int len, int start = 0);

CACMPT_BigInteger ASN1StringToBigInteger(const ASN1ConstCharPtr strBigInt);
ASN1ConstCharPtr BigIntegerToASN1String(ASN1CTXT* pctxt, const CACMPT_BigInteger& blob);
ASN1ConstCharPtr FiletimeToASN1GeneralizedTime(ASN1CTXT* pctxt, const FILETIME& ftime, bool secondPrecision = true);
FILETIME ASN1GeneralizedTimeToFiletime(const ASN1ConstCharPtr asn1_date);

// 2017-may-24 dim: пока не придумаю, где будет жить, пусть здесь побудет
static inline DWORD StrType(DWORD type)
{
    return type & 0x0000ffff;
}

#include "compiler_attributes.h"
CP_FORCE_INLINE bool operator !=(const DATA_BLOB &l, const DATA_BLOB &r)
{
    if(l.cbData != r.cbData)
	return true;
    if(!l.cbData)
	return false;

    return ::memcmp(l.pbData, r.pbData, r.cbData);
}

CP_FORCE_INLINE bool neq_pdatab(const DATA_BLOB *l, const DATA_BLOB *r)
{
    if(l == r)
	return false;
    if(l == NULL || r == NULL)
	return true;
    return *l != *r;
}
#endif // __ASN1_UTIL_H
