# Copyright 2019 Katteli Inc.
# TestFlows.com Open-Source Software Testing Framework (http://testflows.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# to the end flag
TE = 1 << 0
# utility test flag
UT = 1 << 1
# skip flag
SKIP = 1 << 2
# expected OK
EOK = 1 << 3
# expected Fail
EFAIL = 1 << 4
# expected Error
EERROR = 1 << 5
# expected Skip
ESKIP = 1 << 6
# crossed out ok
XOK = 1 << 7
# crossed out fail
XFAIL = 1 << 8
# crossed out error
XERROR = 1 << 9
# crossed out null
XNULL = 1 << 10
# not counted fail
FAIL_NOT_COUNTED = 1 << 11
# not counted error
ERROR_NOT_COUNTED = 1 << 12
# not counted null
NULL_NOT_COUNTED = 1 << 13
# pause before test execution
PAUSE_BEFORE = 1 << 14
# pause before short form
PAUSE = PAUSE_BEFORE
# pause after test execution
PAUSE_AFTER = 1 << 15
# manual test
MANUAL = 1 << 16
# auto test
AUTO = 1 << 17
# report flag
REPORT = 1 << 26
# documentation
DOCUMENT = 1 << 27
# mandatory test
MANDATORY = 1 << 28
# parallel test
PARALLEL = 1 << 29
# disable parallel tests
NO_PARALLEL = 1 << 30
# clear flags mask
CLEAR = 1 << 31
# async test
ASYNC = 1 << 32
# repeated test flag
REPEATED = 1 << 33
# not repeatable test
NOT_REPEATABLE = 1 << 34
# retried test flag
RETRIED = 1 << 35
# last retry flag
LAST_RETRY = 1 << 36
# pause on passing result
PAUSE_ON_PASS = 1 << 37
# pause on failing result
PAUSE_ON_FAIL = 1 << 38
# remote test
REMOTE = 1 << 39
# expected result
ERESULT = EOK | EFAIL | ESKIP | EERROR
# expected any result
EANY = EOK | EFAIL | ESKIP
# crossed out result
XRESULT = XOK | XFAIL | XERROR | XNULL
# not counted
NOT_COUNTED = FAIL_NOT_COUNTED
NOT_COUNTED_ANY = FAIL_NOT_COUNTED | ERROR_NOT_COUNTED | NULL_NOT_COUNTED
# cumulative flags
CFLAGS = UT | MANDATORY | MANUAL | PARALLEL | NO_PARALLEL | NOT_REPEATABLE | RETRIED

class Flags(object):
    """Test flags."""
    all = [
            TE, UT, SKIP, EOK, EFAIL, EERROR, ESKIP,
            XOK, XFAIL, XERROR, XNULL,
            FAIL_NOT_COUNTED, ERROR_NOT_COUNTED, NULL_NOT_COUNTED,
            PAUSE_BEFORE, PAUSE_AFTER, MANUAL, AUTO,
            REPORT, DOCUMENT, MANDATORY, PARALLEL, NO_PARALLEL, CLEAR,
            ASYNC, REPEATED, NOT_REPEATABLE, RETRIED, LAST_RETRY,
            PAUSE_ON_PASS, PAUSE_ON_FAIL, REMOTE
        ]
    all_str = [
            "TE", "UT", "SKIP", "EOK", "EFAIL", "EERROR", "ESKIP",
            "XOK", "XFAIL", "XERROR", "XNULL",
            "FAIL_NOT_COUNTED", "ERROR_NOT_COUNTED", "NULL_NOT_COUNTED",
            "PAUSE_BEFORE", "PAUSE_AFTER", "MANUAL", "AUTO",
            "REPORT", "DOCUMENT", "MANDATORY", "PARALLEL", "NO_PARALLEL", "CLEAR",
            "ASYNC", "REPEATED", "NOT_REPEATABLE", "RETRIED", "LAST_RETRY",
            "PAUSE_ON_PASS", "PAUSE_ON_FAIL", "REMOTE"
        ]

    def __init__(self, flags=0):
        if flags is None:
            flags = 0
        if type(flags) is str:
            self.flags = 0
            if flags:
                for flag in [self.all[self.all_str.index(f)] for f in flags.split("|")]:
                    self.flags |= flag
        else:
            self.flags = int(flags)

    def __str__(self):
        l = [self.all_str[self.all.index(f)] for f in self.all if self.flags & f == f]
        return "|".join(l) or ""

    def __repr__(self):
        return "Flags('" + str(self) + "')"

    def __bool__(self):
        return bool(self.flags)

    def __int__(self):
        return self.flags

    def __and__(self, o):
        return Flags(self.flags & int(Flags(o)))

    def __rand__(self, o):
        return self.__and__(o)

    def __or__(self, o):
        return Flags(self.flags | int(Flags(o)))

    def __ror__(self, o):
        return self.__or__(o)

    def __xor__(self, o):
        return Flags(self.flags ^ int(Flags(o)))

    def __rxor__(self, o):
        return self.__xor__(o)

    def __invert__(self):
        return Flags(~self.flags)

    def __contains__(self, o):
        return bool(self & o)

    def __eq__(self, o):
        return self.flags == int(Flags(o))

    def __ne__(self, o):
        return not self == o

    def keys(self):
        return ["flags"]

    def __getitem__(self, key):
        if key == "flags":
            return self
        raise KeyError(key)

    def __call__(self, func):
        setattr(func, "flags", self)
        return func
