#-*- coding:utf-8 -*-

#  Copyright © 2009, 2011-2017  B. Clausius <barcc@gmx.de>
#
#  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 <http://www.gnu.org/licenses/>.



import re
import html

from . import config
from .settings import settings
from .ext import qt  #FIXME: buildlib.utils imports dialogs and the purepython version of qt with PyQt5
from .theme import Theme
from .model import Model
from .application import Meta  #FIXME: Circular import: works because dialogs is imported only inside functions

try:
    _
except NameError:
    _ = lambda t: t
    
    
def linkedtext_to_html(text):
    html = qt.text_to_html(text)
    html = re.sub(r'&lt;((?:http:|https:|text:).*?)\|&gt;', r'<a href="\1">', html)
    html = re.sub(r'&lt;\|&gt;', r'</a>', html)
    return re.sub(r'&lt;((?:http:|https:).*?)&gt;', r'&lt;<a href="\1">\1</a>&gt;', html)
    
    
def init_preferences(sample_buffers):
    shader_names = {'lighting': _('Lighting'), 'simple': _('Simple'),
                    #TRANSLATORS: labels are the colored stickers on the cube
                    'label': _('Labels')}
    facenames = [(_(name), key) for key, name in Model.cache_index['facenames']]
    qt.preferences_dialog(shader_names, sample_buffers, facenames, Theme.textures.get_icons())
        
def get_langname_from_code_func():
    try:
        import icu
    except ImportError:
        print('PyICU is not installed')
        try:
            from .languages import languages
        except ImportError:
            print('No translations for language names')
            return lambda code: None
        else:
            from locale import getlocale, getdefaultlocale
            l = getlocale()[0]
            if l is None:
                l = getdefaultlocale()[0]
            if l is None:
                return lambda code: None
            try:
                languages = languages[l]
            except KeyError:
                try:
                    languages = languages[l.split('_')[0]]
                except KeyError:
                    return lambda code: None
            return lambda code: languages.get(code)
    else:
        def icu_langname_from_code(code):
            iculocale = icu.Locale.createFromName(code)
            lang = iculocale.getLanguage()
            if icu.Locale.createFromName(lang).getDisplayName() == lang:
                return None
            return str(iculocale.getDisplayName()) or None
        return icu_langname_from_code
        
