# -*- coding: UTF-8 -*-

__revision__ = '$Id$'

# Copyright (c) 2005-2009 Vasco Nunes, Piotr Ożarowski
#
# 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 Library 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
# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA

# You may use and distribute this software under the terms of the
# GNU General Public License, version 2 or later

from gettext import gettext as _
import locale
import logging
import os
import tempfile
import threading
import time
import urllib.error
import urllib.parse
import urllib.request
from urllib.parse import quote
from urllib.request import urlcleanup, FancyURLopener

from gi.repository import Gtk
import gutils

log = logging.getLogger("Griffith")


class Movie(object):
    cast = ''
    classification = ''
    country = ''
    director = ''
    genre = ''
    image = ''
    notes = ''
    number = None
    o_site = ''
    o_title = ''
    plot = ''
    rating = ''
    runtime = ''
    site = ''
    studio = ''
    title = ''
    trailer = ''
    year = ''
    screenplay = ''
    cameraman = ''
    resolution = ''
    barcode = ''

    movie_id = ''
    debug = False
    locations = ''
    engine_author = None
    engine_description = None
    engine_language = None
    engine_name = None
    engine_version = None
    page = None
    url = ''
    image_url = ''
    encode = 'utf-8'
    fields_to_fetch = []
    progress = None
    useurllib2 = False
    parent_window = None  # had been defined outside __init__

    # functions that plugin should implement: {{{
    def initialize(self):
        pass

    def get_barcode(self):
        pass

    def get_cameraman(self):
        pass

    def get_cast(self):
        pass

    def get_classification(self):
        pass

    def get_country(self):
        pass

    def get_director(self):
        pass

    def get_genre(self):
        pass

    def get_image(self):
        pass

    def get_notes(self):
        pass

    def get_o_site(self):
        pass

    def get_o_title(self):
        pass

    def get_plot(self):
        pass

    def get_rating(self):
        pass

    def get_resolution(self):
        pass

    def get_runtime(self):
        pass

    def get_screenplay(self):
        pass

    def get_site(self):
        pass

    def get_studio(self):
        pass

    def get_title(self):
        pass

    def get_trailer(self):
        pass

    def get_year(self):
        pass
    # }}}

    def __getitem__(self, key):
        return getattr(self, key)

    def __setitem__(self, key, value):
        setattr(self, key, value)

    def get_movie(self, parent_window=None):
        try:
            #
            # initialize the progress dialog once for the following loading process
            #
            if self.progress is None:
                self.progress = Progress(parent_window)
            self.progress.set_data(
                parent_window, _("Checking connection"), _("Wait a moment"),
                True)
            #
            # check for internet connection
            urllib.request.urlopen("https://www.google.com")
            self.progress.set_data(
                parent_window, _("Fetching data"), _("Wait a moment"), True)
            #
            # get the page
            #
            if not self.open_page(parent_window):
                return None
            return True

        except Exception as exc:
            log.error(f"[E017]Caught too broad try-except,"
                      f" {type(exc).__name__}: {exc}")
            self.progress.hide()
            gutils.error(_("Connection failed."))
            return False

    def open_page(self, parent_window=None, url=None):
        log.debug("[open_page]")
        if url is None:
            url_to_fetch = self.url
        else:
            url_to_fetch = url
        if parent_window is not None:
            self.parent_window = parent_window
        self.progress.set_data(
            parent_window, _("Fetching data"), _("Wait a moment"), False)
        retriever = Retriever(
            url_to_fetch, self.parent_window, self.progress,
            useurllib2=self.useurllib2)
        retriever.start()
        log.debug(f"self.progress.status: retriever.start(), url: {self.url}")
        while retriever.is_alive():
            self.progress.pulse()
            if self.progress.status:
                retriever.join()
            while Gtk.events_pending():
                Gtk.main_iteration()
        data = None
        try:
            if retriever.exception is None:
                if retriever.html:
                    log.debug(f"HTML retrieval: {retriever.html}")
                    ifile = open(retriever.html[0], "r", encoding=self.encode, errors='replace')
                    try:
                        data = ifile.read()
                    finally:
                        ifile.close()
                    # check for gzip compressed pages before decoding to unicode
                    if len(data) > 2 and data[0:2] == '\037\213':
                        data = gutils.decompress(data)
            else:
                log.warning(f"Unable to retrieve: {retriever.exception}")
                gutils.urllib_error(_("Connection error"), self.parent_window)
        except IOError as exc:
            log.exception(f"IOError: {exc}")
        log.debug(f"Retrieved data size: {len(data)}")
        if url is None:
            self.page = data
        urlcleanup()
        return data

    def fetch_picture(self):
        if self.image_url:
            tmp_dest = tempfile.mktemp(prefix='poster_', dir=self.locations['temp'])
            log.debug(f"fetch_picture: tmp_dest: {tmp_dest}")
            self.image = tmp_dest.split('poster_', 1)[1]
            log.debug(f"fetch_picture: self.image: {self.image}")
            dest = "%s.jpg" % tmp_dest
            try:
                self.progress.set_data(
                    self.parent_window,
                    _("Fetching poster"),
                    _("Wait a moment"),
                    False)
                retriever = Retriever(self.image_url, self.parent_window,
                                      self.progress, dest,
                                      useurllib2=self.useurllib2)
                retriever.start()
                while retriever.is_alive():
                    self.progress.pulse()
                    if self.progress.status:
                        retriever.join()
                    while Gtk.events_pending():
                        Gtk.main_iteration()
                urlcleanup()
            except Exception as exc:
                log.exception(f"[E018]Caught too broad try-except,"
                              f" {type(exc).__name__}: {exc}")
                self.image = ""
                try:
                    os.remove("%s.jpg" % tmp_dest)
                except Exception as exc:
                    log.info(f"Can't remove {tmp_dest} file, {exc}")
        else:
            self.image = ""

    def parse_movie(self):
        log.debug("[parse_movie]")
        try:
            fields = list(self.fields_to_fetch)  # make a copy

            self.initialize()

            if 'year' in fields:
                self.get_year()
                self.year = gutils.digits_only(self.year, 2100)
                fields.pop(fields.index('year'))
            if 'runtime' in fields:
                self.get_runtime()
                self.runtime = gutils.digits_only(self.runtime)
                fields.pop(fields.index('runtime'))
            if 'rating' in fields:
                self.get_rating()
                self.rating = gutils.digits_only(self.rating, 10)
                fields.pop(fields.index('rating'))
            if 'cast' in fields:
                self.get_cast()
                self.cast = gutils.clean(self.cast)
                if not isinstance(self.cast, str):
                    self.cast = gutils.gdecode(self.cast, self.encode)
                fields.pop(fields.index('cast'))
            if 'plot' in fields:
                self.get_plot()
                log.debug(f"self.plot: {self.plot}")
                self.plot = gutils.clean(self.plot)
                if not isinstance(self.plot, str):
                    self.plot = gutils.gdecode(self.plot, self.encode)
                fields.pop(fields.index('plot'))
            if 'notes' in fields:
                self.get_notes()
                self.notes = gutils.clean(self.notes)
                if not isinstance(self.notes, str):
                    self.notes = gutils.gdecode(self.notes, self.encode)
                fields.pop(fields.index('notes'))
            if 'image' in fields:
                log.debug(f"image in fields: {fields}")
                self.get_image()
                log.debug(f"self.image_url: {self.image_url} self.image: {self.image}")
                self.fetch_picture()
                fields.pop(fields.index('image'))

            for i in fields:
                getattr(self, "get_%s" % i)()
                self[i] = gutils.clean(self[i])
                if not isinstance(self[i], str):
                    self[i] = gutils.gdecode(self[i], self.encode)

            if 'o_title' in self.fields_to_fetch and self.o_title is not None:
                if self.o_title[:4] == 'The ':
                    self.o_title = self.o_title[4:] + ', The'
            if 'title' in self.fields_to_fetch and self.title is not None:
                if self.title[:4] == 'The ':
                    self.title = self.title[4:] + ', The'
        except Exception as exc:
            log.exception(f"{exc}")
        finally:
            # close the progress dialog which was opened in get_movie
            self.progress.hide()


