# SPDX-FileCopyrightText: 2014 MicroPython & CircuitPython contributors (https://github.com/adafruit/circuitpython/graphs/contributors)
#
# SPDX-License-Identifier: MIT

import argparse

import os
import struct
import sys

sys.path.insert(0, "bitmap_font")
sys.path.insert(0, "../../tools/bitmap_font")

from adafruit_bitmap_font import bitmap_font

parser = argparse.ArgumentParser(description="Generate displayio resources.")
parser.add_argument("--font", type=str, help="Font path", required=True)
parser.add_argument("--extra_characters", type=str, help="Unicode string of extra characters")
parser.add_argument(
    "--sample_file",
    type=argparse.FileType("r", encoding="utf-8"),
    help="Text file that includes strings to support.",
)
parser.add_argument("--output_c_file", type=argparse.FileType("w"), required=True)

args = parser.parse_args()


class BitmapStub:
    def __init__(self, width, height, color_depth):
        self.width = width
        self.rows = [b""] * height

    def _load_row(self, y, row):
        self.rows[y] = bytes(row)


f = bitmap_font.load_font(args.font, BitmapStub)

# Load extra characters from the sample file.
sample_characters = set()
if args.sample_file:
    for line in args.sample_file:
        # Skip comments because we add additional characters in our huffman comments.
        if line.startswith("//"):
            continue
        for c in line.strip():
            sample_characters.add(c)

# Merge visible ascii, sample characters and extra characters.
visible_ascii = bytes(range(0x20, 0x7F)).decode("utf-8")
all_characters = list(visible_ascii)
for c in sample_characters:
    if c not in all_characters:
        all_characters += c
if args.extra_characters:
    all_characters.extend(args.extra_characters)
all_characters = "".join(sorted(set(all_characters)))
filtered_characters = all_characters

# Try to pre-load all of the glyphs. Misses will still be slow later.
f.load_glyphs(set(ord(c) for c in all_characters))

missing = 0
# Get each glyph.
for c in set(all_characters):
    if ord(c) not in f._glyphs:
        missing += 1
        filtered_characters = filtered_characters.replace(c, "")
        continue
    g = f.get_glyph(ord(c))
    if g["shift"][1] != 0:
        raise RuntimeError("y shift")

if missing > 0:
    print("Font missing", missing, "characters", file=sys.stderr)

tile_x, tile_y, dx, dy = f.get_bounding_box()
total_bits = tile_x * len(all_characters)
total_bits += 32 - total_bits % 32
bytes_per_row = total_bits // 8
b = bytearray(bytes_per_row * tile_y)

