"""Debug utilities for EGL operations"""
from OpenGL.EGL import *
import itertools


def eglErrorName(value):
    """Returns error constant if known, otherwise returns value"""
    return KNOWN_ERRORS.get(value, value)


KNOWN_ERRORS = {
    EGL_SUCCESS: EGL_SUCCESS,
    EGL_NOT_INITIALIZED: EGL_NOT_INITIALIZED,
    EGL_BAD_ACCESS: EGL_BAD_ACCESS,
    EGL_BAD_ALLOC: EGL_BAD_ALLOC,
    EGL_BAD_ATTRIBUTE: EGL_BAD_ATTRIBUTE,
    EGL_BAD_CONTEXT: EGL_BAD_CONTEXT,
    EGL_BAD_CONFIG: EGL_BAD_CONFIG,
    EGL_BAD_CURRENT_SURFACE: EGL_BAD_CURRENT_SURFACE,
    EGL_BAD_DISPLAY: EGL_BAD_DISPLAY,
    EGL_BAD_SURFACE: EGL_BAD_SURFACE,
    EGL_BAD_MATCH: EGL_BAD_MATCH,
    EGL_BAD_PARAMETER: EGL_BAD_PARAMETER,
    EGL_BAD_NATIVE_PIXMAP: EGL_BAD_NATIVE_PIXMAP,
    EGL_BAD_NATIVE_WINDOW: EGL_BAD_NATIVE_WINDOW,
    EGL_CONTEXT_LOST: EGL_CONTEXT_LOST,
}


def write_ppm(buf, filename):
    """Write height * width * 3-component buffer as ppm to filename
    
    This lets us write a simple image format without
    using any libraries that can be viewed on most
    linux workstations.
    """
    with open(filename, "w") as f:
        (h, w, c) = buf.shape
        f.write("P3\n")
        f.write("# ascii ppm file created by pyopengl\n")
        f.write("%i %i\n" % (w, h))
        f.write("255\n")
        for y in range(h - 1, -1, -1):
            for x in range(w):
                pixel = buf[y, x]
                l = " %3d %3d %3d" % (pixel[0], pixel[1], pixel[2])
                f.write(l)
            f.write("\n")


def debug_config(display, config):
    """Get debug display for the given configuration"""
    result = {}
    value = EGLint()
    for attr in CONFIG_ATTRS:
        if not eglGetConfigAttrib(display, config, attr, value):
            log.warning("Failed to get attribute %s from config", attr)
            continue
        if attr in BITMASK_FIELDS:
            attr_value = {}
            for subattr in BITMASK_FIELDS[attr]:
                if value.value & subattr:
                    attr_value[subattr.name] = True
        else:
            attr_value = value.value
        result[attr.name] = attr_value
    return result


def debug_configs(display, configs=None, max_count=256):
    """Present a formatted list of configs for the display"""
    if configs is None:
        configs = (EGLConfig * max_count)()
        num_configs = EGLint()
        eglGetConfigs(display, configs, max_count, num_configs)
        if not num_configs.value:
            return []
        configs = configs[: num_configs.value]
    debug_configs = [debug_config(display, cfg) for cfg in configs]
    return debug_configs


SURFACE_TYPE_BITS = [
    EGL_MULTISAMPLE_RESOLVE_BOX_BIT,
    EGL_PBUFFER_BIT,
    EGL_PIXMAP_BIT,
    EGL_SWAP_BEHAVIOR_PRESERVED_BIT,
    EGL_VG_ALPHA_FORMAT_PRE_BIT,
    EGL_VG_COLORSPACE_LINEAR_BIT,
    EGL_WINDOW_BIT,
]
RENDERABLE_TYPE_BITS = [
    EGL_OPENGL_BIT,
    EGL_OPENGL_ES_BIT,
    EGL_OPENGL_ES2_BIT,
    EGL_OPENGL_ES3_BIT,
    EGL_OPENVG_BIT,
]
CAVEAT_BITS = [
    EGL_NONE,
    EGL_SLOW_CONFIG,
    EGL_NON_CONFORMANT_CONFIG,
]
TRANSPARENT_BITS = [
    EGL_NONE,
    EGL_TRANSPARENT_RGB,
]

CONFIG_ATTRS = [
    EGL_CONFIG_ID,
    EGL_RED_SIZE,
    EGL_GREEN_SIZE,
    EGL_BLUE_SIZE,
    EGL_DEPTH_SIZE,
    EGL_ALPHA_SIZE,
    EGL_ALPHA_MASK_SIZE,
    EGL_BUFFER_SIZE,
    EGL_STENCIL_SIZE,
    EGL_BIND_TO_TEXTURE_RGB,
    EGL_BIND_TO_TEXTURE_RGBA,
    EGL_COLOR_BUFFER_TYPE,
    EGL_CONFIG_CAVEAT,
    EGL_CONFORMANT,
    EGL_LEVEL,
    EGL_LUMINANCE_SIZE,
    EGL_MAX_PBUFFER_WIDTH,
    EGL_MAX_PBUFFER_HEIGHT,
    EGL_MAX_PBUFFER_PIXELS,
    EGL_MIN_SWAP_INTERVAL,
    EGL_MAX_SWAP_INTERVAL,
    EGL_NATIVE_RENDERABLE,
    EGL_NATIVE_VISUAL_ID,
    EGL_NATIVE_VISUAL_TYPE,
    EGL_RENDERABLE_TYPE,
    EGL_SAMPLE_BUFFERS,
    EGL_SAMPLES,
    EGL_SURFACE_TYPE,
    EGL_TRANSPARENT_TYPE,
    EGL_TRANSPARENT_RED_VALUE,
    EGL_TRANSPARENT_GREEN_VALUE,
    EGL_TRANSPARENT_BLUE_VALUE,
]

