# Copyright 2018 Phidica Veia
#
# 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.

import os
import sqlite3

from gi.repository import Gtk
from senf import uri2fsn, text2fsn

from quodlibet import _
from quodlibet import app
from quodlibet import ngettext
from quodlibet import util
from quodlibet.qltk.entry import UndoEntry
from quodlibet.qltk import Icons
from quodlibet.qltk.msg import Message, WarningMessage, ErrorMessage
from quodlibet.util.path import normalize_path
from quodlibet.plugins.events import EventPlugin


class BansheeDBImporter:
    def __init__(self, library):
        self._library = library
        self._changed_songs = []

    def read(self, db):
        """Iterate through the database and import data for songs found in
        the library
        """

        # use the Row class for extracting rows
        db.row_factory = sqlite3.Row

        # iterate over all songs in the database
        # throws sqlite3.OperationalError if CoreTracks is not found
        for row in db.execute("SELECT * FROM CoreTracks"):
            try:
                filename = uri2fsn(row["Uri"])
            except ValueError:
                continue

            song = self._library.get(normalize_path(filename))
            if not song:
                continue

            has_changed = False

            # rating is stored as integer from 0 to 5
            b_rating = row["Rating"] / 5.0
            if b_rating != song("~#rating"):
                song["~#rating"] = b_rating
                has_changed = True

            # play count is stored as integer from 0
            if row["PlayCount"] != song("~#playcount"):
                # summing play counts would break on multiple imports
                song["~#playcount"] = row["PlayCount"]
                has_changed = True

            # skip count is stored as integer from 0
            if row["SkipCount"] != song("~#skipcount"):
                song["~#skipcount"] = row["SkipCount"]
                has_changed = True

            # timestamp is stored as integer or None
            if row["LastPlayedStamp"] is not None:
                value = row["LastPlayedStamp"]
                # keep timestamp if it is newer than what we had
                if value > song("~#lastplayed", 0):
                    song["~#lastplayed"] = value
                    has_changed = True

            if row["DateAddedStamp"] is not None:
                value = row["DateAddedStamp"]
                # keep timestamp if it is older than what we had
                if value < song("~#added", 0):
                    song["~#added"] = value
                    has_changed = True

            if has_changed:
                self._changed_songs.append(song)

    def finish(self):
        """Call at the end, also returns number of songs with data imported"""

        count = len(self._changed_songs)
        self._library.changed(self._changed_songs)
        self._changed_songs = []
        return count


def do_import(parent, library):
    db_path = os.path.expanduser(BansheeImport.USR_PATH)

    importer = BansheeDBImporter(library)
    try:
        db = sqlite3.connect(db_path)
        importer.read(db)
        db.close()
    except sqlite3.OperationalError:
        msg = _("Specified Banshee database is malformed or missing")
        WarningMessage(parent, BansheeImport.PLUGIN_NAME, msg).run()
    except Exception:
        util.print_exc()
        importer.finish()
        msg = _("Import Failed")
        # FIXME: don't depend on the plugin class here
        ErrorMessage(parent, BansheeImport.PLUGIN_NAME, msg).run()
    else:
        count = importer.finish()
        msg = (
            ngettext(
                "Successfully imported ratings and statistics for %d song",
                "Successfully imported ratings and statistics for %d songs",
                count,
            )
            % count
        )
        Message(Gtk.MessageType.INFO, parent, BansheeImport.PLUGIN_NAME, msg).run()


class BansheeImport(EventPlugin):
    PLUGIN_ID = "bansheeimport"
    PLUGIN_NAME = _("Banshee Import")
    PLUGIN_DESC = _("Imports ratings and song statistics from Banshee.")
    PLUGIN_ICON = Icons.DOCUMENT_OPEN

    DEF_PATH = "~/.config/banshee-1/banshee.db"
    USR_PATH = DEF_PATH

    def PluginPreferences(self, *args):
        grid = Gtk.Grid(row_spacing=6, column_spacing=6)

        label = Gtk.Label(label=_("_Database path:"), use_underline=True)
        label.set_alignment(0.0, 0.5)
        grid.attach(label, 0, 0, 1, 1)

        entry = UndoEntry()
        entry.set_hexpand(True)
        entry.set_text(BansheeImport.DEF_PATH)

        def path_activate(entry, *args):
            path = text2fsn(entry.get_text())
            if BansheeImport.USR_PATH != path:
                BansheeImport.USR_PATH = path

        entry.connect_after("activate", path_activate)
        entry.connect_after("focus-out-event", path_activate)
        grid.attach_next_to(entry, label, Gtk.PositionType.RIGHT, 1, 1)

        path_revert = Gtk.Button()
        path_revert.add(
            Gtk.Image.new_from_icon_name(Icons.DOCUMENT_REVERT, Gtk.IconSize.MENU)
        )

        def path_revert_cb(button, entry):
            entry.set_text(BansheeImport.DEF_PATH)
            entry.emit("activate")

        path_revert.connect("clicked", path_revert_cb, entry)
        grid.attach_next_to(path_revert, entry, Gtk.PositionType.RIGHT, 1, 1)

        button = Gtk.Button(label=_("Start Import"))

        def clicked_cb(button):
            do_import(button, app.library)

        button.connect("clicked", clicked_cb)

        box = Gtk.VBox(spacing=12)

        box.pack_start(grid, True, True, 0)
        box.pack_start(button, False, False, 0)

        return box
