#!/usr/bin/env python3
#-*- coding: utf-8 -*-

"""
Get a fortune from a JSON file and say it.

File is under EUPL1.2
Author: kujiu
"""

import json
import argparse
import logging
import threading

import sys
import os.path
import secrets
try:
    import speechd
except ImportError:
    speechd = None

logger = logging.getLogger(__name__)


class JsonFortune:
    def __init__(self, srcfile, speak, module, rate, cycle, timer):
        """
        Fortune software
        """
        self.speak = speak
        self.module = module
        self.rate = rate
        self.cycle = cycle
        self.timer = timer
        if not module:
            module = 'default'

        with open(srcfile) as fin:
            self.quotes = json.load(fin)

        by_category = getattr(options, 'by_category', False)
        if not by_category:
            self.uncategorized_quotes = {}
            for lang in self.quotes:
                self.uncategorized_quotes.setdefault(lang, [])
                for category in self.quotes[lang]:
                    for author in self.quotes[lang][category]:
                        self.uncategorized_quotes[lang].extend(
                            [
                                (quote, author)
                                for quote
                                in self.quotes[lang][category][author]
                            ]
                        )

    def get_quote(self):
        """
        Get a quote, returns (quote, lang, author)
        """
        by_category = getattr(options, 'by_category', False)
        quote = ""
        count = 0
        while not quote and count < 50:
            count += 1
            lang = secrets.choice([lang for lang in self.quotes])
            if by_category:
                category = secrets.choice([cat for cat in self.quotes[lang]])
                if not self.quotes[lang][category]:
                    continue
                author = secrets.choice(
                    [author for author in self.quotes[lang][category]])
                if not self.quotes[lang][category][author]:
                    continue
                quote = secrets.choice(self.quotes[lang][category][author])
            else:
                quote, author = secrets.choice(self.uncategorized_quotes[lang])

        return (quote, lang, author)

    def say_quote(self, callback=None):
        """
        Say a random quote
        """
        quote, lang, author = self.get_quote()

        if not speechd:
            logger.error("Speech Dispatcher is not loaded.")
            return

        if not quote:
            logger.error("No quote found")
            return

        quote += "\n %s" % author

        speech_client = speechd.client.Client()
        speech_client.set_priority('important')
        speech_client.set_rate(self.rate)
        if self.module != 'default' and self.module:
            speech_client.set_output_module(self.module)

        voices = [
            voice[0]
            for voice
            in speech_client.list_synthesis_voices()
            if voice[1] == lang]
        speech_client.set_language(lang)
        speech_client.set_synthesis_voice(secrets.choice(voices))
        logger.info("Say: %s", quote)
        speech_client.speak(quote, callback)
        speech_client.close()

    def print_quote(self):
        """
        Print a random quote.
        """
        if self.cycle:
            print(chr(27) + "[2J")

        quote_dict = self.get_quote()
        if not quote_dict:
            logger.error("No quote found")
            return

        print(quote_dict[0] + "\n  -- " + quote_dict[2])

    def run(self):
        """
        Run
        """
        if self.speak:
            self.say_quote(self.run if self.cycle else None)
        else:
            self.print_quote()
        if self.cycle:
            threading.Timer(self.timer, self.run).start()

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Print/say a quote from JSON file.")
    parser.add_argument(
        '-s', '--source', default='~/.fortunes.json',
        help='JSON file with quotes')
    parser.add_argument(
        '-b', '--by_category', default=False, action='store_true',
        help='Each category has the same weight')
    parser.add_argument(
        '-v', '--verbose', default=False, action='store_true',
        help='Verbose')
    parser.add_argument(
        '-k', '--speak', default=False, action='store_true',
        help='Say with Speech Dispatcher, otherwise print it')
    parser.add_argument(
        '-m', '--module', default='default',
        help='Speech Dispatcher output module')
    parser.add_argument(
        '-r', '--rate', default=15,
        help='Speech Dispatcher rate', type=int)
    parser.add_argument(
        '-c', '--cycle', default=False, action='store_true',
        help='Display/say fortunes in loop.')
    parser.add_argument(
        '-t', '--timer', default=60,
        help='Number of seconds between two quotes', type=int)

    options = parser.parse_args()

    source = os.path.expanduser(
        os.path.expandvars(options.source)
    )

    speak = getattr(options, 'speak', False)
    module = getattr(options, 'module', None)
    rate = getattr(options, 'rate', 15)
    verbose = getattr(options, 'verbose', False)
    cycle = getattr(options, 'cycle', False)

    ch = logging.StreamHandler(sys.stderr)
    ch.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
    ch.setLevel(logging.WARNING)
    logger.addHandler(ch)

    class VerboseFilter(logging.Filter):
        def filter(self, rec):
            return rec.levelno in (logging.DEBUG, logging.INFO)

    if verbose:
        ch = logging.StreamHandler(sys.stdout)
        ch.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
        ch.setLevel(logging.DEBUG)
        ch.addFilter(VerboseFilter())
        logger.addHandler(ch)
        logger.setLevel(logging.DEBUG)

    logger.addHandler(ch)

    fortune = JsonFortune(source, speak, module, rate, cycle, options.timer)
    fortune.run()
