#!/usr/bin/python
# -*- coding: utf-8 -*-
### BEGIN LICENSE
# Copyright (C) 2009 Jono Bacon <jono@ubuntu.com>
# Copyright (C) 2010 Michael Budde <mbudde@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/>.
### END LICENSE

import gtk
import gobject
import webkit
import logging
import re
import webbrowser

from lernid.widgets.Widget import Widget
from lernid.LernidOptions import Options
from urlparse import urlparse
from lernid.lernidconfig import get_data_path, VERSION
from lernid.LernidPreferences import Preferences

class Browser(Widget):

    __gtype_name__ = 'LernidBrowser'

    __gsignals__ = {
        'page-loading': (
            gobject.SIGNAL_RUN_LAST, None, (str,)
        ),
        'page-loaded': (
            gobject.SIGNAL_RUN_LAST, None, (str,)
        ),
        'page-changed': (
            gobject.SIGNAL_RUN_LAST, None, (str,)
        ),
    }

    def __init__(self):
        Widget.__init__(self, 'browser')

        builder = self.builder_with_file('BrowserWidget.ui')
        self.add(builder.get_object('browser_vbox'))
        self._browser = webkit.WebView()

        def sse2():
            try:
                info_file = open("/proc/cpuinfo")
            except:
                return False
            flag_lines = [l for l in info_file if l.startswith("flags")]
            if len(flag_lines) == 0: return False
            return "sse2" in flag_lines[0]
        
        browser_settings=webkit.WebSettings()
        useragent=browser_settings.get_property('user-agent')
        parts=useragent.split(' ')
        parts[-1]='lernid/'+VERSION
        browser_settings.set_property('user-agent', ' '.join(parts))
        if not sse2():
            browser_settings.set_property('enable-plugins', False)
        else:
            browser_settings.set_property('enable-plugins', True)
        self._browser.set_settings(browser_settings)
        
        scroll = builder.get_object('browser_scroll')
        scroll.add(self._browser)

        self._toolbar = builder.get_object('toolbar_hbox')
        self._refresh_button = builder.get_object('browser_refresh')
        self._stop_button = builder.get_object('browser_stop')

        # set up browser combo box
        self._url_combo = builder.get_object('browser_combo')
        self._url_store = gtk.ListStore(str, str, bool) # Title string, URL, delete
        self._url_combo.set_model(self._url_store)
        title_cell = gtk.CellRendererText()
        self._url_combo.pack_start(title_cell, True)
        self._url_combo.add_attribute(title_cell, 'text', 0)

        self._back_button = builder.get_object('browser_back')

        # Connect the signals
        builder.connect_signals(self)
        self._browser.connect("show", self._set_toolbar_state, True)
        self._browser.connect("hide", self._set_toolbar_state, False)
        self._browser.connect("load-started", self._set_button_state, True)
        self._browser.connect("load-finished", self._set_button_state, False)
        self._browser.connect("load-finished", self._update_url_combo)
        self._browser.set_property('can_focus', True)

        self._paused = False
        self._pause_button = builder.get_object('browser_pause')
        def toggle_paused(button):
            self._paused = not self._paused
        self._pause_button.connect('toggled', toggle_paused)

        self._toolbar.set_sensitive(False)

        self._browser_settings = self._browser.get_settings()
        self._browser_settings.set_property('user-stylesheet-uri', 'file://'+get_data_path()+'/stylesheet')

    def do_event_connect(self, eventman, event):
        classroom = eventman.get_widget_by_name('classroom')
        self.event_connect_signal(classroom, 'message-received', self._classroom_msg_received)
        self._toolbar.set_sensitive(True)
        self._browser.show()
        # FIXME: why does the button not follow sensitive state of the toolbar?
        self._pause_button.set_sensitive(True)
        #  Ideally the homepage should be opened inside Lernid itself
        self.set_location(event.homepage, False)
        schedule = eventman.get_widget_by_name('schedule')
        self._on_faculty = schedule.on_faculty # retrieve method

    def do_event_disconnect(self, eventman, event):
        self._url_combo.handler_block_by_func(self._change_page)
        self._browser.handler_block_by_func(self._update_url_combo)
        self._browser.open('about:blank')
        self._url_store.clear()
        self._browser.handler_unblock_by_func(self._update_url_combo)
        self._url_combo.handler_unblock_by_func(self._change_page)
        self._history = []
        self._current = None
        self._back_button.set_sensitive(False)
        self._pause_button.set_sensitive(False)
        self._toolbar.set_sensitive(False)
        self.event_disconnect_signals()

    def get_location(self):
        return self._browser.get_property('uri')

    def set_location(self, url, in_default_browser = True):
        if urlparse(url).scheme == '':
                url = 'http://' + url
        logging.debug('opening url: '+url)
        # check if prefernce is set and the link was inside chat
        if Preferences.get('links_in_default_browser') and in_default_browser:
            webbrowser.open_new_tab(url)
            logging.debug('opening url in tab in default browser: '+url)
        else:
            self._browser.open(url)
            self.emit('page-changed', url)

    def _back(self, button):
        self._browser.go_back()

    def _refresh(self, button):
        self._browser.reload()

    def _stop(self, button):
        self._browser.stop_loading()

    def _set_toolbar_state(self, browser, state):
        """Make all buttons on the brower's toolbar insensitive"""
        # We make the toolbar insensitive, and thereby its children as well
        self._toolbar.set_sensitive(state)

    def _set_button_state(self, browser, frame, loading):
        """Change the stop and reload button states"""
        self._refresh_button.set_sensitive(not loading)
        self._stop_button.set_sensitive(loading)
        if loading:
            self.emit('page-loading', browser.get_property('uri'))
        if not self._browser.can_go_back():
            self._back_button.set_sensitive(False)

    def _update_url_combo(self, browser, *args):
        """Update the browser url combo box with the new title."""
        url = browser.get_property('uri')
        if url == None: 
            url = ''
        logging.debug('update url combo: {0}'.format(url))
        if url == 'about:blank':
            return
        if self._browser.can_go_back():
            self._back_button.set_sensitive(True)
        match = False
        storeiter = self._url_store.get_iter_first()
        while storeiter:
            if self._url_store.get_value(storeiter, 1) == url:
                match = True
                self._url_store.move_before(storeiter, self._url_store.get_iter_first())
                self._url_combo.set_active_iter(storeiter)
                break
            storeiter = self._url_store.iter_next(storeiter)
        if not match:
            title = browser.get_property('title')
            if title is None: title = ''
            urllen = 150 - len(title)
            shorturl = url
            if len(url) > urllen:
                shorturl = url[:urllen] + '...'
            item = "{0} ({1})".format(title, shorturl)
            self._url_store.prepend([item, url, False])
            self._url_combo.set_active(0)
        self.emit('page-loaded', url)

    def _change_page(self, combo):
        logging.debug('change page: {0}'.format(combo.get_active()))
        if len(self._url_store) == 0:
            return
        url = self._url_store[self._url_combo.get_active()][1]
        delete = self._url_store[self._url_combo.get_active()][2]
        if delete:
            del self._url_store[self._url_combo.get_active()]
        if url != self._browser.get_property('uri'):
            logging.debug('{0} != {1}'.format(url, self._browser.get_property('uri')))
            self.set_location(url)

    def _classroom_msg_received(self, classroom, chan, sender, text):
        if not Options.get('unsafe-override') and not self._on_faculty(sender):
            return
        load, ignore = self._parse_urls(text)
        if load and not self._paused:
            self.set_location(load)
        elif load:
            ignore.insert(0, load)
        for url in ignore:
            self._url_store.prepend([url, url, True])

    def _parse_urls(self, message):
        """Parse a message for URLs and return a tuple with the first
        item begin a URL to load or None and the second item being a
        (possibly empty) list of URLs to add to the combo box.
        """
        load = None
        ignore = []
        def cleanurl(url):
            if url.startswith('['):
                url = url[1:]
            if any([url.endswith(c) for c in '.;:]']):
                url = url[:-1]
            return url
        if re.search("slidefile", message, re.IGNORECASE): return (load, ignore)
        urls = re.findall("(\[?https?://[^\s\)]*)[\s\)\.\]]?", message, re.IGNORECASE)
        for url in urls:
            if (url.startswith('[') and url.endswith(']')) or load:
                ignore.append(cleanurl(url))
            else:
                load = cleanurl(url)
        return (load, ignore)