class SearchMovie(object):
    page = None
    number_results = None
    titles = []
    ids = []
    url = None
    encode = 'utf-8'
    original_url_search = None
    translated_url_search = None
    elements = None
    title = None
    remove_accents = True
    progress = None
    useurllib2 = False
    usepostrequest = False

    def __init__(self):
        pass

    def search_movies(self, parent_window):
        try:
            #
            # initialize the progress dialog once for the following search process
            #
            if self.progress is None:
                self.progress = Progress(parent_window)
            self.progress.set_data(parent_window, _("Searching"), _("Wait a moment"), True)
            #
            # call the plugin specific search method
            #
            return self.search(parent_window)
        finally:
            # don't forget to hide the progress dialog
            self.progress.hide()

    def open_search(self, parent_window, destination=None):
        log.debug("[open_search]")
        self.titles = [""]
        self.ids = [""]

        self.title = quote(self.title)
        if self.url.find('%s') > 0:
            self.url %= self.title
            self.url = self.url.replace(' ', '%20')
        else:
            if not self.usepostrequest:
                tempstr = self.url + self.title
                self.url = tempstr.replace(' ', '%20')
        url = self.url
        self.progress.set_data(parent_window, _("Searching"), _("Wait a moment"), True)
        if self.usepostrequest:
            postdata = self.get_postdata()
            retriever = Retriever(
                url, parent_window, self.progress, destination,
                useurllib2=self.useurllib2, postdata=postdata)
        else:
            retriever = Retriever(url, parent_window, self.progress,
                                  destination, useurllib2=self.useurllib2)
        retriever.start()
        while retriever.is_alive():
            self.progress.pulse()
            if self.progress.status:
                retriever.join()
            while Gtk.events_pending():
                Gtk.main_iteration()
        try:
            if retriever.exception is None:
                if destination:
                    # caller gave an explicit destination file
                    # don't care about the content
                    return True
                if retriever.html:
                    with open(retriever.html[0], 'rb') as ifile:
                        self.page = ifile.read()
                    # check for gzip compressed pages before decoding to unicode
                    if len(self.page) > 2 and self.page[0:2] == '\037\213':
                        self.page = gutils.decompress(self.page)
                    self.page = self.page.decode(self.encode, 'replace')
                else:
                    return False
            else:
                self.progress.hide()
                gutils.urllib_error(_("Connection error"), parent_window)
                return False
        except IOError as exc:
            log.exception(f"IOError: {exc}")
        finally:
            urlcleanup()
        return True

    def get_postdata(self):
        # sample, depends on target site
        # return {'title' : self.Title, 'category' : 'movieTitel' }
        return {}


