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

# Copyright(C) 2013 Mark Tully <markjtully@gmail.com>
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, 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/>.

from gi.repository import GLib, Gio
from gi.repository import Unity
import gettext
import urllib.parse
import dbus
import hashlib
import unicodedata
import os

APP_NAME = 'unity-scope-audacious'
LOCAL_PATH = '/usr/share/locale/'
gettext.bindtextdomain(APP_NAME, LOCAL_PATH)
gettext.textdomain(APP_NAME)
_ = gettext.gettext

GROUP_NAME = 'com.canonical.Unity.Scope.Music.Audacious'
UNIQUE_PATH = '/com/canonical/unity/scope/music/audacious'

SVG_DIR = '/usr/share/icons/unity-icon-theme/places/svg/'
CAT_0_ICON = Gio.ThemedIcon.new(SVG_DIR + 'group-music.svg')
CAT_0_TITLE = _('Categorytitle')

SEARCH_HINT = _('Search Audacious')
NO_RESULTS_HINT = _('Sorry, there are no Audacious results that match your search.')
PROVIDER_CREDITS = _('')
SVG_DIR = '/usr/share/icons/unity-icon-theme/places/svg/'
PROVIDER_ICON = SVG_DIR + 'service-audacious.svg'
DEFAULT_RESULT_ICON = SVG_DIR + 'result-audacious.svg'
DEFAULT_RESULT_MIMETYPE = 'taglib/mp3'
DEFAULT_RESULT_TYPE = Unity.ResultType.PERSONAL
AUDACIOUS_DBFILE = os.getenv("HOME") + "/.config/audacious/playlists/"

c1 = {'id': 'songs',
      'name': _('Songs'),
      'icon': SVG_DIR + 'group-installed.svg',
      'renderer': Unity.CategoryRenderer.VERTICAL_TILE}
c2 = {'id': 'albums',
      'name': _('Albums'),
      'icon': SVG_DIR + 'group-installed.svg',
      'renderer': Unity.CategoryRenderer.VERTICAL_TILE}
CATEGORIES = [c1, c2]

FILTERS = []

m1 = {'id'   :'album',
      'type' :'s',
      'field':Unity.SchemaFieldType.OPTIONAL}
m2 = {'id'   :'artist',
      'type' :'s',
      'field':Unity.SchemaFieldType.OPTIONAL}
m3 = {'id'   :'genre',
      'type' :'s',
      'field':Unity.SchemaFieldType.OPTIONAL}
m4 = {'id'   :'year',
      'type' :'i',
      'field':Unity.SchemaFieldType.OPTIONAL}
m5 = {'id': 'track_length',
      'type': 'i',
      'field': Unity.SchemaFieldType.OPTIONAL}
m6 = {'id': 'track_number',
      'type': 'i',
      'field': Unity.SchemaFieldType.OPTIONAL}
EXTRA_METADATA = [m1, m2, m3, m4, m5, m6]

tracks = []


def get_music_from_audacious():
    '''
    Parses Audacious's database into a form we can use
    '''
    uri = ""
    title = ""
    artist = ""
    album = ""
    year = 0
    genre = ""
    tracknumber = 0
    length = 0
    global tracks
    tracks = []
    for collection in os.listdir(AUDACIOUS_DBFILE):
        dbfile = '%s/%s' % (AUDACIOUS_DBFILE, collection)
        database = open(dbfile, "r")
        database = database.read()
        if not database.startswith("title:Library"):
            records = database[14:]
            records = records.split("uri=")
        else:
            records = ""

        for record in records:
            track = []
            lines = record.split("\n")
            uri = ""
            year = 0
            for line in lines:
                line = urllib.parse.unquote(line)
                if line.startswith("file://"):
                    uri = line[7:]
                if line.startswith("title="):
                    title = line[6:]
                if line.startswith("artist="):
                    artist = line[7:]
                if line.startswith("album="):
                    album = line[6:]
                if line.startswith("year="):
                    year = line[5:]
                if line.startswith("genre="):
                    genre = line[6:]
                if line.startswith("track-number="):
                    tracknumber = line[13:]
                if line.startswith("length="):
                    length = line[7:]
            track = [title, uri, artist, album, "taglib/mp3", int(year), genre, int(tracknumber), int(length) / 1000]
            tracks.append(track)
    tracks.sort(key=lambda track: track[7])
    return tracks


