# bdflib, a library for working with BDF font files
# Copyright (C) 2009-2022, Timothy Allen
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""
Automatically generate visual variants of a font.
"""

from bdflib import model


def embolden(font: model.Font, maintain_spacing: bool = True) -> model.Font:
    """
    Create a bold version of a font by widening each glyph.

    :param font: The font to embolden.
    :param maintain_spacing: If true, each glyph's advance width
            will be incremented, because a wider glyph needs more room.
    :returns: A copy of ``font``, with each glyph emboldened.

    To embolden a glyph, it is drawn over itself one pixel to the right,
    making each vertical stroke one pixel wider.
    """
    res = font.copy()

    for cp in res.codepoints():
        g = res[cp]
        g.merge_glyph(g, 1, 0)
        if maintain_spacing:
            g.advance += 1

    return res


def expand(font: model.Font, x_factor: int, y_factor: int) -> model.Font:
    """
    Create a font with greater resolution by duplicating glyph rows and columns.

    :param font: The font to expand.
    :param x_factor: The number of times to increase
            the font's horizontal resolution.
            Must be > 0.
    :param y_factor: The number of times to increase
            the font's vertical resolution.
            Must be > 0.
    :returns: A copy of ``font``, with the resolution adjusted.

    If ``font`` has a horizontal resolution of 72dpi,
    and ``x_factor`` is 2,
    the resulting font will have the same vertical metrics,
    but the horizontal resolution will be doubled to 144dpi.
    The new columns will be created by duplicating existing columns
    ("Nearest Neighbour"),
    so each column of a source glyph
    will appear twice in the resulting glyph.

    Vertical increases work the same way,
    except duplicating rows instead of columns.
    Because the resolution is increased,
    rather than making the glyph bigger,
    the point size stays the same.
    """
    if x_factor < 1:
        raise ValueError("Scaling factor must be greater than 0")
    if y_factor < 1:
        raise ValueError("Scaling factor must be greater than 0")

    res = font.copy()

    # Adjust font metadata
    res.xdpi *= x_factor
    res.ydpi *= y_factor

    # Scale glyphs horizontally and vertically
    for g in res.glyphs:
        # Scale the glyph bitmap
        new_data = []
        for old_row in g.data:
            # Calculate the new, horizontally scaled row
            new_row = 0
            for x in range(g.bbW):
                pixel = (old_row >> x) & 1
                for x_offset in range(x_factor):
                    new_row |= pixel << (x * x_factor + x_offset)
            new_data.append(new_row)

            # Duplicate the new row to create vertical scaling
            for _ in range(1, y_factor):
                new_data.append(new_row)

        g.data = new_data

        # Adjust glyph metadata
        g.bbX *= x_factor
        g.bbY *= y_factor
        g.bbW *= x_factor
        g.bbH *= y_factor
        g.advance *= x_factor

    return res


def merge(base: model.Font, custom: model.Font) -> model.Font:
    """
    Create a new font by choosing glyphs from two other fonts.

    :param base: The lower-priority font.
    :param custom: The higher-priority font.
    :returns: A font where each glyph comes from ``base`` or ``custom``.

    For any given codepoint, the resulting font will use the corresponding
    glyph from ``custom`` if there is one, falling back to the glyph from
    ``base``. The new font's properties and other metadata are all copied
    from ``custom``.
    """
    res = custom.copy()

    for cp in base.codepoints():
        if cp not in res:
            old_glyph = base[cp]
            res.new_glyph_from_data(
                old_glyph.name,
                old_glyph.data,
                old_glyph.bbX,
                old_glyph.bbY,
                old_glyph.bbW,
                old_glyph.bbH,
                old_glyph.advance,
                old_glyph.codepoint,
            )

    return res