class ModelSelection (metaclass=Meta):
    def __init__(self):
        self.stack = [self.get_modeldata_main]
        self.pagedata = {}
        
    def get_modeldata_main(self):
        defaultindex = -1
        defaultmtype = settings['game.type']
        index = 0
        modeldata_funcs = [None, self.get_modeldata_type1, self.get_modeldata_type2, self.get_modeldata_type3]
        factory_get_modeldata = lambda mtype, func: func and (lambda: func(mtype))
        for mtype in Model.cache_index['types']:
            if mtype == defaultmtype:
                defaultindex = index
            size = Model.cache_index['type'][mtype]['defaultsize']
            text = _(Model.cache_index['type'][mtype]['name'])
            thumbnail = '{}/thumbnails/{}-{}.png'.format(config.UI_DIR, mtype, 'x'.join(str(i) for i in size))
            yield text, thumbnail, mtype, size, factory_get_modeldata(mtype, modeldata_funcs[len(size)])
            index += 1
        yield defaultindex
        yield _('Select Puzzle')
        
    def get_modeldata_type1(self, mtype):
        defaultindex = -1
        defaultsize = settings['games',mtype,'size']
        try:
            defaultsize = Model.cache_index['normsize'][mtype][defaultsize]
        except KeyError:
            defaultsize = Model.cache_index['type'][mtype]['defaultsize']
        index = 0
        for size in sorted(set(Model.cache_index['normsize'][mtype].values())):
            if size == defaultsize:
                defaultindex = index
            text = _(Model.cache_index['type'][mtype]['mformat']).format(*size)
            thumbnail = '{}/thumbnails/{}-{}.png'.format(config.UI_DIR, mtype, 'x'.join(str(i) for i in size))
            yield text, thumbnail, mtype, size, None
            index += 1
        yield defaultindex
        yield _(Model.cache_index['type'][mtype]['name'])
        
    def get_modeldata_type2(self, mtype):
        defaultindex = -1
        defaultsize = Model.cache_index['type'][mtype]['defaultsize']
        index = 0
        factory_get_modeldata = lambda mtype, height: lambda: self.get_modeldata_typeheight2(mtype, height)
        for size in sorted(set(Model.cache_index['normsize'][mtype].values())):
            if size[0] == defaultsize[0] and size[2:] == defaultsize[2:]:
                if size == defaultsize:
                    defaultindex = index
                #TRANSLATORS: 1 slice, 2 slices, 3 slices, ...
                text = ngettext('{} slice','{} slices',size[1]).format(size[1])
                thumbnail = '{}/thumbnails/{}-{}.png'.format(config.UI_DIR, mtype, 'x'.join(str(i) for i in size))
                yield text, thumbnail, mtype, size, factory_get_modeldata(mtype, size[1])
                index += 1
        yield defaultindex
        yield _(Model.cache_index['type'][mtype]['name'])
        
    def get_modeldata_type3(self, mtype):
        defaultindex = -1
        defaultsize = Model.cache_index['type'][mtype]['defaultsize']
        index = 0
        factory_get_modeldata = lambda mtype, height: lambda: self.get_modeldata_typeheight3(mtype, height)
        for size1 in range(1,11):  #FIXME: hardcoded limit
            try:
                size = Model.cache_index['normsize'][mtype][(size1, defaultsize[0], defaultsize[2])]
            except KeyError as e:
                print('hm', e)
                continue
            if size == defaultsize:
                defaultindex = index
            text = ngettext('{} slice','{} slices',size1).format(size1)
            thumbnail = '{}/thumbnails/{}-{}.png'.format(config.UI_DIR, mtype, 'x'.join(str(i) for i in size))
            yield text, thumbnail, mtype, size, factory_get_modeldata(mtype, size1)
            index += 1
        yield defaultindex
        yield _(Model.cache_index['type'][mtype]['name'])
        
    def get_modeldata_typeheight2(self, mtype, height):
        defaultindex = -1
        defaultsize = settings['games',mtype,'size']
        try:
            defaultsize = Model.cache_index['normsize'][mtype][defaultsize]
        except KeyError:
            defaultsize = Model.cache_index['type'][mtype]['defaultsize']
        if defaultsize[1] != height:
            defaultsize = tuple((height if i==1 else s) for i,s in enumerate(defaultsize))
        index = 0
        for size in sorted(set(Model.cache_index['normsize'][mtype].values())):
            if size[1] == height:
                if size == defaultsize:
                    defaultindex = index
                text = _(Model.cache_index['type'][mtype]['mformat']).format(*size)
                thumbnail = '{}/thumbnails/{}-{}.png'.format(config.UI_DIR, mtype, 'x'.join(str(i) for i in size))
                yield text, thumbnail, mtype, size, None
                index += 1
        yield defaultindex
        yield _(Model.cache_index['type'][mtype]['name'])+' – '+ngettext('{} slice','{} slices',height).format(height)
        
    def get_modeldata_typeheight3(self, mtype, height):
        defaultindex = -1
        defaultsize = Model.cache_index['type'][mtype]['defaultsize']
        if defaultsize[1] != height:
            defaultsize = tuple((height if i==1 else s) for i,s in enumerate(defaultsize))
        index = 0
        factory_get_modeldata = lambda mtype, height, width: lambda: self.get_modeldata_typeheightwidth3(mtype, height, width)
        for width in range(1,11):  #FIXME: hardcoded limit
            try:
                size = Model.cache_index['normsize'][mtype][(width, height, defaultsize[2])]
            except KeyError:
                continue
            if size == defaultsize:
                defaultindex = index
            text = _('{}×{} slices').format(width, height)
            thumbnail = '{}/thumbnails/{}-{}.png'.format(config.UI_DIR, mtype, 'x'.join(str(i) for i in size))
            yield text, thumbnail, mtype, size, factory_get_modeldata(mtype, height, width)
            index += 1
        yield defaultindex
        yield _(Model.cache_index['type'][mtype]['name'])+' – '+ngettext('{} slice','{} slices',height).format(height)
        
    def get_modeldata_typeheightwidth3(self, mtype, height, width):
        defaultindex = -1
        defaultsize = settings['games',mtype,'size']
        try:
            defaultsize = Model.cache_index['normsize'][mtype][defaultsize]
        except KeyError:
            defaultsize = Model.cache_index['type'][mtype]['defaultsize']
        if defaultsize[1] != height or defaultsize[0] != width:
            defaultsize = (width, height, defaultsize[2])
        index = 0
        for depth in range(1,11):  #FIXME: hardcoded limit
            try:
                size = Model.cache_index['normsize'][mtype][(width, height, depth)]
            except KeyError:
                continue
            if size == defaultsize:
                defaultindex = index
            text = _(Model.cache_index['type'][mtype]['mformat']).format(*size)
            thumbnail = '{}/thumbnails/{}-{}.png'.format(config.UI_DIR, mtype, 'x'.join(str(i) for i in size))
            yield text, thumbnail, mtype, size, None
            index += 1
        yield defaultindex
        yield _(Model.cache_index['type'][mtype]['name'])+' – '+_('{}×{} slices').format(width, height)
        
    def on_model_get_modeldata(self, pageindex):
        assert pageindex >= 1, (pageindex, self.stack)
        func = self.stack[pageindex-1]
        try:
            items, defaultindex, title = self.pagedata[func]
        except KeyError:
            *items, defaultindex, title = func()
            self.pagedata[func] = [items, defaultindex, title]
        return items, defaultindex, title
    
    def on_model_back(self):
        if len(self.stack) > 1:
            self.stack.pop()
            return True
        return False
    
    def on_convert_to_mtype_size(self, pageindex, index):
        func = self.stack[pageindex-1]
        self.pagedata[func][1] = index
        unused_text, unused_thumbnail, mtype, size, func = self.pagedata[func][0][index]
        if func is None:
            return mtype, size
        else:
            self.stack[pageindex:] = [func]
            return None
    
    
