# Copyright (c) 2016, Matt Layman


class Line(object):
    """Base type for TAP data.

    TAP is a line based protocol. Thus, the most primitive type is a line.
    """
    @property
    def category(self):
        raise NotImplementedError


class Result(Line):
    """Information about an individual test line."""

    def __init__(
            self, ok, number=None, description='', directive=None,
            diagnostics=None):
        self._ok = ok
        if number:
            self._number = int(number)
        else:
            # The number may be an empty string so explicitly set to None.
            self._number = None
        self._description = description
        self.directive = directive
        self.diagnostics = diagnostics

    @property
    def category(self):
        """:returns: ``test``"""
        return 'test'

    @property
    def ok(self):
        """Get the ok status.

        :rtype: bool
        """
        return self._ok

    @property
    def number(self):
        """Get the test number.

        :rtype: int
        """
        return self._number

    @property
    def description(self):
        """Get the description."""
        return self._description

    @property
    def skip(self):
        """Check if this test was skipped.

        :rtype: bool
        """
        return self.directive.skip

    @property
    def todo(self):
        """Check if this test was a TODO.

        :rtype: bool
        """
        return self.directive.todo

    def __str__(self):
        is_not = ''
        if not self.ok:
            is_not = 'not '
        directive = ''
        if self.directive is not None and self.directive.text:
            directive = ' # {0}'.format(self.directive.text)
        diagnostics = ''
        if self.diagnostics is not None:
            diagnostics = '\n' + self.diagnostics.rstrip()
        return "{0}ok {1} - {2}{3}{4}".format(
            is_not, self.number, self.description, directive, diagnostics)


class Plan(Line):
    """A plan line to indicate how many tests to expect."""

    def __init__(self, expected_tests, directive=None):
        self._expected_tests = expected_tests
        self.directive = directive

    @property
    def category(self):
        """:returns: ``plan``"""
        return 'plan'

    @property
    def expected_tests(self):
        """Get the number of expected tests.

        :rtype: int
        """
        return self._expected_tests

    @property
    def skip(self):
        """Check if this plan should skip the file.

        :rtype: bool
        """
        return self.directive.skip


class Diagnostic(Line):
    """A diagnostic line (i.e. anything starting with a hash)."""

    def __init__(self, text):
        self._text = text

    @property
    def category(self):
        """:returns: ``diagnostic``"""
        return 'diagnostic'

    @property
    def text(self):
        """Get the text."""
        return self._text


class Bail(Line):
    """A bail out line (i.e. anything starting with 'Bail out!')."""

    def __init__(self, reason):
        self._reason = reason

    @property
    def category(self):
        """:returns: ``bail``"""
        return 'bail'

    @property
    def reason(self):
        """Get the reason."""
        return self._reason


class Version(Line):
    """A version line (i.e. of the form 'TAP version 13')."""

    def __init__(self, version):
        self._version = version

    @property
    def category(self):
        """:returns: ``version``"""
        return 'version'

    @property
    def version(self):
        """Get the version number.

        :rtype: int
        """
        return self._version


class Unknown(Line):
    """A line that represents something that is not a known TAP line.

    This exists for the purpose of a Null Object pattern.
    """
    @property
    def category(self):
        """:returns: ``unknown``"""
        return 'unknown'
