#!/usr/bin/python
# -*- coding: utf-8 -*-
### BEGIN LICENSE
# Copyright (C) 2009 Rick Spencer rick.spencer@canonical.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/>.
### END LICENSE

import sys
import os
import gtk
from quickly import prompts
import quickly
# optional Launchpad integration
# this shouldn't crash if not found as it is simply used for bug reporting
try:
    import LaunchpadIntegration
    launchpad_available = True
except:
    launchpad_available = False

import urllib
import simplejson as json
import pynotify


from desktopcouch.records.server import CouchDatabase
from desktopcouch.records.record import Record as CouchRecord

# Check if we are working in the source tree or from the installed 
# package and mangle the python path accordingly
if os.path.dirname(sys.argv[0]) != ".":
    if sys.argv[0][0] == "/":
        fullPath = os.path.dirname(sys.argv[0])
    else:
        fullPath = os.getcwd() + "/" + os.path.dirname(sys.argv[0])
else:
    fullPath = os.getcwd()
sys.path.insert(0, os.path.dirname(fullPath))

from bughugger import AboutBughuggerDialog, PreferencesBughuggerDialog
from bughugger import JsonsearchesDialog
from bughugger.LaunchpadLogonDialog import LaunchpadLogonDialog
from bughugger.FindTeamDialog import FindTeamDialog
from bughugger.FindPackageDialog import FindPackageDialog
from bughugger.FindUserDialog import FindUserDialog
from bughugger.bughuggerconfig import getdatapath
from quickly.widgets.asynch_task_progressbox import AsynchTaskProgressBox
from bughugger import LaunchpadUtils
from bughugger.TriageBugsPage import TriageBugsPage, PageTab

