#!/usr/bin/env vpython
#
# [VPYTHON:BEGIN]
# wheel: <
#   name: "infra/python/wheels/freetype-py/${vpython_platform}"
#   version: "version:2.1.0.post1"
# >
# [VPYTHON:END]

# Copyright 2019 The ANGLE Project Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# gen_vk_overlay_fonts.py:
#  Code generation for overlay fonts.  Should be run if the font file under overlay/ is changed,
#  or the font sizes declared in this file are modified.  The font is assumed to be monospace.
#  The output will contain ASCII characters in order from ' ' to '~'.  The output will be images
#  with 3 rows of 32 characters each.
#  NOTE: don't run this script directly. Run scripts/run_code_generation.py.

from datetime import date
import sys

if len(sys.argv) < 2:
    from freetype import *

out_file_cpp = 'Overlay_font_autogen.cpp'
out_file_h = 'Overlay_font_autogen.h'
font_file = 'overlay/DejaVuSansMono-Bold.ttf'

template_out_file_h = u"""// GENERATED FILE - DO NOT EDIT.
// Generated by {script_name} using {font_file}.
//
// Copyright {copyright_year} The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// {out_file_name}:
//   Autogenerated overlay font data.

#include "libANGLE/Overlay.h"

namespace gl
{{
namespace overlay
{{
constexpr int kFontCount = {font_count};
constexpr int kFontGlyphWidths[kFontCount] = {{ {font_glyph_widths} }};
constexpr int kFontGlyphHeights[kFontCount] = {{ {font_glyph_heights} }};
constexpr int kFontCharactersPerRow = 32;
constexpr int kFontCharactersPerCol = 3;
constexpr int kFontCharacters = kFontCharactersPerRow * kFontCharactersPerCol;
constexpr int kFontImageWidth = {max_font_width} * kFontCharactersPerRow;
constexpr int kFontImageHeight = {max_font_height} * kFontCharactersPerCol;
{font_layers}
}}  // namespace overlay
}}  // namespace gl

"""

template_out_file_cpp = u"""// GENERATED FILE - DO NOT EDIT.
// Generated by {script_name} using images from {font_file}.
//
// Copyright {copyright_year} The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// {out_file_name}:
//   Autogenerated overlay font data.

#include "libANGLE/Overlay.h"
#include "libANGLE/Overlay_font_autogen.h"

#include <numeric>

namespace gl
{{
using namespace overlay;

// Save binary size if the font images are never to be used.
#if ANGLE_ENABLE_OVERLAY
namespace
{{
constexpr int kFontWidths[kFontCount] = {{ {font_layer_widths} }};
constexpr int kFontHeights[kFontCount] = {{ {font_layer_heights} }};

{font_data}

// Returns a bit with the value of the pixel.
template<int kFontWidth, int kFontHeight>
uint32_t GetFontLayerPixel(const uint32_t fontImage[kFontHeight][kFontWidth / 32], int x, int y)
{{
    ASSERT(x >= 0 && x < kFontWidth && y >= 0 && y < kFontHeight);
    return fontImage[y][x / 32] >> (x % 32) & 1;
}}

inline uint32_t GetFontPixel(int layer, int x, int y)
{{
    switch (layer)
    {{
    {get_font_layer_pixel}
    default:
        UNREACHABLE();
        return 0;
    }}
}}

}}  // anonymous namespace

void OverlayState::initFontData(uint8_t *fontData) const
{{
    constexpr int kFontDataLayerSize = kFontImageWidth * kFontImageHeight;

    // Unpack the font bitmap into R8_UNORM format.  Border pixels are given a 0.5 value for better
    // font visibility.
    for (int layer = 0; layer < kFontCount; ++layer)
    {{
        memset(fontData, 0, kFontDataLayerSize);
        for (int y = 0; y < kFontHeights[layer]; ++y)
        {{
            for (int x = 0; x < kFontWidths[layer]; ++x)
            {{
                uint32_t src     = GetFontPixel(layer, x, y);
                uint8_t dstValue = src ? 255 : 0;
                fontData[y * kFontImageWidth + x] = dstValue;
            }}
        }}
        fontData += kFontDataLayerSize;
    }}
}}
#else
void OverlayState::initFontData(uint8_t *fontData) const
{{
    memset(fontData, 0, kFontCount * kFontImageWidth * kFontImageHeight * sizeof(*fontData));
}}
#endif
}}  // namespace gl
"""

template_get_font_layer_pixel = u"""case {layer}:
    return GetFontLayerPixel<kFontWidths[{layer}], kFontHeights[{layer}]>({font_image}, x, y);
"""


