import os
import re


def CHECK_ALL( *checks ):
    def check_all( value, name=None, context=None ):
        for check in checks:
            check( value, name, context )

    return check_all

CHECK_AND = CHECK_ALL


def CHECK_ONE( *checks ):
    def check_one( value, name=None, context=None ):
        errors = []
        for check in checks:
            try:
                check( value, name, context )
                break
            except Exception as error:
                errors.append( str(error) )

        if len(checks) > 0 and len(errors) == len(checks):
            raise AssertionError( ' OR '.join(errors) )

    return check_one

CHECK_OR = CHECK_ONE


def DEFINE_CHECK( check, message ):
    def define_check( value, name=None, context=None ):
        if not check( value, name, context ):
            raise AssertionError(("{}'{}' {}").format(
                "" if name is None else ("'{}': ").format(name),
                value,
                message
                ))

    return define_check

# for test purpose ...
#
CHECK_NEVER_FAILS = CHECK_NOTHING = DEFINE_CHECK(
    lambda v,n,c: True,
    "never fails"
    )
CHECK_ALLWAYS_FAILS = DEFINE_CHECK(
    lambda v,n,c: False,
    "allways fails"
    )

# value checks ...
#
CHECK_NOT_NONE = DEFINE_CHECK(
    lambda v,n,c: v is not None,
    "must not none"
    )
CHECK_IS_NONE = DEFINE_CHECK(
    lambda v,n,c: v is None,
    "is not none"
    )

CHECK_IS_EMPTY = CHECK_NOT_DEFINED = DEFINE_CHECK(
    lambda v,n,c: v is None or (hasattr(v,'__len__') and len(v) == 0),
    "is not defined"
    )
CHECK_NOT_EMPTY = CHECK_IS_DEFINED = DEFINE_CHECK(
    lambda v,n,c: v is not None and (not hasattr(v,'__len__') or len(v) > 0),
    "is not defined"
    )

CHECK_IS_FILE = DEFINE_CHECK(
    lambda v,n,c: v is not None and os.path.isfile( v ),
    "is not a file"
    )
CHECK_IS_DIRECTORY = DEFINE_CHECK(
    lambda v,n,c: v is not None and os.path.isdir( v ),
    "is not a directory"
    )

def CHECK_IS( *typelist ):
    return DEFINE_CHECK(
        lambda v,n,c: v is not None and isinstance( v, tuple( t for t in typelist )),
        ("is not {}").format( '|'.join( list( t.__name__ for t in typelist )))
        )

def CHECK_LT( threshold ):
    return DEFINE_CHECK(
        lambda v,n,c: v < threshold,
        ("is not less than '{}'" ).format(threshold)
        )
def CHECK_LE( threshold ):
    return DEFINE_CHECK(
        lambda v,n,c: v <= threshold,
        ("is not less or equal than {}" ).format(threshold)
        )
def CHECK_EQ( threshold ):
    return DEFINE_CHECK(
        lambda v,n,c: v == threshold,
        ("is not equal to {}" ).format(threshold)
        )
def CHECK_GE( threshold ):
    return DEFINE_CHECK(
        lambda v,n,c: v >= threshold,
        ("is not greater or equal than {}" ).format(threshold)
        )
def CHECK_GT( threshold ):
    return DEFINE_CHECK(
        lambda v,n,c: v > threshold,
        ("is not greater than {}" ).format(threshold)
        )

def CHECK_REGX( pattern, message=None ):
    return DEFINE_CHECK(
        lambda v,n,c: v is not None and re.match( pattern, v ),
        ("does not match '{}'").format( str(pattern) if message is None else message )
        )

CHECK_MATCH = CHECK_REGX

# context checks ...
#
def CHECK_IS_SET(name):
    return DEFINE_CHECK(
        lambda v,n,c: name in c and c[name] is not None,
        ("'{}' is not defined").format(name)
        )
def CHECK_NOT_SET(name):
    return DEFINE_CHECK(
        lambda v,n,c: (name not in c or c[name] is None) ,
        ("must not defined with '{}'").format(name)
        )

def CHECK_WITH( name, check ):
    def check_with( v, n, c ):
        check( c[name] if c is not None and name in c else None, name, c )
    return check_with