BITMASK_FIELDS = dict(
    [
        (EGL_SURFACE_TYPE, SURFACE_TYPE_BITS),
        (EGL_RENDERABLE_TYPE, RENDERABLE_TYPE_BITS),
        (EGL_CONFORMANT, RENDERABLE_TYPE_BITS),
        (EGL_CONFIG_CAVEAT, CAVEAT_BITS),
        (EGL_TRANSPARENT_TYPE, TRANSPARENT_BITS),
    ]
)


def bit_renderer(bit):
    def render(value):
        if bit.name in value:
            return " Y"
        else:
            return " ."

    return render


CONFIG_FORMAT = [
    (EGL_CONFIG_ID, "0x%x", "id", "cfg"),
    (EGL_BUFFER_SIZE, "%i", "sz", "bf"),
    (EGL_LEVEL, "%i", "l", "lv"),
    (EGL_RED_SIZE, "%i", "r", "cbuf"),
    (EGL_GREEN_SIZE, "%i", "g", "cbuf"),
    (EGL_BLUE_SIZE, "%i", "b", "cbuf"),
    (EGL_ALPHA_SIZE, "%i", "a", "cbuf"),
    (EGL_DEPTH_SIZE, "%i", "th", "dp"),
    (EGL_STENCIL_SIZE, "%i", "t", "s"),
    (EGL_SAMPLES, "%i", "ns", "mult"),
    (EGL_SAMPLE_BUFFERS, "%i", "bu", "mult"),
    (EGL_NATIVE_VISUAL_ID, "0x%x", "id", "visual"),
    (EGL_RENDERABLE_TYPE, bit_renderer(EGL_OPENGL_BIT), "gl", "render"),
    (EGL_RENDERABLE_TYPE, bit_renderer(EGL_OPENGL_ES_BIT), "es", "render"),
    (EGL_RENDERABLE_TYPE, bit_renderer(EGL_OPENGL_ES2_BIT), "e2", "render"),
    (EGL_RENDERABLE_TYPE, bit_renderer(EGL_OPENGL_ES3_BIT), "e3", "render"),
    (EGL_RENDERABLE_TYPE, bit_renderer(EGL_OPENVG_BIT), "vg", "render"),
    (EGL_SURFACE_TYPE, bit_renderer(EGL_WINDOW_BIT), "wn", "surface"),
    (EGL_SURFACE_TYPE, bit_renderer(EGL_PBUFFER_BIT), "pb", "surface"),
    (EGL_SURFACE_TYPE, bit_renderer(EGL_PIXMAP_BIT), "px", "surface"),
]


def format_debug_configs(debug_configs, formats=CONFIG_FORMAT):
    """Format config for compact debugging display
    
    Produces a config summary display for a set of 
    debug_configs as a text-mode table.

    Uses `formats` (default `CONFIG_FORMAT`) to determine 
    which fields are extracted and how they are formatted
    along with the column/subcolum set to be rendered in
    the overall header.

    returns formatted ASCII table for display in debug
    logs or utilities
    """
    columns = []
    for (key, format, subcol, col) in formats:
        column = []
        max_width = 0
        for row in debug_configs:
            if isinstance(row, EGLConfig):
                raise TypeError(row, "Call debug_config(display,config)")
            try:
                value = row[key.name]
            except KeyError:
                formatted = "_"
            else:
                if isinstance(format, str):
                    formatted = format % (value)
                else:
                    formatted = format(value)
            max_width = max((len(formatted), max_width))
            column.append(formatted)
        columns.append(
            {
                "rows": column,
                "key": key,
                "format": format,
                "subcol": subcol,
                "col": col,
                "width": max_width,
            }
        )
    headers = []
    subheaders = []
    rows = [headers, subheaders]
    last_column = None
    last_column_width = 0
    for header, subcols in itertools.groupby(columns, lambda x: x["col"]):
        subcols = list(subcols)
        width = sum([col["width"] for col in subcols]) + (len(subcols) - 1)
        headers.append(header.center(width, ".")[:width])
    for column in columns:
        subheaders.append(column["subcol"].rjust(column["width"])[: column["width"]])
    rows.extend(
        zip(*[[v.rjust(col["width"], " ") for v in col["rows"]] for col in columns])
    )
    return "\n".join([" ".join(row) for row in rows])
