#!/usr/bin/env python3
"""Hangman example
"""


from random import choice
import unicodedata
import re
from os import unlink
from os.path import isfile
from tiramisu import RegexpOption, OptionDescription, Config, IntOption, UnicodeOption, BoolOption, ParamOption, Params
from tiramisu.storage import storage_type
from tiramisu.storage.sqlite3.storage import SETTING
from tiramisu_parser import TiramisuParser


LANG = 'fr_FR'
DICT_FILE = '/usr/share/myspell/{}.dic'.format(LANG)
WORD_REGEXP = re.compile(r'^[a-z]{7,12}$')
PROPOSALS_LEN = 27
NB_PROPOSALS = 6


def remove_accent(word):
    """remove all accent"""
    word = unicodedata.normalize('NFD', word)
    return word.encode('ascii', 'ignore').decode()


def get_random_word():
    """get line randomly in myspell file
    """
    with open(DICT_FILE, 'r') as file_content:
        word = choice(file_content.readlines()).strip()
        if word.endswith('/S.'):
            word = word[:-3]
        if word.endswith('/X.'):
            word = word[:-3]
        if word.endswith('/F.'):
            word = word[:-3]
        if word.endswith('/a0p+') or word.endswith('/d0p+') or word.endswith('/a3p+'):
            word = word[:-5]
        if not WORD_REGEXP.search(remove_accent(word)):
            return get_random_word()
        return word


def display_uncomplete_word(word, *proposals):
    """display response with proposals
    """
    if display_proposals_left(display_misses(word, *proposals)) == 0:
        return word
    display = ['-'] * len(word)
    for idx, char in enumerate(remove_accent(word)):
        if char in proposals:
            display[idx] = word[idx]
    return ''.join(display)


def display_misses(word, *proposals):
    """display all proposals
    """
    ret = list(set(proposals) - set(list(remove_accent(word))))
    if None in ret:
        ret.remove(None)
    ret.sort()
    return ' '.join(ret)


def validate_misses(misses):
    if display_proposals_left(misses) == 0:
        raise ValueError('No more guest possible')


def display_proposals_left(misses):
    if not misses:
        return NB_PROPOSALS
    return max(NB_PROPOSALS - len(misses.split(' ')), 0)


def display_proposal(word, *proposals):
    if display_uncomplete_word(word, *proposals) == word:
        return False
    return display_proposals_left(display_misses(word, *proposals)) != 0


class ProposalOption(RegexpOption):
    __slots__ = tuple()
    _regexp = re.compile(r'^[a-z]$')
    _display_name = 'proposal'


def main():
    options = []
    proposal = None
    word = UnicodeOption('word',
                         'Word',
                         properties=('hidden', 'force_store_value'),
                         callback=get_random_word)
    proposals = [ParamOption(word)]
    for idx in range(PROPOSALS_LEN):
        requires = [{'option': 'self',
                     'expected': None,
                     'action': 'hidden',
                     'inverse': True}]
        if proposal is not None:
            display = BoolOption('display{}'.format(idx),
                                 'Display {}'.format(idx),
                                 properties=('hidden',),
                                 callback=display_proposal,
                                 callback_params=Params(tuple(proposals)))
            options.append(display)
            requires.append({'option': proposal,
                             'expected': None,
                             'action': 'disabled'})
            requires.append({'option': display,
                             'expected': False,
                             'action': 'disabled'})

        proposal = ProposalOption('guess{}'.format(idx),
                                  'Guess {}'.format(idx),
                                  requires=requires,
                                  properties=('positional',))
        #FIXME maximum recursion ...
        #if proposals:
        #    proposal.impl_add_consistency('not_equal', proposals[0])

        proposals.append(ParamOption(proposal, True))
        options.append(proposal)
    #
    proposal_word = UnicodeOption('proposal_word',
                                  'Word',
                                  properties=('frozen',),
                                  callback=display_uncomplete_word,
                                  callback_params=Params(tuple(proposals)))
    misses = UnicodeOption('misses',
                           'Misses',
                           properties=('frozen',),
                           callback=display_misses,
                           callback_params=Params(tuple(proposals)),
                           validator=validate_misses)
    proposals_left = IntOption('proposals_left',
                               'Proposals left',
                               properties=('frozen',),
                               callback=display_proposals_left,
                               callback_params=Params(ParamOption(misses)))
    #descr = OptionDescription('proposals',
    #                          'Suggesting letters',
    #                          options)
    storage_type.set('sqlite3')
    config = Config(OptionDescription('root', 'root', [word, proposal_word, misses, proposals_left] + options), persistent=True, session_id='hangman')
    parser = TiramisuParser()
    parser.add_arguments(config)
    try:
        parser.parse_args()
    except ValueError:
        pass
    config = parser.get_config()
    filename = '{}/tiramisu.db'.format(SETTING.dir_database)
    lost = False
    for name in ['proposal_word', 'misses', 'proposals_left']:
        option = config.option(name)
        try:
            value = option.value.get()
            print('{}: {}'.format(option.option.doc(), value))
        except ValueError as err:
            lost = True
            err.prefix = ''
            print(option.option.doc(), str(err))
            if isfile(filename):
                unlink(filename)
    if not lost and \
            config.option('proposal_word').value.get() == config.forcepermissive.option('word').value.get():
        print('You win')
        if isfile(filename):
            unlink(filename)

if __name__ == "__main__":
    main()