class ProgressDialog:
    def __init__(self):
        self.canceled_ = False
        self.value_max = 10
        self.value = 0
        
    def on_canceled(self):
        self.canceled_ = True
        #XXX: currently not used, so not translation needed
        print(('Canceling operation, please wait'))
        
    def tick(self, step, message=None, value_max=None):
        if message is not None:
            print(message)
        if value_max is not None:
            self.value_max = value_max
            self.value = 0
        if step < 0 or self.value > self.value_max:
            print('  tick')
        else:
            print(' ', self.value, '/', self.value_max)
            self.value += step
        return self.canceled_
        
    def done(self):
        self.canceled_ = False
        
        
class DialogValues:
    @classmethod
    def get_text(self, string):
        return getattr(self, string)()
        
    helpstrings = [
            #TRANSLATORS: The next strings form the text in the help dialog:
            #             Title of the 1st paragraph
            _('Using the mouse to rotate the cube'),
            #TRANSLATORS: Text of the 1st paragraph, followed by bullet list items
            _('Position the mouse cursor over the puzzle and you will see an arrow '
              'that gives you a hint in which direction the slice under the mouse cursor will be rotated.'),
            _('The left mouse button rotates a single slice of the cube in the direction of the arrow.'),
            _('The right mouse button rotates a single slice of the cube against the direction of the arrow.'),
            _('To rotate the whole cube instead of a single slice press the Ctrl key together with the mouse button.'),
            #TRANSLATORS: Title of the 2nd paragraph
            _('Using the keyboard to rotate the cube'),
            #TRANSLATORS: Text of the 2nd paragraph, followed by bullet list items
            _('Make sure the keyboard focus is on the cube area (e.g. click on the background of the cube). '
              'The keys can be configured in the preferences dialog, the default is:'),
            #TRANSLATORS: before the item text is a list of keys
            _('Moves the left, right, upper, down, front or back slice clockwise.'),
            _('Moves a slice couterclockwise.'),
            _('Moves the whole cube.'),
            #TRANSLATORS: Title of the 3rd paragraph
            _('Other keys and buttons'),
            #TRANSLATORS: bullet list items of the 3rd paragraph
            _('Mouse wheel – Zoom in/out'),
            _('Arrow keys, Left mouse button on the background – Changes the direction of looking at the cube.'),
            _('Moves keyboard focus to the sequence editor above the cube area '
              'where you can edit the move sequence in the notation described below. '
              'Hit enter when done.'),
            #TRANSLATORS: Title of the 4th paragraph
            _('Notation for moves'),
            #TRANSLATORS: Text of the 4th paragraph, followed by bullet list items
            _('All moves, however they are made, are displayed progressively above the cube area:'),
            _('Moves the left, right, upper, down, front or back slice clockwise.'),
            _('Moves a slice couterclockwise.'),
            _('Moves the whole cube.'),
            _('Moves the first, second or third slice from left clockwise. '
              'The allowed numbers are in the range from 1 to the count of parallel slices. '
              '"l1" is always the same as "l" '
              'and for the classic 3×3×3-Cube "l2" is the same as "r2-" and "l3" is the same as "r-".'),
            _('You can use a space to separate groups of moves.'),
        ]
    helpformat = '''
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html>
<head>
<meta name="qrichtext" content="1" />
<style type="text/css">p, li {{ white-space: pre-wrap; }}</style>
</head>

<body>

<h3>{}</h3>
<p>{}</p>
<ul>
<li>{}</li>
<li>{}</li>
<li>{}</li>
</ul>

<h3>{}</h3>
<p>{}</p>
<ul>
<li>4,6,8,2,5,0 – {}</li>
<li>Shift+4, … – {}</li>
<li>Ctrl+4, … – {}</li>
</ul>

<h3>{}</h3>
<ul>
<li>{}</li>
<li>{}</li>
<li>Ctrl+L – {}</li>
</ul>

<h3>{}</h3>
<p>{}</p>
<ul>
<li>l, r, u, d, f, b – {}</li>
<li>l-, r-, u-, d-, f-, b- – {}</li>
<li>L, R, U, D, F, B – {}</li>
<li>l1, l2, l3 – {}</li>
<li>{}</li>
</ul>

</body></html>
    '''
    
    @classmethod
    def help_text(self):
        return self.helpformat.format(*[html.escape(s) for s in self.helpstrings])
        
    @staticmethod
    def about_website():
        return '<a href="{}">{}</a>'.format(config.WEBSITE, _('Pybik project website'))
        
    _about_translators = None
    
    @classmethod
    def about_translators(self):
        if self._about_translators is not None:
            return self._about_translators
        html_template = '''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li {{ white-space: pre-wrap; }}
</style></head>
<body>{}</body></html>'''
        html_p_template = '<p align="center">{}</p>'
        html_language_template1 = '<span style=" font-weight:600;">{}</span>'
        html_language_template2 = '{}'
        html_person_template = '<a href="{}">{}</a>'
        html_p_items = []
        from .translators import translators
        langname_from_code = get_langname_from_code_func()
        import locale
        sortfunc = lambda v: locale.strxfrm(v[0])
        def gentranslators():
            for language, langcode, complete, persons in translators:
                language = langname_from_code(langcode) or language
                yield language, complete, persons
        for language, complete, persons in sorted(gentranslators(), key=sortfunc):
            language = html.escape(language)
            html_items = [(html_language_template1 if complete else html_language_template2).format(language)]
            for name, link in persons:
                name = html.escape(name)
                html_items.append(html_person_template.format(link, name))
            html_p_items.append(html_p_template.format('<br/>'.join(html_items)))
        self._about_translators = html_template.format(''.join(html_p_items))
        return self._about_translators
        
    @staticmethod
    def about_contribute():
        return linkedtext_to_html(config.get_filebug_text() +'\n\n\n'+ _(config.TRANSLATION_TEXT))
        
    @staticmethod
    def about_license_short():
        text = '\n\n'.join((_(config.LICENSE_INFO), _(config.LICENSE_FURTHER)))
        return linkedtext_to_html(text)
        
    @staticmethod
    def about_license_full():
        try:
            with open(config.LICENSE_FILE, 'rt', encoding='utf-8') as license_file:
                text = license_file.read()
            text = linkedtext_to_html(text)
        except Exception as e:
            print('Unable to find license text:', e)
            text = ''
        return text
        
    @staticmethod
    def about_license_notfound():
        text = _(config.LICENSE_NOT_FOUND)
        return linkedtext_to_html(text)
        

