# Authors: see git history
#
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later.  See the file LICENSE for details.

import os

import inkex

from ..svg.tags import (INKSCAPE_GROUPMODE, INKSCAPE_LABEL, SVG_GROUP_TAG,
                        SVG_PATH_TAG, SVG_USE_TAG)
from ..update import update_inkstitch_document
from .glyph import Glyph


class FontVariant(object):
    """Represents a single variant of a font.

    Each font may have multiple variants for left-to-right, right-to-left,
    etc.  Each variant has a set of Glyphs, one per character.

    A FontVariant instance can be accessed as a dict by using a unicode
    character as a key.

    Properties:
      path    -- the path to the directory containing this font
      variant -- the font variant, specified using one of the constants below
      glyphs  -- a dict of Glyphs, with the glyphs' unicode characters as keys.
    """

    # We use unicode characters rather than English strings for font file names
    # in order to be more approachable for languages other than English.
    LEFT_TO_RIGHT = "→"
    RIGHT_TO_LEFT = "←"
    TOP_TO_BOTTOM = "↓"
    BOTTOM_TO_TOP = "↑"
    VARIANT_TYPES = (LEFT_TO_RIGHT, RIGHT_TO_LEFT, TOP_TO_BOTTOM, BOTTOM_TO_TOP)

    @classmethod
    def reversed_variant(cls, variant):
        if variant == cls.LEFT_TO_RIGHT:
            return cls.RIGHT_TO_LEFT
        elif variant == cls.RIGHT_TO_LEFT:
            return cls.LEFT_TO_RIGHT
        elif variant == cls.TOP_TO_BOTTOM:
            return cls.BOTTOM_TO_TOP
        elif variant == cls.BOTTOM_TO_TOP:
            return cls.TOP_TO_BOTTOM
        else:
            return None

    def __init__(self, font_path, variant, default_glyph=None):
        # If the font variant file does not exist, this constructor will
        # raise an exception.  The caller should catch it and decide
        # what to do.

        self.path = font_path
        self.variant = variant
        self.default_glyph = default_glyph
        self.glyphs = {}
        self._load_glyphs()

    def _load_glyphs(self):
        variant_file_paths = self._get_variant_file_paths()
        for svg_path in variant_file_paths:
            document = inkex.load_svg(svg_path)
            update_inkstitch_document(document)
            svg = document.getroot()
            svg = self._apply_transforms(svg)

            glyph_layers = svg.xpath(".//svg:g[starts-with(@inkscape:label, 'GlyphLayer-')]", namespaces=inkex.NSS)
            for layer in glyph_layers:
                self._clean_group(layer)
                layer.attrib[INKSCAPE_LABEL] = layer.attrib[INKSCAPE_LABEL].replace("GlyphLayer-", "", 1)
                glyph_name = layer.attrib[INKSCAPE_LABEL]
                try:
                    self.glyphs[glyph_name] = Glyph(layer)
                except (AttributeError, ValueError):
                    pass

    def _get_variant_file_paths(self):
        file_paths = []
        direct_path = os.path.join(self.path, "%s.svg" % self.variant)
        if os.path.isfile(direct_path):
            file_paths.append(direct_path)
        elif os.path.isdir(os.path.join(self.path, "%s" % self.variant)):
            path = os.path.join(self.path, "%s" % self.variant)
            file_paths.extend([os.path.join(path, svg) for svg in os.listdir(path) if svg.endswith('.svg')])
        return file_paths

    def _clean_group(self, group):
        # We'll repurpose the layer as a container group labelled with the
        # glyph.
        del group.attrib[INKSCAPE_GROUPMODE]

        # The layer may be marked invisible, so we'll clear the 'display'
        # style and presentation attribute.
        group.style.pop('display', None)
        group.attrib.pop('display', None)

    def _apply_transforms(self, svg):
        # apply transforms to paths and use tags
        for element in svg.iterdescendants((SVG_PATH_TAG, SVG_USE_TAG)):
            transform = element.composed_transform()
            if element.tag == SVG_PATH_TAG:
                path = element.path.transform(transform)
                element.set_path(path)
                element.attrib.pop("transform", None)

            if element.tag == SVG_USE_TAG:
                oldx = element.get('x', 0)
                oldy = element.get('y', 0)
                newx, newy = transform.apply_to_point((oldx, oldy))
                element.set('x', newx)
                element.set('y', newy)
                element.attrib.pop("transform", None)

        # remove transforms after they have been applied
        for group in svg.iterdescendants(SVG_GROUP_TAG):
            group.attrib.pop('transform', None)

        return svg

    def __getitem__(self, character):
        if character in self.glyphs:
            return self.glyphs[character]
        else:
            return self.glyphs.get(self.default_glyph, None)

    def __contains__(self, character):
        return character in self.glyphs
