# -*- coding: UTF-8 -*-

WORD_BYTE_SIZE = 4
MAGIC_NUMBER = 0x07230203
WORD_COUNT_HEADER = 5


class DataType(object):
    # BOOL = "bool"  # machine unit 1 byte? (probably not 1 bit)
    INT = "int"
    UINT = "uint"
    FLOAT = "float"
    DOUBLE = "double"

    ALL = [INT, UINT, FLOAT, DOUBLE]


class Layout(object):
    STD140 = "std140"
    STD430 = "std430"
    STDXXX = "stdxxx"  # can not be inferred / does not matter


class Order(object):
    COLUMN_MAJOR = "column_major"
    ROW_MAJOR = "row_major"


class Access(object):
    READ_ONLY = "read_only"
    WRITE_ONLY = "write_only"
    READ_WRITE = "read_write"
    NEITHER = "neither"  # only "metadata" can be accessed, e.g. length of an array


class SpirvEnum(object):

    _MAP = ()
    _UNKNOWN = "UNKNOWN"

    @classmethod
    def map(cls, key, keys, values, default=None):
        if key in keys:
            return values[keys.index(key)]
        if default:
            return default
        raise RuntimeError("Could not map {}".format(key))

    @classmethod
    def to_spirv(cls, key):
        keys, values = zip(*cls._MAP)
        return cls.map(key, keys, values)

    @classmethod
    def from_spirv(cls, key):
        values, keys = zip(*cls._MAP)
        return cls.map(key, keys, values, default=cls._UNKNOWN)

    @classmethod
    def keys(cls):
        return list(zip(*cls._MAP))[0]


class SourceLanguage(SpirvEnum):

    UNKNOWN = "UNKNOWN"
    ESSL = "ESSL"
    GLSL = "GLSL"
    OPEN_CL_C = "OPEN_CL_C"
    OPEN_CL_CPP = "OPEN_CL_CPP"
    HLSL = "HLSL"

    _MAP = (
        (UNKNOWN, 0), (ESSL, 1), (GLSL, 2), (OPEN_CL_C, 3), (OPEN_CL_CPP, 4), (HLSL, 5),
    )


class ExecutionModel(SpirvEnum):

    VERTEX = "VERTEX"
    TESSELATION_CONTROL = "TESSELATION_CONTROL"
    TESSELATION_EVALUATION = "TESSELATION_EVALUATION"
    GEOMETRY = "GEOMETRY"
    FRAGMENT = "FRAGMENT"
    GL_COMPUTE = "GL_COMPUTE"
    KERNEL = "KERNEL"

    _MAP = (
        (VERTEX, 0), (TESSELATION_CONTROL, 1), (TESSELATION_EVALUATION, 2), (GEOMETRY, 3), (FRAGMENT, 4),
        (GL_COMPUTE, 5), (KERNEL, 6)
    )


