from .constants import (
    NORMAL, FONT_STYLES, FONT_VARIANTS, FONT_WEIGHTS,
    ITALIC, OBLIQUE, SMALL_CAPS, BOLD
)

class Font:
    def __init__(self, family, size, style=NORMAL, variant=NORMAL, weight=NORMAL):
        if (family[0] == "'" and family[-1] == "'") or (family[0] == '"' and family[-1] == '"'):
            self.family = family[1:-1]
        else:
            self.family = family

        try:
            self.size = int(size)
        except ValueError:
            try:
                if size.strip().endswith('pt'):
                    self.size = int(size[:-2])
                else:
                    raise ValueError("Invalid font size {!r}".format(size))
            except:
                raise ValueError("Invalid font size {!r}".format(size))
        self.style = style if style in FONT_STYLES else NORMAL
        self.variant = variant if variant in FONT_VARIANTS else NORMAL
        self.weight = weight if weight in FONT_WEIGHTS else NORMAL

    def __repr__(self):
        return '<Font: {}{}{}{}pt {}>'.format(
            '' if self.style is NORMAL else (self.style + ' '),
            '' if self.variant is NORMAL else (self.variant + ' '),
            '' if self.weight is NORMAL else (self.weight + ' '),
            self.size,
            self.family
        )

    def __eq__(self, other):
        return (
            self.family == other.family
            and self.size == other.size
            and self.style == other.style
            and self.variant == other.variant
            and self.weight == other.weight
        )

    def normal_style(self):
        "Generate a normal style version of this font"
        return Font(self.family, self.size, style=NORMAL, variant=self.variant, weight=self.weight)

    def italic(self):
        "Generate an italic version of this font"
        return Font(self.family, self.size, style=ITALIC, variant=self.variant, weight=self.weight)

    def oblique(self):
        "Generate an oblique version of this font"
        return Font(self.family, self.size, style=OBLIQUE, variant=self.variant, weight=self.weight)

    def normal_variant(self):
        "Generate a normal variant of this font"
        return Font(self.family, self.size, style=self.style, variant=NORMAL, weight=self.weight)

    def small_caps(self):
        "Generate a small-caps variant of this font"
        return Font(self.family, self.size, style=self.style, variant=SMALL_CAPS, weight=self.weight)

    def normal_weight(self):
        "Generate a normal weight version of this font"
        return Font(self.family, self.size, style=self.style, variant=self.variant, weight=NORMAL)

    def bold(self):
        "Generate a bold version of this font"
        return Font(self.family, self.size, style=self.style, variant=self.variant, weight=BOLD)


def font(value):
    """Parse a font from a string.

    Accepts:
    * Font instances

    style: normal / italic / oblique
    variant: normal / small-caps
    weight: normal / bold

    style variant weight size family
    variant weight size family
    weight size family
    size family
    """

    if isinstance(value, Font):
        return value

    elif isinstance(value, str):
        parts = value.split(' ')

        style = None
        variant = None
        weight = None
        size = None

        while size is None:
            part = parts.pop(0)
            if part == NORMAL:
                if style is None:
                    style = NORMAL
                elif variant is None:
                    variant = NORMAL
                elif weight is None:
                    weight = NORMAL
            elif part in FONT_STYLES:
                if style is not None:
                    raise ValueError("Invalid font declaration '{}'".format(value))
                style = part
            elif part in FONT_VARIANTS:
                if variant is not None:
                    raise ValueError("Invalid font declaration '{}'".format(value))
                if style is None:
                     style = NORMAL
                variant = part
            elif part in FONT_WEIGHTS:
                if weight is not None:
                    raise ValueError("Invalid font declaration '{}'".format(value))
                if style is None:
                     style = NORMAL
                if variant is None:
                     variant = NORMAL
                weight = part
            else:
                try:
                    if part.endswith('pt'):
                        size = int(part[:-2])
                    else:
                        size = int(part)
                except ValueError:
                    raise ValueError("Invalid size in font declaration '{}'".format(value))

                if parts[0] == 'pt':
                    parts.pop(0)

        family = ' '.join(parts)
        return Font(family, size, style=style, variant=variant, weight=weight)

    raise ValueError("Unknown font '%s'" % value)