def get_album_art(track):
    dirname = dirname = os.path.dirname(track[1])
    if os.path.exists(dirname + "/cover.jpg"):
        return dirname + "/cover.jpg"

    if os.path.exists(dirname + "/folder.jpg"):
        return dirname + "/folder.jpg"
        
    if os.path.exists(dirname + "/album.jpg"):
        return dirname + "/album.jpg"

    # Try thumbnailing any embedded album art and use that
    hashname = '%s\t%s' % (unicodedata.normalize("NFKD", track[2]), unicodedata.normalize("NFKD", track[3]))
    file_hash = hashlib.md5(hashname.encode('utf-8')).hexdigest()
    tb_filename = os.path.join(os.path.expanduser("~/.cache/media-art"), ("album-" + file_hash)) + ".jpg"
    if os.path.exists(tb_filename):
        return tb_filename
    else:
        try:
            from mutagen import File
            trackfile = track[1][7:]
            trackfile.replace("%20", " ")
            trackfile = urllib.parse.unquote(trackfile)
            audio = File(trackfile)
            if "APIC" in audio:
                artwork = audio.tags["APIC:"].data
                if not os.path.exists(os.path.expanduser("~/.cache/media-art")):
                    os.makedirs(os.path.expanduser("~/.cache/media-art"))
                with open(tb_filename, "wb") as img:
                    img.write(artwork)
                return tb_filename
            else:
                return "media-audio"
        except:
            # Otherwise, return a generic audio icon
            return "media-audio"


def search(search, filters):
    '''
    Search for help documents matching the search string
    '''
    results = []
    global tracks
    if len(tracks) == 0:
        tracks = get_music_from_audacious()
    trackresults = []
    albumresults = []

    for track in tracks:
        title = "" if track[0] is None else track[0]
        uri = "" if track[1] is None else track[1]
        artist = "" if track[2] is None else track[2]
        album = "" if track[3] is None else track[3]
        mimetype = "" if track[4] is None else track[4]
        albumartist = "" if track[5] is None else track[2]
        year = 0 if track[5] is None else int(track[5])
        track_length = 0 if track[8] is None else track[8]
        track_number = 0 if track[7] is None else track[7]
        genre = "" if track[6] is None else track[6]
        trackname = title + " - " + album + " - " + artist
        if search.lower() in trackname.lower():
            albumart = get_album_art(track)
            albumuri = "album://" + albumartist + "/" + album
            if uri not in trackresults:
                results.append({'uri': uri,
                                'icon': albumart,
                                'category': 0,
                                'title': title,
                                'album': GLib.Variant('s', album),
                                'artist': GLib.Variant('s', artist),
                                'genre': GLib.Variant('s', genre),
                                'year': GLib.Variant('i', year),
                                'track_length': GLib.Variant('i', track_length),
                                'track_number': GLib.Variant('i', track_number)})
                trackresults.append(uri)

            if albumuri not in albumresults:
                results.append({'uri': albumuri,
                                'icon': albumart,
                                'category': 1,
                                'title': album,
                                'album':GLib.Variant('s', album),
                                'artist':GLib.Variant('s', artist),
                                'genre':GLib.Variant('s', genre),
                                'year':GLib.Variant('i', year),
                                'track_length': GLib.Variant('i', track_length),
                                'track_number': GLib.Variant('i', track_number)})
                albumresults.append(albumuri)
    return results


class Preview(Unity.ResultPreviewer):

    def do_run(self):
        global tracks
        album = self.result.metadata['album'].get_string()
        artist = self.result.metadata['artist'].get_string()
        preview = Unity.MusicPreview.new(self.result.title, '', None)
        preview.props.image_source_uri = 'file://%s' % self.result.icon_hint
        preview.props.subtitle = self.result.metadata['artist'].get_string()
        if self.result.uri.startswith("album://"):
            for track in tracks:
                if album in track[3] and artist in track[2]:
                    track = Unity.TrackMetadata.full('file://%s' % track[1],
                                                     int(track[7]),
                                                     track[0],
                                                     track[2],
                                                     track[3],
                                                     track[8])
                    preview.add_track(track)
        else:
            track = Unity.TrackMetadata.full('file://%s' % self.result.uri,
                                             self.result.metadata['track_number'].get_int32(),
                                             self.result.title,
                                             self.result.metadata['artist'].get_string(),
                                             self.result.metadata['album'].get_string(),
                                             self.result.metadata['track_length'].get_int32())
            preview.add_track(track)

        view_action = Unity.PreviewAction.new("play", _("Play"), None)
        preview.add_action(view_action)
        show_action = Unity.PreviewAction.new("show", _("Show in Folder"), None)
        preview.add_action(show_action)
        return preview