for x, c in enumerate(filtered_characters):
    g = f.get_glyph(ord(c))
    start_bit = x * tile_x + g["bounds"][2]
    start_y = (tile_y - 2) - (g["bounds"][1] + g["bounds"][3])
    for y, row in enumerate(g["bitmap"].rows):
        for i in range(g["bounds"][0]):
            byte = i // 8
            bit = i % 8
            if row[byte] & (1 << (7 - bit)) != 0:
                overall_bit = start_bit + (start_y + y) * bytes_per_row * 8 + i
                b[overall_bit // 8] |= 1 << (7 - (overall_bit % 8))


extra_characters = ""
for c in filtered_characters:
    if c not in visible_ascii:
        extra_characters += c

c_file = args.output_c_file

c_file.write(
    """\

#include "shared-bindings/displayio/Bitmap.h"
#include "shared-bindings/displayio/Palette.h"
#include "supervisor/shared/display.h"

"""
)


c_file.write(
    """\
#if CIRCUITPY_REPL_LOGO
"""
)
if tile_y == 16:
    blinka_size = 16
    c_file.write(
        """\
const uint32_t blinka_bitmap_data[32] = {
    0x00000011, 0x11000000,
    0x00000111, 0x53100000,
    0x00000111, 0x56110000,
    0x00000111, 0x11140000,
    0x00000111, 0x20002000,
    0x00000011, 0x13000000,
    0x00000001, 0x11200000,
    0x00000000, 0x11330000,
    0x00000000, 0x01122000,
    0x00001111, 0x44133000,
    0x00032323, 0x24112200,
    0x00111114, 0x44113300,
    0x00323232, 0x34112200,
    0x11111144, 0x44443300,
    0x11111111, 0x11144401,
    0x23232323, 0x21111110
};
"""
    )
else:
    blinka_size = 12
    c_file.write(
        """\
const uint32_t blinka_bitmap_data[28] = {
    0x00000111, 0x00000000,
    0x00001153, 0x10000000,
    0x00001156, 0x11000000,
    0x00001111, 0x14000000,
    0x00000112, 0x00200000,
    0x00000011, 0x30000000,
    0x00000011, 0x20000000,
    0x00011144, 0x13000000,
    0x00232324, 0x12000000,
    0x01111444, 0x13000000,
    0x32323234, 0x12010000,
    0x11111144, 0x44100000
};
"""
    )

c_file.write(
    """\
const displayio_bitmap_t blinka_bitmap = {{
    .base = {{.type = &displayio_bitmap_type }},
    .width = {0},
    .height = {0},
    .data = (uint32_t*) blinka_bitmap_data,
    .stride = 2,
    .bits_per_value = 4,
    .x_shift = 3,
    .x_mask = 0x7,
    .bitmask = 0xf,
    .read_only = true
}};

_displayio_color_t blinka_colors[7] = {{
    {{
        .rgb888 = 0x000000,
        .transparent = true
    }},
    {{ // Purple
        .rgb888 = 0x8428bc
    }},
    {{ // Pink
        .rgb888 = 0xff89bc
    }},
    {{ // Light blue
        .rgb888 = 0x7beffe
    }},
    {{ // Dark purple
        .rgb888 = 0x51395f
    }},
    {{ // White
        .rgb888 = 0xffffff
    }},
    {{ // Dark Blue
        .rgb888 = 0x0736a0
    }},
}};

displayio_palette_t blinka_palette = {{
    .base = {{.type = &displayio_palette_type }},
    .colors = blinka_colors,
    .color_count = 7,
    .needs_refresh = false
}};

displayio_tilegrid_t supervisor_blinka_sprite = {{
    .base = {{.type = &displayio_tilegrid_type }},
    .bitmap = (displayio_bitmap_t*) &blinka_bitmap,
    .pixel_shader = &blinka_palette,
    .x = 0,
    .y = 0,
    .pixel_width = {0},
    .pixel_height = {0},
    .bitmap_width_in_tiles = 1,
    .width_in_tiles = 1,
    .height_in_tiles = 1,
    .tile_width = {0},
    .tile_height = {0},
    .top_left_x = {0},
    .top_left_y = {0},
    .tiles = 0,
    .partial_change = false,
    .full_change = false,
    .hidden = false,
    .hidden_by_parent = false,
    .moved = false,
    .inline_tiles = true,
    .in_group = true
}};
#endif
""".format(
        blinka_size
    )
)

c_file.write(
    """\
#if CIRCUITPY_TERMINALIO
_displayio_color_t terminal_colors[2] = {
    {
        .rgb888 = 0x000000
    },
    {
        .rgb888 = 0xffffff
    },
};

displayio_palette_t supervisor_terminal_color = {
    .base = {.type = &displayio_palette_type },
    .colors = terminal_colors,
    .color_count = 2,
    .needs_refresh = false
};
"""
)

c_file.write(
    """\
displayio_tilegrid_t supervisor_terminal_scroll_area_text_grid = {{
    .base = {{ .type = &displayio_tilegrid_type }},
    .bitmap = (displayio_bitmap_t*) &supervisor_terminal_font_bitmap,
    .pixel_shader = &supervisor_terminal_color,
    .x = 0,
    .y = 0,
    .pixel_width = {1},
    .pixel_height = {2},
    .bitmap_width_in_tiles = {0},
    .tiles_in_bitmap = {0},
    .width_in_tiles = 1,
    .height_in_tiles = 1,
    .tile_width = {1},
    .tile_height = {2},
    .tiles = NULL,
    .partial_change = false,
    .full_change = false,
    .hidden = false,
    .hidden_by_parent = false,
    .moved = false,
    .inline_tiles = false,
    .in_group = true
}};
""".format(
        len(all_characters), tile_x, tile_y
    )
)

c_file.write(
    """\
displayio_tilegrid_t supervisor_terminal_status_bar_text_grid = {{
    .base = {{ .type = &displayio_tilegrid_type }},
    .bitmap = (displayio_bitmap_t*) &supervisor_terminal_font_bitmap,
    .pixel_shader = &supervisor_terminal_color,
    .x = 0,
    .y = 0,
    .pixel_width = {1},
    .pixel_height = {2},
    .bitmap_width_in_tiles = {0},
    .tiles_in_bitmap = {0},
    .width_in_tiles = 1,
    .height_in_tiles = 1,
    .tile_width = {1},
    .tile_height = {2},
    .tiles = NULL,
    .partial_change = false,
    .full_change = false,
    .hidden = false,
    .hidden_by_parent = false,
    .moved = false,
    .inline_tiles = false,
    .in_group = true
}};
""".format(
        len(all_characters), tile_x, tile_y
    )
)

c_file.write(
    """\
const uint32_t font_bitmap_data[{}] = {{
""".format(
        bytes_per_row * tile_y // 4
    )
)

for i, word in enumerate(struct.iter_unpack(">I", b)):
    c_file.write("0x{:08x}, ".format(word[0]))
    if (i + 1) % (bytes_per_row // 4) == 0:
        c_file.write("\n")

c_file.write(
    """\
};
"""
)

c_file.write(
    """\
displayio_bitmap_t supervisor_terminal_font_bitmap = {{
    .base = {{.type = &displayio_bitmap_type }},
    .width = {},
    .height = {},
    .data = (uint32_t*) font_bitmap_data,
    .stride = {},
    .bits_per_value = 1,
    .x_shift = 5,
    .x_mask = 0x1f,
    .bitmask = 0x1,
    .read_only = true
}};
""".format(
        len(all_characters) * tile_x, tile_y, bytes_per_row / 4
    )
)


c_file.write(
    """\
const fontio_builtinfont_t supervisor_terminal_font = {{
    .base = {{.type = &fontio_builtinfont_type }},
    .bitmap = &supervisor_terminal_font_bitmap,
    .width = {},
    .height = {},
    .unicode_characters = (const uint8_t*) "{}",
    .unicode_characters_len = {}
}};
""".format(
        tile_x, tile_y, extra_characters, len(extra_characters.encode("utf-8"))
    )
)

c_file.write(
    """\
terminalio_terminal_obj_t supervisor_terminal = {
    .base = { .type = &terminalio_terminal_type },
    .font = &supervisor_terminal_font,
    .cursor_x = 0,
    .cursor_y = 0,
    .scroll_area = NULL,
    .status_bar = NULL
};

#endif
"""
)