# coding: utf-8
from __future__ import unicode_literals, division, absolute_import, print_function

from .._ffi import FFIEngineError, new, null, unwrap

try:
    from ._cng_cffi import bcrypt
except (FFIEngineError, ImportError):
    from ._cng_ctypes import bcrypt


__all__ = [
    'bcrypt',
    'BcryptConst',
    'close_alg_handle',
    'handle_error',
    'open_alg_handle',
]


def open_alg_handle(constant, flags=0):
    handle_pointer = new(bcrypt, 'BCRYPT_ALG_HANDLE *')
    res = bcrypt.BCryptOpenAlgorithmProvider(handle_pointer, constant, null(), flags)
    handle_error(res)

    return unwrap(handle_pointer)


def close_alg_handle(handle):
    res = bcrypt.BCryptCloseAlgorithmProvider(handle, 0)
    handle_error(res)


def handle_error(error_num):
    """
    Extracts the last Windows error message into a python unicode string

    :param error_num:
        The number to get the error string for

    :return:
        A unicode string error message
    """

    if error_num == 0:
        return

    messages = {
        BcryptConst.STATUS_NOT_FOUND: 'The object was not found',
        BcryptConst.STATUS_INVALID_PARAMETER: 'An invalid parameter was passed to a service or function',
        BcryptConst.STATUS_NO_MEMORY: (
            'Not enough virtual memory or paging file quota is available to complete the specified operation'
        ),
        BcryptConst.STATUS_INVALID_HANDLE: 'An invalid HANDLE was specified',
        BcryptConst.STATUS_INVALID_SIGNATURE: 'The cryptographic signature is invalid',
        BcryptConst.STATUS_NOT_SUPPORTED: 'The request is not supported',
        BcryptConst.STATUS_BUFFER_TOO_SMALL: 'The buffer is too small to contain the entry',
        BcryptConst.STATUS_INVALID_BUFFER_SIZE: 'The size of the buffer is invalid for the specified operation',
    }

    output = 'NTSTATUS error 0x%0.2X' % error_num

    if error_num is not None and error_num in messages:
        output += ': ' + messages[error_num]

    raise OSError(output)


class BcryptConst():
    BCRYPT_RNG_ALGORITHM = 'RNG'

    BCRYPT_KEY_LENGTH = 'KeyLength'
    BCRYPT_EFFECTIVE_KEY_LENGTH = 'EffectiveKeyLength'

    BCRYPT_RSAPRIVATE_BLOB = 'RSAPRIVATEBLOB'
    BCRYPT_RSAFULLPRIVATE_BLOB = 'RSAFULLPRIVATEBLOB'
    BCRYPT_RSAPUBLIC_BLOB = 'RSAPUBLICBLOB'
    BCRYPT_DSA_PRIVATE_BLOB = 'DSAPRIVATEBLOB'
    BCRYPT_DSA_PUBLIC_BLOB = 'DSAPUBLICBLOB'
    BCRYPT_ECCPRIVATE_BLOB = 'ECCPRIVATEBLOB'
    BCRYPT_ECCPUBLIC_BLOB = 'ECCPUBLICBLOB'

    BCRYPT_RSAPUBLIC_MAGIC = 0x31415352
    BCRYPT_RSAPRIVATE_MAGIC = 0x32415352
    BCRYPT_RSAFULLPRIVATE_MAGIC = 0x33415352

    BCRYPT_DSA_PUBLIC_MAGIC = 0x42505344
    BCRYPT_DSA_PRIVATE_MAGIC = 0x56505344
    BCRYPT_DSA_PUBLIC_MAGIC_V2 = 0x32425044
    BCRYPT_DSA_PRIVATE_MAGIC_V2 = 0x32565044

    DSA_HASH_ALGORITHM_SHA1 = 0
    DSA_HASH_ALGORITHM_SHA256 = 1
    DSA_HASH_ALGORITHM_SHA512 = 2

    DSA_FIPS186_2 = 0
    DSA_FIPS186_3 = 1

    BCRYPT_NO_KEY_VALIDATION = 8

    BCRYPT_ECDSA_PUBLIC_P256_MAGIC = 0x31534345
    BCRYPT_ECDSA_PRIVATE_P256_MAGIC = 0x32534345
    BCRYPT_ECDSA_PUBLIC_P384_MAGIC = 0x33534345
    BCRYPT_ECDSA_PRIVATE_P384_MAGIC = 0x34534345
    BCRYPT_ECDSA_PUBLIC_P521_MAGIC = 0x35534345
    BCRYPT_ECDSA_PRIVATE_P521_MAGIC = 0x36534345

    STATUS_SUCCESS = 0x00000000
    STATUS_NOT_FOUND = 0xC0000225
    STATUS_INVALID_PARAMETER = 0xC000000D
    STATUS_NO_MEMORY = 0xC0000017
    STATUS_INVALID_HANDLE = 0xC0000008
    STATUS_INVALID_SIGNATURE = 0xC000A000
    STATUS_NOT_SUPPORTED = 0xC00000BB
    STATUS_BUFFER_TOO_SMALL = 0xC0000023
    STATUS_INVALID_BUFFER_SIZE = 0xC0000206

    BCRYPT_KEY_DATA_BLOB_MAGIC = 0x4d42444b
    BCRYPT_KEY_DATA_BLOB_VERSION1 = 0x00000001
    BCRYPT_KEY_DATA_BLOB = 'KeyDataBlob'

    BCRYPT_PAD_PKCS1 = 0x00000002
    BCRYPT_PAD_OAEP = 0x00000004
    BCRYPT_PAD_PSS = 0x00000008

    BCRYPT_3DES_ALGORITHM = '3DES'
    BCRYPT_3DES_112_ALGORITHM = '3DES_112'
    BCRYPT_AES_ALGORITHM = 'AES'
    BCRYPT_DES_ALGORITHM = 'DES'
    BCRYPT_RC2_ALGORITHM = 'RC2'
    BCRYPT_RC4_ALGORITHM = 'RC4'

    BCRYPT_DSA_ALGORITHM = 'DSA'
    BCRYPT_ECDSA_P256_ALGORITHM = 'ECDSA_P256'
    BCRYPT_ECDSA_P384_ALGORITHM = 'ECDSA_P384'
    BCRYPT_ECDSA_P521_ALGORITHM = 'ECDSA_P521'
    BCRYPT_RSA_ALGORITHM = 'RSA'

    BCRYPT_MD5_ALGORITHM = 'MD5'
    BCRYPT_SHA1_ALGORITHM = 'SHA1'
    BCRYPT_SHA256_ALGORITHM = 'SHA256'
    BCRYPT_SHA384_ALGORITHM = 'SHA384'
    BCRYPT_SHA512_ALGORITHM = 'SHA512'

    BCRYPT_ALG_HANDLE_HMAC_FLAG = 0x00000008

    BCRYPT_BLOCK_PADDING = 0x00000001
