"""Theming support for LaTeX builder."""

import configparser
from os import path
from typing import Dict, Optional

from sphinx.application import Sphinx
from sphinx.config import Config
from sphinx.errors import ThemeError
from sphinx.locale import __
from sphinx.util import logging

logger = logging.getLogger(__name__)


class Theme:
    """A set of LaTeX configurations."""

    LATEX_ELEMENTS_KEYS = ['papersize', 'pointsize']
    UPDATABLE_KEYS = ['papersize', 'pointsize']

    def __init__(self, name: str) -> None:
        self.name = name
        self.docclass = name
        self.wrapperclass = name
        self.papersize = 'letterpaper'
        self.pointsize = '10pt'
        self.toplevel_sectioning = 'chapter'

    def update(self, config: Config) -> None:
        """Override theme settings by user's configuration."""
        for key in self.LATEX_ELEMENTS_KEYS:
            if config.latex_elements.get(key):
                value = config.latex_elements[key]
                setattr(self, key, value)

        for key in self.UPDATABLE_KEYS:
            if key in config.latex_theme_options:
                value = config.latex_theme_options[key]
                setattr(self, key, value)


class BuiltInTheme(Theme):
    """A built-in LaTeX theme."""

    def __init__(self, name: str, config: Config) -> None:
        super().__init__(name)

        if name == 'howto':
            self.docclass = config.latex_docclass.get('howto', 'article')
        else:
            self.docclass = config.latex_docclass.get('manual', 'report')

        if name in ('manual', 'howto'):
            self.wrapperclass = 'sphinx' + name
        else:
            self.wrapperclass = name

        # we assume LaTeX class provides \chapter command except in case
        # of non-Japanese 'howto' case
        if name == 'howto' and not self.docclass.startswith('j'):
            self.toplevel_sectioning = 'section'
        else:
            self.toplevel_sectioning = 'chapter'


class UserTheme(Theme):
    """A user defined LaTeX theme."""

    REQUIRED_CONFIG_KEYS = ['docclass', 'wrapperclass']
    OPTIONAL_CONFIG_KEYS = ['papersize', 'pointsize', 'toplevel_sectioning']

    def __init__(self, name: str, filename: str) -> None:
        super().__init__(name)
        self.config = configparser.RawConfigParser()
        self.config.read(path.join(filename), encoding='utf-8')

        for key in self.REQUIRED_CONFIG_KEYS:
            try:
                value = self.config.get('theme', key)
                setattr(self, key, value)
            except configparser.NoSectionError as exc:
                raise ThemeError(__('%r doesn\'t have "theme" setting') %
                                 filename) from exc
            except configparser.NoOptionError as exc:
                raise ThemeError(__('%r doesn\'t have "%s" setting') %
                                 (filename, exc.args[0])) from exc

        for key in self.OPTIONAL_CONFIG_KEYS:
            try:
                value = self.config.get('theme', key)
                setattr(self, key, value)
            except configparser.NoOptionError:
                pass


class ThemeFactory:
    """A factory class for LaTeX Themes."""

    def __init__(self, app: Sphinx) -> None:
        self.themes: Dict[str, Theme] = {}
        self.theme_paths = [path.join(app.srcdir, p) for p in app.config.latex_theme_path]
        self.config = app.config
        self.load_builtin_themes(app.config)

    def load_builtin_themes(self, config: Config) -> None:
        """Load built-in themes."""
        self.themes['manual'] = BuiltInTheme('manual', config)
        self.themes['howto'] = BuiltInTheme('howto', config)

    def get(self, name: str) -> Theme:
        """Get a theme for given *name*."""
        if name in self.themes:
            theme = self.themes[name]
        else:
            theme = self.find_user_theme(name) or Theme(name)

        theme.update(self.config)
        return theme

    def find_user_theme(self, name: str) -> Optional[Theme]:
        """Find a theme named as *name* from latex_theme_path."""
        for theme_path in self.theme_paths:
            config_path = path.join(theme_path, name, 'theme.conf')
            if path.isfile(config_path):
                try:
                    return UserTheme(name, config_path)
                except ThemeError as exc:
                    logger.warning(exc)

        return None
