from typing import Optional


def get_input(
    key,  # type: str
    description="",  # type: str
    question="Enter",  # type: str
    required=False,  # type: bool
    default=None,  # type: Optional[str]
    new_line=False,  # type: bool
):
    # type: (...) -> Optional[str]
    if new_line:
        print()
    while True:
        value = input("{} {} {}: ".format(question, key, description))
        if not value.strip() and required:
            print("{} is required".format(key))
        elif not (value.strip() or required):
            return default
        else:
            return value


def input_int(
    key,  # type: str
    description="",  # type: str
    required=False,  # type: bool
    default=None,  # type: Optional[int]
    new_line=False,  # type: bool
):
    # type: (...) -> Optional[int]
    while True:
        try:
            value = int(
                get_input(
                    key,
                    description,
                    required=required,
                    default=default,
                    new_line=new_line,
                )
            )
            return value
        except ValueError:
            print(
                "Invalid input: {} should be a number. Please enter an integer".format(
                    key
                )
            )


def input_bool(question, default=False):
    # type: (str, bool) -> bool
    """
    :param question: string to display
    :param default: default boolean value
    :return: return True if response is 'y'/'yes' 't'/'true' in input.lower()
    """
    while True:
        try:
            response = input("{}: ".format(question)).lower()
            if not response:
                return default
            if response.startswith("y") or response.startswith("t"):
                return True
            if response.startswith("n") or response.startswith("f"):
                return False
            raise ValueError()
        except ValueError:
            print("Invalid input: please enter 'yes' or 'no'")


def input_list(
    key,  # type: str
    description="",  # type: str
    question="Enter",  # type: str
    required=False,  # type: bool
    default=None,  # type: Optional[str]
    new_line=False,  # type: bool
):
    res_list = [get_input(key, description, question, required, default, new_line)]
    while input_bool("\nDefine another {}? [y/N]".format(key)):
        response = get_input(
                    key=key,
                    description=description,
                    question=question,
                    required=False,
                    default=default,
                    new_line=new_line,
                )
        if response:
            res_list.append(response)
    return res_list


def multiline_input(description=""):
    print("{} \nNote: two consecutive empty lines would terminate the input : ".format(description))
    lines = []
    empty_lines = 0
    while empty_lines < 2:
        line = input()
        lines.append(line)
        empty_lines = 0 if line else empty_lines + 1
    res = "\n".join(lines[:-1])
    return res, len(res.splitlines())