class BughuggerWindow(gtk.Window):
    __gtype_name__ = "BughuggerWindow"
    default_keys = ["id","title","status","importance","release","target","milestone","assignee","team name","tags","patch?","gravity","affected users"]

    def __init__(self):
        """__init__ - This function is typically not called directly.
        Creation a BughuggerWindow requires redeading the associated ui
        file and parsing the ui definition extrenally,
        and then calling BughuggerWindow.finish_initializing().

        Use the convenience function NewBughuggerWindow to create
        BughuggerWindow object.

        """
        pass

    def finish_initializing(self, builder):
        """finish_initalizing should be called after parsing the ui definition
        and creating a BughuggerWindow object with it in order to finish
        initializing the start of the new BughuggerWindow instance.

        """

        #get a reference to the builder and set up the signals
        self.builder = builder
        self.builder.connect_signals(self)

        global launchpad_available
        if launchpad_available:
            # see https://wiki.ubuntu.com/UbuntuDevelopment/Internationalisation/Coding for more information
            # about LaunchpadIntegration
            helpmenu = self.builder.get_object('menu3')
            if helpmenu:
                LaunchpadIntegration.set_sourcepackagename('bughugger')
                LaunchpadIntegration.add_items(helpmenu, 0, False, True)
            else:
                launchpad_available = False

        #add menu items for saved remote searches
        rt = "http://wiki.ubuntu.com/Quickly/BughuggerUrlSearch"
        db = CouchDatabase("bughugger", create=True)
        url_searches = db.get_records(record_type=rt,create_view=True)
        search_menu = self.builder.get_object("menuitem_search_menu")

            
        #uncomment the following code to read in preferences at start up
        dlg = PreferencesBughuggerDialog.NewPreferencesBughuggerDialog()
        self.preferences = dlg.get_preferences()
        self.__set_server_pref_ui()
        self.app_name = "bughugger"
        
        self.find_package_dialog = None
        self.find_team_dialog = None
        self.find_users_dialog = None
        if self.preferences["auto_connect"]:
            self.__logon()

        pynotify.init('Bughugger')


    def __set_server_pref_ui(self):
        srvr_lbl = self.builder.get_object("label_server")
        if self.preferences["use_production"]:
            srvr_lbl.set_text("Using Production Server")
        else:
            srvr_lbl.set_text("Using Staging Server")

    def connect_to_launchpad(self, widget, data=None):
        self.__logon()
        
    def __logon(self):
        """logon: shows the logon dialog to log the user onto launchpad.
        Uses cached credentials if available.

        """
        use_production = self.preferences["use_production"]
        self.logon_dialog = LaunchpadLogonDialog(self.app_name, use_production)
        self.logon_dialog.connect("response",self.__logon_complete)
        self.logon_dialog.show()

    def __logon_complete(self, widget, response):
        """ logon_complete: called by the logon dialog when it completes"""
        gtk.gdk.threads_enter()
        self.logon_dialog.hide()
        print "logon complete"
        disc_butt = self.builder.get_object("button_connect")
        con_img = self.builder.get_object("image_connected")

        if response != gtk.RESPONSE_ACCEPT:
            print "logon failed"
            disc_butt.show()
            con_img.hide()
        else:
            disc_butt.hide()
            con_img.show()
        gtk.gdk.threads_leave()

    def about(self, widget, data=None):
        """about - display the about box for what_the_heck """
        about = AboutBughuggerDialog.NewAboutBughuggerDialog()
        response = about.run()
        about.destroy()

    def json_searches(self, widget, data=None):
        json_dialog = JsonsearchesDialog.NewJsonsearchesDialog()
        result = json_dialog.run()
        if result == gtk.RESPONSE_OK:
            url = json_dialog.url
            name = json_dialog.search_name
            self.bug_tasks_from_url(url, name)
        json_dialog.destroy()

    def bug_tasks_from_url(self, url, name):
        params = {"url":url, "name":name}
        get_bug_tasks_progressbox = AsynchTaskProgressBox(self.__get_bug_tasks_from_url, params)
        self.builder.get_object("progress_box").pack_end(get_bug_tasks_progressbox, False, False)
        get_bug_tasks_progressbox.connect("complete",self.__add_custom_triage_page)
        get_bug_tasks_progressbox.show()
        get_bug_tasks_progressbox.start("Retrieving bug tasks from JSON file at %s" % url)

    def __get_bug_tasks_from_url(self, params):
        #for now, we'll assume that all Urls are json files
        f = urllib.urlopen(params["url"])
        bug_json = f.read()
        data = json.loads(bug_json)
        keys = data["keys"]
        found_bug_tasks = data["bug_tasks"]
        bug_task_list = []
        for b in found_bug_tasks:
            tags = ""
            releases = ""
            try:
                tags = " ".join(b["tags"])
                b["tags"] = tags
            except:
                pass
            try:
                releases = " ".join(b["releases"])
                b["releases"] = releases
            except:
                pass
            bug_task_list.append(b)
        return (params["name"],bug_task_list, keys)
        
    def tag_bug_task(self, widget, data=None):
        response, tag = quickly.prompts.string("Bughugger","Find bugs with tag:")
        if response == gtk.RESPONSE_OK:
            params = {"lp":self.logon_dialog.launchpad, "tag":tag}
            get_bug_tasks_progressbox = AsynchTaskProgressBox(LaunchpadUtils.get_tasks_with_tag , params)
            self.builder.get_object("progress_box").pack_end(get_bug_tasks_progressbox, False, False)
            get_bug_tasks_progressbox.connect("complete",self.__add_default_triage_page)
            get_bug_tasks_progressbox.show()
            get_bug_tasks_progressbox.start("Retrieving bugs with tag " + tag + " from launchpad")

    def team_assigned_bug_tasks(self, widget, data=None):
        """bug_tasks_for_team: displays all Ubuntu bug_tasks assigned to a designated team"""
        if self.find_team_dialog == None:
            self.find_team_dialog = FindTeamDialog(self.logon_dialog.launchpad)
            self.find_team_dialog.connect("response", self.__get_team_complete)

        self.find_team_dialog.show()

    def __get_team_complete(self, widget, response):
        self.find_team_dialog.hide()
        if response == gtk.RESPONSE_OK:
            params = {"lp":self.logon_dialog.launchpad, "team":self.find_team_dialog.get_selected()}
            get_bug_tasks_progressbox = AsynchTaskProgressBox(LaunchpadUtils.get_team_bug_task_list , params)
            self.builder.get_object("progress_box").pack_end(get_bug_tasks_progressbox, False, False)
            get_bug_tasks_progressbox.connect("complete",self.__add_default_triage_page)
            get_bug_tasks_progressbox.show()
            get_bug_tasks_progressbox.start("Retrieving team assigned bug_tasks from Launchpad")
        else:
            raise Exception("couldn't get team info")

    def team_package_bug_tasks(self, widget, data=None):
        """bug_tasks_for_team: displays all open bug_tasks about Ubuntu packages to which a designated team is subscribed"""
        if self.find_team_dialog == None:
            self.find_team_dialog = FindTeamDialog(self.logon_dialog.launchpad)
            self.find_team_dialog.connect("response", self.__get_team_packages)

        self.find_team_dialog.show()

    def __get_team_packages(self, widget, response):
        self.find_team_dialog.hide()
        if response == gtk.RESPONSE_OK:
            params = {"lp":self.logon_dialog.launchpad, "team":self.find_team_dialog.get_selected()}
            get_bug_tasks_progressbox = AsynchTaskProgressBox(LaunchpadUtils.get_team_subscribed_list_store, params)
            # bah just stick it all in get_team_subscribed_packages
            self.builder.get_object("progress_box").pack_end(get_bug_tasks_progressbox, False, False)
            get_bug_tasks_progressbox.connect("complete",self.__add_default_triage_page)
            get_bug_tasks_progressbox.show()
            get_bug_tasks_progressbox.start("Retrieving team subscribed package bug_tasks from Launchpad")
        else:
            raise Exception("couldn't get team info")

    def user_assigned_bug_tasks(self, widget, data=None):
        if self.find_users_dialog == None:
            self.find_users_dialog = FindUserDialog(self.logon_dialog.launchpad)
            self.find_users_dialog.connect("response", self.__get_user_assigned_complete)

        self.find_users_dialog.show()

    def __get_user_assigned_complete(self, widget, response):
        self.find_users_dialog.hide()
        if response == gtk.RESPONSE_OK:
            params = {"lp":self.logon_dialog.launchpad, "user":self.find_users_dialog.get_selected()}
            self.get_bug_tasks_progressbox = AsynchTaskProgressBox(LaunchpadUtils.get_user_assigned_list , params)
            self.builder.get_object("progress_box").pack_end(self.get_bug_tasks_progressbox, False, False)
            self.get_bug_tasks_progressbox.connect("complete",self.__add_default_triage_page)
            self.get_bug_tasks_progressbox.show()
            self.get_bug_tasks_progressbox.start("Retrieving bug_tasks from Launchpad")

    def user_reported_bug_tasks(self, widget, data=None):
        if self.find_users_dialog == None:
            self.find_users_dialog = FindUserDialog(self.logon_dialog.launchpad)
            self.find_users_dialog.connect("response", self.__get_user_reported_complete)

        self.find_users_dialog.show()

    def __get_user_reported_complete(self, widget, response):
        self.find_users_dialog.hide()
        if response == gtk.RESPONSE_OK:
            params = {"lp":self.logon_dialog.launchpad, "user":self.find_users_dialog.get_selected()}
            self.get_bug_tasks_progressbox = AsynchTaskProgressBox(LaunchpadUtils.get_user_reported_list_store , params)
            self.builder.get_object("progress_box").pack_end(self.get_bug_tasks_progressbox, False, False)
            self.get_bug_tasks_progressbox.connect("complete",self.__add_default_triage_page)
            self.get_bug_tasks_progressbox.show()
            self.get_bug_tasks_progressbox.start("Retrieving bug_tasks from Launchpad")

    def package_bug_tasks(self, widget, data = None):
        """bug_tasks_for_package: displays all open bug_tasks on a package."""
        if self.find_package_dialog == None:
            self.find_package_dialog = FindPackageDialog(self.logon_dialog.launchpad)
            self.find_package_dialog.connect("response", self.__get_package_complete)

        self.find_package_dialog.show()

    def __get_package_complete(self, widget, response):
        """get_package_complete: called by the package dialog when complete.
        Retrieves open bug_tasks for the package.

        """
        self.find_package_dialog.hide()
        if response == gtk.RESPONSE_OK:
            #asynchronously retrieve bug_tasks from Launchpad
            #callling __add_default_triage_page when done
            params = {"lp":self.logon_dialog.launchpad, "package":self.find_package_dialog.get_selected()}
            get_package_bug_tasks_progressbox = AsynchTaskProgressBox(self.__get_bug_tasks_assigned_package_task, params)
            self.builder.get_object("progress_box").pack_end(get_package_bug_tasks_progressbox, False, False)
            get_package_bug_tasks_progressbox.connect("complete",self.__add_default_triage_page)
            get_package_bug_tasks_progressbox.show()
            get_package_bug_tasks_progressbox.start("Retrieving bug_tasks from Launchpad for " + self.find_package_dialog.get_selected().display_name )
        else:
            raise Exception("couldn't get package info")


    def __add_default_triage_page(self, widget, data):
        """Adds a page of bug_tasks using the standard set of keys for columns"""

        self.builder.get_object("progress_box").remove(widget)
        name, bug_task_list = data
        self.add_page(name, bug_task_list, self.default_keys)

    def __add_custom_triage_page(self, widget, data):
        """Adds a page of bug_tasks without specifying keys for columns"""
        self.builder.get_object("progress_box").remove(widget)
        name, bug_task_list, keys  = data
        self.add_page(name, bug_task_list, keys)

    def add_page(self, name, bug_task_list, keys):
        title_label = gtk.Label(name)
        page = TriageBugsPage(self.logon_dialog.launchpad, bug_task_list, keys)
        pt = PageTab(title_label, page)
        pt.connect("close_requested",self.close_page)
        self.builder.get_object("notebook").append_page(page, pt)
        page.show()
        n = pynotify.Notification("Bughugger Query Complete", name)
        n.show()

    def close_page(self, widget, page):
        notebook = self.builder.get_object("notebook")
        num = notebook.page_num(page)
        notebook.remove_page(num)


    def __get_bug_tasks_assigned_package_task(self, params):
        """get_bug_tasks_assigned_package: used asychronously to retrieve bug_tasks for a package

        arguments:
        params -- a dictionary of arguments passed by the AsynchTaskProgressBox
        params["lp"] is a launchpad instance
        params["package"] is a reference to a package on launchpad
        returns a two tuple of a list of bug_tasks and the package name

        """
  
        pack_name = params["package"].source_package_name
        package = self.logon_dialog.launchpad.distributions['ubuntu'].getSourcePackage( name = pack_name )
  
        bug_task_tasks = package.searchTasks()
        bug_task_dicts = []
        for b in bug_task_tasks:
            if params["kill"] == True:
                return pack_name, bug_task_dicts
            else:
                bug_task_dicts.append(LaunchpadUtils.bug_task_dict(self.logon_dialog.launchpad, b))
        return pack_name, bug_task_dicts
        

    def preferences(self, widget, data=None):
        """preferences - display the preferences window for bughugger """
        prefs = PreferencesBughuggerDialog.NewPreferencesBughuggerDialog()
        cur_use_prod = prefs.get_preferences()["use_production"]
        response = prefs.run()
        if response == gtk.RESPONSE_OK:
            self.preferences = prefs.get_preferences()            
            #make any updates based on changed preferences here
            self.__set_server_pref_ui()
            if self.preferences["auto_connect"]:
                if self.preferences["use_production"] !=  cur_use_prod:
                    self.__logon()
        prefs.destroy()

    def quit(self, widget, data=None):
        """quit - signal handler for closing the BughuggerWindow"""
        self.destroy()

    def on_destroy(self, widget, data=None):
        """on_destroy - called when the BughuggerWindow is close. """
        #clean up code for saving application state should be added here

        gtk.main_quit()

def NewBughuggerWindow():
    """NewBughuggerWindow - returns a fully instantiated
    BughuggerWindow object. Use this function rather than
    creating a BughuggerWindow directly.
    """

    #look for the ui file that describes the ui
    ui_filename = os.path.join(getdatapath(), 'ui', 'BughuggerWindow.ui')
    if not os.path.exists(ui_filename):
        ui_filename = None

    builder = gtk.Builder()
    builder.add_from_file(ui_filename)
    window = builder.get_object("bughugger_window")
    window.finish_initializing(builder)
    return window

if __name__ == "__main__":
    #support for command line options
    import logging, optparse
    parser = optparse.OptionParser(version="%prog %ver")
    parser.add_option("-v", "--verbose", action="store_true", dest="verbose", help="Show debug messages")
    (options, args) = parser.parse_args()

    #set the logging level to show debug messages
    if options.verbose:
        logging.basicConfig(level=logging.DEBUG)
        logging.debug('logging enabled')



    window = NewBughuggerWindow()
    window.show()
    gtk.gdk.threads_init()
    gtk.main()