class ExecutionMode(SpirvEnum):

    INVOCATIONS = "INVOCATIONS"
    SPACING_EQUAL = "SPACING_EQUAL"
    SPACING_FRACTIONAL_EVEN = "SPACING_FRACTIONAL_EVEN"
    SPACING_FRACTIONAL_ODD = "SPACING_FRACTIONAL_ODD"
    VERTEX_ORDER_CW = "VERTEX_ORDER_CW"
    VERTEX_ORDER_CCW = "VERTEX_ORDER_CCW"
    PIXEL_CENTER_INTEGER = "PIXEL_CENTER_INTEGER"
    ORIGIN_UPPER_LEFT = "ORIGIN_UPPER_LEFT"
    ORIGIN_LOWER_LEFT = "ORIGIN_LOWER_LEFT"
    EARLY_FRAGMENT_TESTS = "EARLY_FRAGMENT_TESTS"
    POINT_MODE = "POINT_MODE"
    XFB = "XFB"
    DEPTH_REPLACING = "DEPTH_REPLACING"
    DEPTH_GREATER = "DEPTH_GREATER"
    DEPTH_LESS = "DEPTH_LESS"
    DEPTH_UNCHANGED = "DEPTH_UNCHANGED"
    LOCAL_SIZE = "LOCAL_SIZE"
    LOCAL_SIZE_HINT = "LOCAL_SIZE_HINT"
    INPUT_POINTS = "INPUT_POINTS"
    INPUT_LINES = "INPUT_LINES"
    INPUT_LINES_ADJACENCY = "INPUT_LINES_ADJACENCY"
    TRIANGLES = "TRIANGLES"
    INPUT_TRIANGLES_ADJACENCY = "INPUT_TRIANGLES_ADJACENCY"
    QUADS = "QUADS"
    ISOLINES = "ISOLINES"
    OUTPUT_VERTICES = "OUTPUT_VERTICES"
    OUTPUT_POINTS = "OUTPUT_POINTS"
    OUTPUT_LINE_STRIP = "OUTPUT_LINE_STRIP"
    OUTPUT_TRIANGLE_STRIP = "OUTPUT_TRIANGLE_STRIP"
    VEC_TYPE_HINT = "VEC_TYPE_HINT"
    CONTRACTION_OFF = "CONTRACTION_OFF"
    INITIALIZER = "INITIALIZER"
    FINALIZER = "FINALIZER"
    SUBGROUP_SIZE = "SUBGROUP_SIZE"
    SUBGROUPS_PER_WORKGROUP = "SUBGROUPS_PER_WORKGROUP"
    SUBGROUPS_PER_WORKGROUP_ID = "SUBGROUPS_PER_WORKGROUP_ID"
    LOCAL_SIZE_ID = "LOCAL_SIZE_ID"
    LOCAL_SIZE_HINT_ID = "LOCAL_SIZE_HINT_ID"
    POST_DEPTH_COVERAGE = "POST_DEPTH_COVERAGE"
    STENCIL_REF_REPLACING_EXT = "STENCIL_REF_REPLACING_EXT"

    _MAP = (
        (INVOCATIONS, 0), (SPACING_EQUAL, 1), (SPACING_FRACTIONAL_EVEN, 2), (SPACING_FRACTIONAL_ODD, 3),
        (VERTEX_ORDER_CW, 4), (VERTEX_ORDER_CCW, 5), (PIXEL_CENTER_INTEGER, 6), (ORIGIN_UPPER_LEFT, 7),
        (ORIGIN_LOWER_LEFT, 8), (EARLY_FRAGMENT_TESTS, 9), (POINT_MODE, 10), (XFB, 11), (DEPTH_REPLACING, 12),
        (DEPTH_GREATER, 14), (DEPTH_LESS, 15), (DEPTH_UNCHANGED, 16), (LOCAL_SIZE, 17), (LOCAL_SIZE_HINT, 18),
        (INPUT_POINTS, 19), (INPUT_LINES, 20), (INPUT_LINES_ADJACENCY, 21), (TRIANGLES, 22),
        (INPUT_TRIANGLES_ADJACENCY, 23), (QUADS, 24), (ISOLINES, 25), (OUTPUT_VERTICES, 26), (OUTPUT_POINTS, 27),
        (OUTPUT_LINE_STRIP, 28), (OUTPUT_TRIANGLE_STRIP, 29), (VEC_TYPE_HINT, 30), (CONTRACTION_OFF, 31),
        (INITIALIZER, 33), (FINALIZER, 34), (SUBGROUP_SIZE, 35), (SUBGROUPS_PER_WORKGROUP, 36),
        (SUBGROUPS_PER_WORKGROUP_ID, 37), (LOCAL_SIZE_ID, 38), (LOCAL_SIZE_HINT_ID, 39),
        (POST_DEPTH_COVERAGE, 4446), (STENCIL_REF_REPLACING_EXT, 5027),
    )