# Classes below this point establish communication
# with Unity, you probably shouldn't modify them.


class MySearch(Unity.ScopeSearchBase):
    def __init__(self, search_context):
        super(MySearch, self).__init__()
        self.set_search_context(search_context)

    def do_run(self):
        '''
        Adds results to the model
        '''
        try:
            result_set = self.search_context.result_set
            for i in search(self.search_context.search_query,
                            self.search_context.filter_state):
                if not 'uri' in i or not i['uri'] or i['uri'] == '':
                    continue
                if not 'icon' in i or not i['icon'] or i['icon'] == '':
                    i['icon'] = DEFAULT_RESULT_ICON
                if not 'mimetype' in i or not i['mimetype'] or i['mimetype'] == '':
                    i['mimetype'] = DEFAULT_RESULT_MIMETYPE
                if not 'result_type' in i or not i['result_type'] or i['result_type'] == '':
                    i['result_type'] = DEFAULT_RESULT_TYPE
                if not 'category' in i or not i['category'] or i['category'] == '':
                    i['category'] = 0
                if not 'title' in i or not i['title']:
                    i['title'] = ''
                if not 'comment' in i or not i['comment']:
                    i['comment'] = ''
                if not 'dnd_uri' in i or not i['dnd_uri'] or i['dnd_uri'] == '':
                    i['dnd_uri'] = i['uri']
                i['provider_credits'] = GLib.Variant('s', PROVIDER_CREDITS)
                result_set.add_result(**i)
        except Exception as error:
            print(error)


class Scope(Unity.AbstractScope):
    def __init__(self):
        Unity.AbstractScope.__init__(self)

    def do_get_search_hint(self):
        return SEARCH_HINT

    def do_get_schema(self):
        '''
        Adds specific metadata fields
        '''
        schema = Unity.Schema.new()
        if EXTRA_METADATA:
            for m in EXTRA_METADATA:
                schema.add_field(m['id'], m['type'], m['field'])
        #FIXME should be REQUIRED for credits
        schema.add_field('provider_credits', 's', Unity.SchemaFieldType.OPTIONAL)
        return schema

    def do_get_categories(self):
        '''
        Adds categories
        '''
        cs = Unity.CategorySet.new()
        if CATEGORIES:
            for c in CATEGORIES:
                cat = Unity.Category.new(c['id'], c['name'],
                                         Gio.ThemedIcon.new(c['icon']),
                                         c['renderer'])
                cs.add(cat)
        return cs

    def do_get_filters(self):
        '''
        Adds filters
        '''
        fs = Unity.FilterSet.new()
        #if FILTERS:
        #
        return fs

    def do_get_group_name(self):
        return GROUP_NAME

    def do_get_unique_name(self):
        return UNIQUE_PATH

    def do_create_search_for_query(self, search_context):
        se = MySearch(search_context)
        return se

    def do_activate(self, result, metadata, id):
        album = result.metadata['album'].get_string()
        artist = result.metadata['artist'].get_string()
        global tracks
        if id == 'show':
            if result.uri.startswith("album://"):
                for track in tracks:
                    if album in track[3] and artist in track[2]:
                        filename = tracks[1]
                        continue
            else:
                filename = result.uri
            dirname = os.path.dirname(filename)
            os.system("xdg-open '%s'" % str(dirname))
        else:
            albumtracks = ''
            if result.uri.startswith('album://'):
                for track in tracks:
                    if album in track[3] and artist in track[2]:
                        albumtracks = albumtracks + ' \'%s\'' % (track[1])
            else:
                albumtracks = '\'%s\'' % result.uri
            print(albumtracks)
            os.system('audacious -E %s' % albumtracks)

        return Unity.ActivationResponse(handled=Unity.HandledType.HIDE_DASH, goto_uri=None)

    def do_create_previewer(self, result, metadata):
        rp = Preview()
        rp.set_scope_result(result)
        rp.set_search_metadata(metadata)
        return rp


def load_scope():
    return Scope()
