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

# Copyright (C) 2013 David Callé <davidc@framli.eu>
# Copyright (C) 2012 Chris Wayne <cwayne@ubuntu.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 Unity
from gi.repository import Gio
import json
import urllib.parse
import urllib.request
import gettext

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

GROUP_NAME = 'com.canonical.Unity.Scope.Developer.Github'
UNIQUE_PATH = '/com/canonical/unity/scope/developer/github'
SEARCH_URI = 'https://api.github.com/'
SEARCH_HINT = _('Search GitHub')
NO_RESULTS_HINT = _('Sorry, there are no GitHub results that match your search.')
PROVIDER_CREDITS = _('Powered by GitHub')
SVG_DIR = '/usr/share/icons/unity-icon-theme/places/svg/'
PROVIDER_ICON = SVG_DIR+'service-github.svg'
DEFAULT_RESULT_ICON = SVG_DIR+'result-developer.svg'
DEFAULT_RESULT_MIMETYPE = 'text/html'
DEFAULT_RESULT_TYPE = Unity.ResultType.DEFAULT
ICON_MAP = {'python':{'mime':'text/x-python'},
            'ruby':{'mime':'text/x-ruby'},
            'php':{'mime':'text/x-php'},
            'java':{'mime':'text/x-java'},
            'javascript':{'mime':'text/x-javascript'},
            'c#':{'mime':'text/x-csharp'},
            'css':{'mime':'text/x-css'},
            'html':{'mime':'text/x-html'},
            'c':{'mime':'text/x-c'},
            'shell':{'mime':'text/x-sh'},
            'perl':{'mime':'text/x-perl'},
            'lua':{'mime':'text/x-lua'},
            'vala':{'mime':'text/x-vala'},
            'html':{'mime':'text/html'},
            'text':{'mime':'text', 'icon':'text'}}
for i in ICON_MAP:
    icon_list = Gio.content_type_get_icon(ICON_MAP[i]['mime'])
    if icon_list:
        ICON_MAP[i]['icon'] = icon_list.to_string()

c1 = {'id'      :'code',
      'name'    :_('Projects'),
      'icon'    :SVG_DIR+'group-developer.svg',
      'renderer':Unity.CategoryRenderer.VERTICAL_TILE}
c2 = {'id'      :'people', 
      'name'    :_('Users'),
      'icon'    :SVG_DIR+'group-developer.svg',
      'renderer':Unity.CategoryRenderer.VERTICAL_TILE}
CATEGORIES = [c1, c2]
FILTERS = []
m1 = {'id'   :'owner',
      'type' :'s',
      'field':Unity.SchemaFieldType.OPTIONAL}
m2 = {'id'   :'followers',
      'type' :'i',
      'field':Unity.SchemaFieldType.OPTIONAL}
m3 = {'id'   :'language',
      'type' :'s',
      'field':Unity.SchemaFieldType.OPTIONAL}
EXTRA_METADATA = [m1, m2, m3]


def search(search, filters, cancellable):
    '''
    Any search method returning results as a list of tuples.
    Available tuple fields:
    uri (string)
    icon (string)
    title (string)
    comment (string)
    dnd_uri (string)
    mimetype (string)
    category (int)
    result_type (Unity ResultType)
    extras metadata fields (variant)
    '''
    results = []
    if not search or len(search) < 2:
        return results
    search = urllib.parse.quote(search)
    if search.startswith("%40"):
        uri = '%susers/%s/repos' % (SEARCH_URI, search.replace('%40',''))
    else:
        for target in ['repos']:
            uri = '%slegacy/%s/search/%s' % (SEARCH_URI, target, search)
    print(uri)
    try:
        response = urllib.request.urlopen(uri)
        if cancellable.is_cancelled():
            return []
        body = response.read()
        if cancellable.is_cancelled():
            return []
    except urllib.error.URLError as error:
        print(error)
        return []
    try:
        data = json.loads(body.decode('utf-8'))
    except ValueError as error:
        print(error)
        return []
    if not data:
        return results
    if 'repositories' in data:
        for d in data['repositories']:
            if len(results) < 25:
                src = str(d['language']).lower()
                icon = ICON_MAP['text']['icon']
                if src in ICON_MAP:
                    if 'icon' in ICON_MAP[src]:
                        icon = ICON_MAP[src]['icon']
                results.append({'uri':'https://github.com/%s/%s' % (d['owner'], d['name']),
                                'title':d['name'],
                                'comment':d['description'],
                                'owner':d['owner'],
                                'followers':d['watchers'],
                                'language':str(d['language']),
                                'icon':icon})
    else:
        for d in data:
            if len(results) < 25:
                src = str(d['language']).lower()
                icon = ICON_MAP['text']['icon']
                if src in ICON_MAP:
                    if 'icon' in ICON_MAP[src]:
                        icon = ICON_MAP[src]['icon']
                results.append({'uri':d['html_url'],
                                'title':d['name'],
                                'comment':d['description'],
                                'owner':d['owner']['login'],
                                'followers':d['watchers'],
                                'language':str(d['language']),
                                'icon':icon})
    return results


# 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,
                            self.search_context.cancellable):
                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']
                result_set.add_result(**i)
        except Exception as error:
            print (error)

class Preview (Unity.ResultPreviewer):

    def do_run(self):
        icon = Gio.FileIcon.new(Gio.file_new_for_uri(self.result.icon_hint))
        preview = Unity.GenericPreview.new(self.result.title, self.result.comment.strip(), icon)
        preview.props.subtitle = self.result.metadata['owner'].get_string()
        if self.result.metadata['language'].get_string():
            preview.add_info(Unity.InfoHint.new("language", _("Language"), None, self.result.metadata['language'].get_string()))
        if self.result.metadata['followers'].get_int32():
            preview.add_info(Unity.InfoHint.new("followers", _("Followers"), None, str(self.result.metadata['followers'].get_int32())))
        icon = Gio.FileIcon.new (Gio.file_new_for_path(PROVIDER_ICON))
        view_action = Unity.PreviewAction.new("open", _("View"), icon)
        preview.add_action(view_action)
        return preview

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_create_previewer(self, result, metadata):
        rp = Preview()
        rp.set_scope_result(result)
        rp.set_search_metadata(metadata)
        return rp

def load_scope():
    return Scope()