class Decoration(SpirvEnum):
    # https://www.khronos.org/registry/spir-v/specs/1.2/SPIRV.pdf#Decoration

    RELAXED_PRECISION = "RELAXED_PRECISION"
    SPEC_ID = "SPEC_ID"
    BLOCK = "BLOCK"
    BUFFER_BLOCK = "BUFFER_BLOCK"
    ROW_MAJOR = "ROW_MAJOR"
    COL_MAJOR = "COL_MAJOR"
    ARRAY_STRIDE = "ARRAY_STRIDE"
    MATRIX_STRIDE = "MATRIX_STRIDE"
    GLSL_SHARED = "GLSL_SHARED"
    GLSL_PACKED = "GLSL_PACKED"
    C_PACKED = "C_PACKED"
    BUILT_IN = "BUILT_IN"
    NO_PERSPECTIVE = "NO_PERSPECTIVE"
    FLAT = "FLAT"
    PATCH = "PATCH"
    CENTROID = "CENTROID"
    SAMPLE = "SAMPLE"
    INVARIANT = "INVARIANT"
    RESTRICT = "RESTRICT"
    ALIASED = "ALIASED"
    VOLATILE = "VOLATILE"
    CONSTANT = "CONSTANT"
    COHERENT = "COHERENT"
    NON_WRITABLE = "NON_WRITABLE"
    NON_READABLE = "NON_READABLE"
    UNIFORM = "UNIFORM"
    SATURATED_CONVERSION = "SATURATED_CONVERSION"
    STREAM = "STREAM"
    LOCATION = "LOCATION"
    COMPONENT = "COMPONENT"
    INDEX = "INDEX"
    BINDING = "BINDING"
    DESCRIPTOR_SET = "DESCRIPTOR_SET"
    OFFSET = "OFFSET"
    XFB_BUFFER = "XFB_BUFFER"
    XFB_STRIDE = "XFB_STRIDE"
    FUNC_PARAM_ATTR = "FUNC_PARAM_ATTR"
    FP_ROUNDING_MODE = "FP_ROUNDING_MODE"
    FP_FAST_MATH_MODE = "FP_FAST_MATH_MODE"
    LINKAGE_ATTRIBUTES = "LINKAGE_ATTRIBUTES"
    NO_CONTRACTION = "NO_CONTRACTION"
    INPUT_ATTACHMENT_INDEX = "INPUT_ATTACHMENT_INDEX"
    ALIGNMENT = "ALIGNMENT"
    MAX_BYTE_OFFSET = "MAX_BYTE_OFFSET"
    ALIGNMENT_ID = "ALIGNMENT_ID"
    MAX_BYTE_OFFSET_ID = "MAX_BYTE_OFFSET_ID"
    EXPLICIT_INTERP_AMD = "EXPLICIT_INTERP_AMD"
    OVERRIDE_COVERAGE_NV = "OVERRIDE_COVERAGE_NV"
    PASSTHROUGH_NV = "PASSTHROUGH_NV"
    VIEWPORT_RELATIVE_NV = "VIEWPORT_RELATIVE_NV"
    SECONDARY_VIEWPORT_RELATIVE_NV = "SECONDARY_VIEWPORT_RELATIVE_NV"
    HLSL_COUNTER_BUFFER_GOOGLE = "HLSL_COUNTER_BUFFER_GOOGLE"
    HLSL_SEMANTIC_GOOGLE = "HLSL_SEMANTIC_GOOGLE"

    _MAP = (
        (RELAXED_PRECISION, 0), (SPEC_ID, 1), (BLOCK, 2), (BUFFER_BLOCK, 3), (ROW_MAJOR, 4), (COL_MAJOR, 5),
        (ARRAY_STRIDE, 6), (MATRIX_STRIDE, 7), (GLSL_SHARED, 8), (GLSL_PACKED, 9), (C_PACKED, 10), (BUILT_IN, 11),
        (NO_PERSPECTIVE, 13), (FLAT, 14), (PATCH, 15), (CENTROID, 16), (SAMPLE, 17), (INVARIANT, 18),
        (RESTRICT, 19), (ALIASED, 20), (VOLATILE, 21), (CONSTANT, 22), (COHERENT, 23), (NON_WRITABLE, 24),
        (NON_READABLE, 25), (UNIFORM, 26), (SATURATED_CONVERSION, 28), (STREAM, 29), (LOCATION, 30),
        (COMPONENT, 31), (INDEX, 32), (BINDING, 33), (DESCRIPTOR_SET, 34), (OFFSET, 35), (XFB_BUFFER, 36),
        (XFB_STRIDE, 37), (FUNC_PARAM_ATTR, 38), (FP_ROUNDING_MODE, 39), (FP_FAST_MATH_MODE, 40),
        (LINKAGE_ATTRIBUTES, 41), (NO_CONTRACTION, 42), (INPUT_ATTACHMENT_INDEX, 43), (ALIGNMENT, 44),
        (MAX_BYTE_OFFSET, 45), (ALIGNMENT_ID, 46), (MAX_BYTE_OFFSET_ID, 47), (EXPLICIT_INTERP_AMD, 4999),
        (OVERRIDE_COVERAGE_NV, 5248), (PASSTHROUGH_NV, 5250), (VIEWPORT_RELATIVE_NV, 5252),
        (SECONDARY_VIEWPORT_RELATIVE_NV, 5256), (HLSL_COUNTER_BUFFER_GOOGLE, 5634), (HLSL_SEMANTIC_GOOGLE, 5635),
    )


class StorageClass(SpirvEnum):
    # https://www.khronos.org/registry/spir-v/specs/1.2/SPIRV.pdf#subsection.3.7

    UNIFORM_CONSTANT = "UNIFORM_CONSTANT"
    INPUT = "INPUT"
    UNIFORM = "UNIFORM"
    OUTPUT = "OUTPUT"
    WORKGROUP = "WORKGROUP"
    CROSS_WORKGROUP = "CROSS_WORKGROUP"
    PRIVATE = "PRIVATE"
    FUNCTION = "FUNCTION"
    GENERIC = "GENERIC"
    PUSH_CONSTANT = "PUSH_CONSTANT"
    ATOMIC_COUNTER = "ATOMIC_COUNTER"
    IMAGE = "IMAGE"
    STORAGE_BUFFER = "STORAGE_BUFFER"

    _MAP = (
        (UNIFORM_CONSTANT, 0), (INPUT, 1), (UNIFORM, 2), (OUTPUT, 3), (WORKGROUP, 4), (CROSS_WORKGROUP, 5),
        (PRIVATE, 6), (FUNCTION, 7), (GENERIC, 8), (PUSH_CONSTANT, 9), (ATOMIC_COUNTER, 10), (IMAGE, 11),
        (STORAGE_BUFFER, 12),
    )
