# -*- coding: utf-8 -*-
#
# Picard, the next-generation MusicBrainz tagger
# Copyright (C) 2018 Bob Swift
#
# 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 2
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

from picard import tagger
from picard import (PICARD_VERSION, PICARD_VERSION_STR_SHORT, log, version_from_string, version_to_string)
import picard.util.webbrowser2 as wb2
from PyQt5 import QtCore
from PyQt5.QtWidgets import QMessageBox
from picard.util import load_json
from functools import partial
import re


# Used to strip leading and trailing text from version string.
_RE_CLEAN_VERSION = re.compile('^[^0-9]*(.*)[^0-9]*$', re.IGNORECASE)


# GitHub API
GITHUB_API = {
    'host': 'api.github.com',
    'port': 443,
    'endpoint': {
        'releases': '/repos/metabrainz/picard/releases',
        'tags': '/repos/metabrainz/picard/git/refs/tags'
    }
}


class UpdateCheckManager(QtCore.QObject):

    def __init__(self):
        super().__init__()

        ''' str.key: ( str.version, ( int.major, int.minor, int.micro, str.type, int.development ), str.title, str.url ) '''
        self._available_versions = {
            'stable': ('', (0, 0, 0, 'dev', 0), '', ''),
            'beta': ('', (0, 0, 0, 'dev', 0), '', ''),
            'dev': ('', (0, 0, 0, 'dev', 0), '', ''),
        }

    def check_update(self, show_always=False, update_level='dev'):
        '''Checks if an update is available.

        Compares the version number of the currently running instance of Picard
        and displays a dialog box informing the user  if an update is available,
        with an option of opening the Picard site in the browser to download the
        update.  If there is no update available, no dialog will be shown unless
        the "show_always" parameter has been set to True.  This allows for silent
        checking during startup if so configured.
        '''
        msg_title = _("Picard Update")
        if (compare_versions(PICARD_VERSION, self._available_versions['stable'][1]) > 0) | (
                    (compare_versions(PICARD_VERSION, self._available_versions['beta'][1]) > 0) & (update_level == 'dev')):
            key = 'beta' if (compare_versions(self._available_versions['stable'][1],
                                              self._available_versions['beta'][1]) > 0) & (update_level == 'dev') else 'stable'
            msg_text = _("A new version of Picard is available.\n\nNew version: %s (%s)\n\n"
                         "Would you like to download the new version?") % (self._available_versions[key][2],
                                                                           self._available_versions[key][0])
            if QMessageBox.information(None, msg_title, msg_text, QMessageBox.Ok | QMessageBox.Cancel,
                                       QMessageBox.Cancel) == QMessageBox.Ok:
                wb2.open(self._available_versions[key][3])
        else:
            if show_always:
                msg_text = _("There is no update currently available.")
                QMessageBox.information(None, msg_title, msg_text, QMessageBox.Ok, QMessageBox.Ok)

    @property
    def available_versions(self):
        return self._available_versions

    def query_available_updates(self, callback=None):
        '''Gets list of releases from GitHub website api.'''
        txt = _("Getting release information from GitHub.")
        log.debug(txt)
        self.tagger.webservice.get(
            GITHUB_API['host'],
            GITHUB_API['port'],
            GITHUB_API['endpoint']['releases'],
            partial(self._releases_json_loaded, callback=callback),
            parse_response_type=None,
            priority=True,
            important=True
        )

    def _releases_json_loaded(self, response, reply, error, callback=None):
        if error:
            tagger.window.set_statusbar_message(
                N_("Error loading releases list: %(error)s"),
                {'error': reply.errorString()},
                echo=log.error
            )
        else:
            releases = load_json(response)

            for release in releases:
                ver = version_from_string(_RE_CLEAN_VERSION.findall(release['tag_name'])[0])
                key = 'beta' if release['prerelease'] else 'stable'
                if compare_versions(self._available_versions[key][1], ver) > 0:
                    temp_version = (version_to_string(ver, short=True), ver, release['name'], release['html_url'],)
                    self._available_versions[key] = (version_to_string(ver, short=True), ver, release['name'], release['html_url'],)
            for key in self._available_versions.keys():
                log.debug("Version key '%s' --> %s" % (key, self._available_versions[key],))
        if callback:
            callback()


def compare_versions(version1, version2):
    '''Compare Versions

    Compares two Picard version tuples to determine whether the second tuple
    contains a higher version number than the first tuple.

    Returns:  -1 if version2 is lower than version1
               0 if version2 is the same as version1
               1 if version2 is higher than version1
    '''

    # Create test copies that can be modified
    test1 = list(version1)
    test2 = list(version2)

    # Set sort order for release type element
    test1[3] = 1 if test1[3] == 'final' else 0
    test2[3] = 1 if test2[3] == 'final' else 0

    # Compare elements in order
    for x in range(0, 5):
        if test1[x] != test2[x]:
            return 1 if test1[x] < test2[x] else -1
    return 0