class Retriever(threading.Thread):

    def __init__(self, url, parent_window, progress, destination=None, useurllib2=False, postdata=None):
        self.URL = url
        self.html = None
        self.exception = None
        self.destination = destination
        self.useurllib2 = useurllib2
        self.progress = progress
        self._stopevent = threading.Event()
        self._sleepperiod = 1.0
        self.postdata = postdata
        threading.Thread.__init__(self, name="Retriever")

    def run(self):
        try:
            if self.useurllib2:
                if self.postdata:
                    encodedpostdata = urllib.parse.urlencode(self.postdata)
                    self.html = urlretrieve2(self.URL, self.destination,
                                             self.hook, encodedpostdata)
                    log.debug(f"urllib2 POST HTML=\n{self.html}")
                else:
                    self.html = urlretrieve2(self.URL, self.destination,
                                             self.hook)
            else:
                if self.postdata:
                    encodedpostdata = urllib.parse.urlencode(self.postdata)
                    log.debug(f"encodedpostdata={encodedpostdata}")
                    self.html = urlretrieve(self.URL, self.destination,
                                            self.hook, encodedpostdata)
                    log.debug(f"urllib POST data={self.postdata} "
                              f"HTML=\n{self.html}")
                else:
                    try:
                        log.debug(f"No encodedpostdata")
                        self.html = urlretrieve(self.URL, self.destination,
                                                self.hook)
                        log.debug(f"urllib POST HTML=\n{self.html}")
                    except ValueError as exc:
                        msg = f"Could not retrieve data from URL {self.URL}"
                        log.debug(f"No exception 'ValueError', "
                                  f"self.html={self.html}")
                        log.error(f"{msg}, {exc}")
                        gutils.error(_(msg))
            if self.progress.status:
                log.debug(f"self.progress.status={self.progress.status}")
                self.html = []
        except Exception as exc:
            log.exception(f"[E019]Caught too broad try-except,"
                          f" {type(exc).__name__}: {exc}")
            self.exception = exc

    def hook(self, count, block_size, total_size):
        if total_size == -1:
            pass
        else:
            try:
                downloaded_percentage = min((count * block_size * 100) /
                                            total_size, 100)
            except:
                downloaded_percentage = 100
            if count != 0:
                downloaded_kbyte = int(count * block_size / 1024.0)
                filesize_kbyte = int(total_size / 1024.0)

#
# use own derived URLopener class because we need to set a correct User-Agent
# string for some websites. The default is 'Python-urllib/<version>'
# which not all sites accepting. (zelluloid.de for example)
#


_uaurlopener = None
_tempfilecleanup = None


