"""
novelWriter – Spell Check Classes
=================================
Wrapper classes for spell checking tools

File History:
Created: 2019-06-11 [0.1.5]

This file is a part of novelWriter
Copyright 2018–2022, Veronica Berglyd Olsen

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 <https://www.gnu.org/licenses/>.
"""

import os
import logging

from novelwriter.error import logException

logger = logging.getLogger(__name__)


class NWSpellEnchant():

    def __init__(self):

        self._theDict = None
        self._projDict = set()
        self._projectDict = None
        self._spellLanguage = None
        self._theBroker = None

        logger.debug("Enchant spell checking activated")

        return

    ##
    #  Getters and Setters
    ##

    def spellLanguage(self):
        return self._spellLanguage

    def setLanguage(self, theLang, projectDict=None):
        """Load a dictionary for the language specified in the config.
        If that fails, we load a mock dictionary so that lookups don't
        crash.
        """
        try:
            import enchant
            if self._theBroker is not None:
                logger.debug("Deleting old pyenchant broker")
                del self._theBroker

            self._theBroker = enchant.Broker()
            self._theDict = self._theBroker.request_dict(theLang)
            self._spellLanguage = theLang
            logger.debug("Enchant spell checking for language '%s' loaded", theLang)

        except Exception:
            logger.error("Failed to load enchant spell checking for language '%s'", theLang)
            self._theDict = FakeEnchant()
            self._spellLanguage = None

        self._readProjectDictionary(projectDict)
        for pWord in self._projDict:
            self._theDict.add_to_session(pWord)

        return

    ##
    #  Methods
    ##

    def checkWord(self, theWord):
        """Wrapper function for pyenchant.
        """
        try:
            return self._theDict.check(theWord)
        except Exception:
            return True

    def suggestWords(self, theWord):
        """Wrapper function for pyenchant.
        """
        try:
            return self._theDict.suggest(theWord)
        except Exception:
            return []

    def addWord(self, newWord):
        """Add a word to the project dictionary.
        """
        try:
            self._theDict.add_to_session(newWord)
        except Exception:
            return False

        if self._projectDict is not None and newWord not in self._projDict:
            newWord = newWord.strip()
            try:
                with open(self._projectDict, mode="a+", encoding="utf-8") as outFile:
                    outFile.write("%s\n" % newWord)
                self._projDict.add(newWord)
            except Exception:
                logger.error("Failed to add word to project word list %s", str(self._projectDict))
                logException()
                return False
            return True

        return False

    def listDictionaries(self):
        """Wrapper function for pyenchant.
        """
        retList = []
        try:
            import enchant
            for spTag, spProvider in enchant.list_dicts():
                retList.append((spTag, spProvider.name))
        except Exception:
            logger.error("Failed to list languages for enchant spell checking")

        return retList

    def describeDict(self):
        """Return the tag and provider of the currently loaded
        dictionary.
        """
        try:
            spTag = self._theDict.tag
            spName = self._theDict.provider.name
        except Exception:
            logger.error("Failed to extract information about the dictionary")
            logException()
            spTag = ""
            spName = ""

        return spTag, spName

    ##
    #  Internal Functions
    ##

    def _readProjectDictionary(self, projectDict):
        """Read the content of the project dictionary, and add it to the
        lookup lists.
        """
        self._projDict = set()
        self._projectDict = projectDict

        if projectDict is None:
            return False

        if not os.path.isfile(projectDict):
            return False

        try:
            logger.debug("Loading project word list")
            with open(projectDict, mode="r", encoding="utf-8") as wordsFile:
                for theLine in wordsFile:
                    theLine = theLine.strip()
                    if len(theLine) > 0 and theLine not in self._projDict:
                        self._projDict.add(theLine)
            logger.debug("Project word list contains %d words", len(self._projDict))

        except Exception:
            logger.error("Failed to load project word list")
            logException()
            return False

        return True

# END Class NWSpellEnchant


class FakeEnchant:
    """Fallback for when Enchant is selected, but not installed.
    """
    def __init__(self):
        return

    def check(self, theWord):
        return True

    def suggest(self, theWord):
        return []

    def add_to_session(self, theWord):
        return

# END Class FakeEnchant