def main():
    if len(sys.argv) == 2 and sys.argv[1] == 'inputs':
        print(font_file)
        return
    if len(sys.argv) == 2 and sys.argv[1] == 'outputs':
        print(','.join([out_file_cpp, out_file_h]))
        return

    font_defs = [('large', 36), ('medium', 23), ('small', 14)]
    chars = ' !"#$%&\'()*+,-./0123456789:;<=>?' + \
            '@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_' + \
            '`abcdefghijklmnopqrstuvwxyz{|}~ '
    output_rows = 3
    output_cols = 32
    assert (len(chars) == output_rows * output_cols)

    font_glyph_widths = []
    font_glyph_heights = []
    font_layers = []
    font_data = []
    get_font_layer_pixel = []
    current_font_layer = 0

    # Load the font file.
    face = Face(font_file)
    assert (face.is_fixed_width)

    for font_name, font_size in font_defs:

        # Since the font is fixed width, we can retrieve its size right away.
        face.set_char_size(font_size << 6)
        glyph_width = face.size.max_advance >> 6
        glyph_ascender = face.size.ascender >> 6
        glyph_descender = face.size.descender >> 6
        glyph_height = glyph_ascender - glyph_descender

        font_tag = font_name.capitalize()
        font_layer = str(current_font_layer)
        font_layer_symbol = 'kFontLayer' + font_tag
        font_array_name = 'kFontImage' + font_tag
        font_width = 'kFontWidths[' + font_layer_symbol + ']'
        font_height = 'kFontHeights[' + font_layer_symbol + ']'

        # Font pixels are packed in 32-bit values.
        font_array_width = output_cols * glyph_width / 32
        font_array_height = output_rows * glyph_height
        font_array = [[0] * font_array_width for i in range(font_array_height)]

        for charIndex in range(len(chars)):
            char = chars[charIndex]
            base_x = (charIndex % output_cols) * glyph_width
            base_y = (charIndex / output_cols) * glyph_height

            # Render the character.
            face.load_char(char)
            bitmap = face.glyph.bitmap
            left = face.glyph.bitmap_left
            top = face.glyph.bitmap_top
            width = bitmap.width
            rows = bitmap.rows
            pitch = bitmap.pitch

            offset_x = left
            offset_y = glyph_height - (top - glyph_descender)

            # '#' in the smallest font generates a larger glyph than the "fixed" font width.
            if offset_x + width > glyph_width:
                offset_x = glyph_width - width
                if offset_x < 0:
                    width += offset_x
                    offset_x = 0

            base_x += offset_x
            base_y += offset_y

            assert (offset_x + width <= glyph_width)
            assert (offset_y + rows <= glyph_height)

            # Write the character bitmap in the font image.
            for y in range(rows):
                for x in range(width):
                    pixel_value = bitmap.buffer[y * pitch + x]
                    output_bit = 1 if pixel_value >= 122 else 0

                    font_array_row = base_y + y
                    font_array_col = (base_x + x) / 32
                    font_array_bit = (base_x + x) % 32
                    font_array[font_array_row][font_array_col] |= output_bit << font_array_bit

        # Output the image to a C array.
        data = 'constexpr uint32_t ' + font_array_name + '[' + font_height + '][' + font_width + '/32] = {\n'
        for y in range(font_array_height):
            data += '{'
            for x in range(font_array_width):
                data += '0x{:08X}, '.format(font_array[y][x])
            data += '},\n'
        data += '};\n'

        font_glyph_widths.append(glyph_width)
        font_glyph_heights.append(glyph_height)
        font_layers.append('constexpr int ' + font_layer_symbol + ' = ' + font_layer + ';')
        font_data.append(data)
        get_font_layer_pixel.append(
            template_get_font_layer_pixel.format(
                layer=font_layer_symbol, font_image=font_array_name))
        current_font_layer += 1

    with open(out_file_h, 'w') as outfile:
        outfile.write(
            template_out_file_h.format(
                script_name=__file__,
                font_file=font_file,
                copyright_year=date.today().year,
                out_file_name=out_file_h,
                font_count=len(font_data),
                font_glyph_widths=','.join(map(str, font_glyph_widths)),
                font_glyph_heights=','.join(map(str, font_glyph_heights)),
                max_font_width=max(font_glyph_widths),
                max_font_height=max(font_glyph_heights),
                font_layers='\n'.join(font_layers)))
        outfile.close()

    font_layer_widths = [
        'kFontGlyphWidths[' + str(layer) + '] * kFontCharactersPerRow'
        for layer in range(len(font_data))
    ]
    font_layer_heights = [
        'kFontGlyphHeights[' + str(layer) + '] * kFontCharactersPerCol'
        for layer in range(len(font_data))
    ]

    with open(out_file_cpp, 'w') as outfile:
        outfile.write(
            template_out_file_cpp.format(
                script_name=__file__,
                font_file=font_file,
                copyright_year=date.today().year,
                out_file_name=out_file_cpp,
                font_layer_widths=','.join(font_layer_widths),
                font_layer_heights=','.join(font_layer_heights),
                font_data='\n'.join(font_data),
                get_font_layer_pixel=''.join(get_font_layer_pixel)))
        outfile.close()


if __name__ == '__main__':
    sys.exit(main())