def urlretrieve(url, filename=None, reporthook=None, data=None):
    global _uaurlopener
    if not _uaurlopener:
        _uaurlopener = UAFancyURLopener()
    log.debug(f"url='{url}' _uaurlopener={_uaurlopener}")
    if _uaurlopener and url:
        try:
            ua_url_opener = _uaurlopener.retrieve(url, filename, reporthook,
                                                  data)
        except (AttributeError, OSError) as exc:
            msg = f"Retrieving URL={url}. filename={filename} failed. exc={exc}"
            log.error(msg)
            gutils.error(msg)
            ua_url_opener = None
    else:
        return None
    if ua_url_opener:
        return _uaurlopener.retrieve(url, filename, reporthook, data)
    else:
        return None


def urlretrieve2(url, filename=None, reporthook=None, data=None):
    global _tempfilecleanup
    headers = {
        # 'User-Agent': 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0; MSAppHost/1.0)',
        # 'User-Agent': 'Dalvik/1.6.0 (Linux; U; Android 4.2.2; Nexus 4 Build/JDQ39E)'
        'User-Agent': 'Dalvik/1.2.0 (Linux; U; Android 2.2.2; Huawei U8800-51 Build/HWU8800B635)',
        'Cache-Control': 'no-cache',
        'Pragma': 'no-cache',
        'Accept-Encoding': 'gzip'}
    req = urllib.request.Request(url, data, headers)
    response = urllib.request.urlopen(req)
    if not filename:
        import tempfile
        (fd, filename) = tempfile.mkstemp()
        if not _tempfilecleanup:
            _tempfilecleanup = TempFileCleanup()
        _tempfilecleanup._tempfiles.append(filename)
    else:
        fd = filename
    with open(fd, 'wb') as tfp:
        while 1:
            block = response.read(4096)
            if block == "":
                break
            tfp.write(block)
    return [filename, response.info()]


def get_locale():
    os_locale = locale.getdefaultlocale()[0]
    if os_locale is None:
        return "en-US"
    return os_locale.replace('_', '-')


class UAFancyURLopener(FancyURLopener):
    # use Firefox 3.0 User-Agent string from Windows XP
    version = 'Mozilla/5.0 (Windows; U; Windows NT 6.0) Gecko/2008052906 Firefox/3.0'

    def __init__(self, *args, **kwargs):
        FancyURLopener.__init__(self, *args, **kwargs)
        # additional HTTP headers to work around the html file compression which
        # is used by UMTS connections. compression breaks the movie import plugins sometimes
        self.addheader('Cache-Control', 'no-cache')
        self.addheader('Pragma', 'no-cache')
        self.addheader('Accept-Language', '%s,en;q=0.5' % get_locale())

    def open(self, fullurl, data=None):
        # prevent blocking calls which doesn't come back
        import socket
        socket.setdefaulttimeout(20)
        try:
            fancy_url_opener_fp = FancyURLopener.open(self, fullurl, data)
        except urllib.error.URLError as exc:
            log.error(f"Can't open URL {fullurl}, exception: {exc}")
            fancy_url_opener_fp = None
        return fancy_url_opener_fp


class TempFileCleanup:
    _tempfiles = []

    def __init__(self):
        self.__unlink = os.unlink

    def __del__(self):
        self.cleanup()

    def cleanup(self):
        if self._tempfiles:
            for file in self._tempfiles:
                try:
                    self.__unlink(file)
                except OSError:
                    pass
            del self._tempfiles[:]


class Progress:

    def __init__(self, window, title='', message=''):
        self.status = False
        self.dialog = Gtk.Dialog(title, window, Gtk.DialogFlags.MODAL, ())
        self.dialog.set_urgency_hint(False)
        self.label = Gtk.Label()
        self.label.set_markup(message)
        self.dialog.vbox.pack_start(self.label, True, True, 0)
        self.progress = Gtk.ProgressBar()
        self.progress.set_pulse_step(0.01)
        self.dialog.vbox.pack_start(self.progress, False, False, 0)
        self.button = Gtk.Button(_("Cancel"), Gtk.STOCK_CANCEL)
        self.button.connect("clicked", self.callback)
        self.dialog.vbox.pack_start(self.button, False, False, 0)
        self.dialog.show_all()

    def callback(self, widget):
        self.dialog.hide()
        self.status = True

    def pulse(self):
        self.progress.pulse()
        time.sleep(0.01)

    def close(self):
        self.dialog.destroy()

    def hide(self):
        self.dialog.hide()

    def set_data(self, parent_window, title, message, showit):
        # self.dialog.set_parent(parent_window)
        self.dialog.set_title(title)
        self.label.set_markup(message)
        if showit is True:
            self.dialog.show()
# vim: fdm=marker
