import unicodedata
import codecs


def validate_ae_title(ae_title):
    """Return a valid AE title from `ae_title`, if possible.

    An AE title:
    *   Must be no more than 16 characters
    *   Leading and trailing spaces are not significant
    *   The characters should belong to the Default Character Repertoire
        excluding 5CH (backslash "\") and all control characters

    If the supplied `ae_title` is greater than 16 characters once
        non-significant spaces have been removed then the returned AE title
        will be truncated to remove the excess characters.
    If the supplied `ae_title` is less than 16 characters once non-significant
        spaces have been removed, the spare trailing characters will be
        set to space (0x20)

    Parameters
    ----------
    ae_title : str or bytes
        The AE title to check

    Returns
    -------
    str or bytes
        A valid AE title (with the same type as the supplied `ae_title`),
        truncated to 16 characters if necessary. If Python 3 then only returns
        bytes.

    Raises
    ------
    ValueError
        If `ae_title` is an empty string, contains only spaces or contains
        control characters or backslash
    TypeError
        If `ae_title` is not a string or bytes
    """
    try:
        is_bytes = False
        if isinstance(ae_title, bytes):
            is_bytes = True
            ae_title = ae_title.decode('ascii')

        # Remove leading and trailing spaces
        significant_characters = ae_title.strip()

        # Remove trailing nulls (required as AE titles may be padded by nulls)
        #   and common control chars (optional, for convenience)
        significant_characters = significant_characters.rstrip('\0\r\t\n')

        # Check for backslash or control characters
        for char in significant_characters:
            if unicodedata.category(char)[0] == "C" or char == "\\":
                raise ValueError("Invalid value for an AE title; must not "
                                 "contain backslash or control characters")

        # AE title OK
        if 0 < len(significant_characters) <= 16:
            while len(significant_characters) < 16:
                significant_characters += ' '

            if is_bytes:
                return codecs.encode(significant_characters, 'ascii')

            return significant_characters

        # AE title too long : truncate
        elif len(significant_characters.strip()) > 16:
            if is_bytes:
                return codecs.encode(significant_characters[:16], 'ascii')

            return significant_characters[:16]

        # AE title empty str
        else:
            raise ValueError("Invalid value for an AE title; must be a "
                             "non-empty string or bytes.")
    except AttributeError:
        raise TypeError("Invalid value for an AE title; must be a "
                        "non-empty string or bytes.")
    except ValueError:
        raise
    except:
        raise TypeError("Invalid value for an AE title; must be a "
                        "non-empty string or bytes.")