#
# This file is part of Sargasso, http://zamez.org/sargasso
# Licensed under the GNU General Public License,
#                http://www.opensource.org/licenses/gpl-license
# Copyright 2005 James Bursa <james@semichrome.net>
#

import os
import urlparse
import wimp
import sargasso
import swi
import rufl


# styles for text: font family, style, size, text colour, background colour
style_feed_title = ('Homerton', rufl.regular, 200, 0x000000, 0xffaa99)
style_feed_title_new = ('Homerton', rufl.bold, 200, 0x000000, 0xffaa99)
style_feed_tagline = ('NewHall', rufl.regular, 200, 0x000000, None)
style_feed_error = ('NewHall', rufl.regular, 200, 0x0000a0, None)
style_feed_date = ('NewHall', rufl.slanted, 160, 0x0060ee, None)
style_feed_type = ('NewHall', rufl.slanted, 160, 0x666666, None)
style_entry_title = ('Homerton', rufl.regular, 200, 0x000000, 0xa0dddd)
style_entry_title_new = ('Homerton', rufl.bold, 200, 0x000000, 0xa0dddd)
style_entry_author = ('NewHall', rufl.slanted, 160, 0x666666, None)
style_entry_date = ('NewHall', rufl.slanted, 160, 0x0060ee, None)
style_entry_summary = ('NewHall', rufl.regular, 180, 0x000000, None)


feed_type = {
    'rss090': 'RSS 0.90',
    'rss091n': 'Netscape RSS 0.91',
    'rss091u': 'Userland RSS 0.91',
    'rss10': 'RSS 1.0',
    'rss092': 'RSS 0.92',
    'rss093': 'RSS 0.93',
    'rss094': 'RSS 0.94',
    'rss20': 'RSS 2.0',
    'rss': 'RSS',
    'atom01': 'Atom 0.1',
    'atom02': 'Atom 0.2',
    'atom03': 'Atom 0.3',
    'atom': 'Atom',
    'cdf': 'CDF',
    'hotrss': 'Hot RSS',
    '': 'Unknown',
}


default_feeds = (
    'http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/front_page/rss.xml',
    'http://today.reuters.co.uk/rss/topNews/',
    'http://news.ft.com/rss/home/uk',
    'http://www.drobe.co.uk/rss.php',
    'http://www.iconbar.com/cgi-bin/iconbar.rss',
    'http://www.theregister.co.uk/feeds/latest.rdf',
    'http://www.snackspot.org.uk/rss/rss.xml',
)


def init():
    "Initialise program specific stuff"
    global main_window, add_feed_box
    wimp.hourglass_on()
    iconbar_init()
    main_window = MainWindow('main')
    add_feed_box = AddFeedBox('add_feed')
    wimp.poll_mask = 0
    wimp.null_reason_handler = null_reason_handler
    load_choices()
    if sargasso.feeds == []:
        for url in default_feeds:
            sargasso.add_feed(url)
    for feed in sargasso.feeds:
        feed.last_status = feed.status
        feed.window = wimp.window('main')
    make_main_window()
    main_window.open()
    wimp.hourglass_off()


def quit():
    "Tidy up before quitting"
    pass


def iconbar_init():
    "Setup iconbar icon"
    global window_info, menu_iconbar
    wimp.iconbar_icon('!sargasso', iconbar_click)
    window_info = wimp.info_window()
    menu_iconbar = wimp.menu('iconbar',
            ('task', ('info', window_info), 'update', 'quit'))


def null_reason_handler():
    sargasso.poll()
    remake = False
    for feed in sargasso.feeds:
        if feed.last_status != feed.status:
            remake = True
            feed.last_status = feed.status
            if feed.window.is_open:
                make_feed_window(feed)
                feed.window.refresh(0, 0, 900, 10000)
    if remake:
        make_main_window()
        main_window.refresh(0, 0, 1000, 10000)


def iconbar_click(buttons, x):
    "Handle click on iconbar"
    if buttons == 2:
        reopen = 1
        while reopen:
            selection, reopen = menu_iconbar.menu(x - 64, 96 + 44 * 3)
            if selection is None:
                pass
            elif selection[0] == 0:
                window_info.open_as_menu()
                reopen = 0
            elif selection[0] == 1:
                for feed in sargasso.feeds:
                    feed.update()
            elif selection[0] == 2:
                wimp.quit()
    elif buttons in (1, 4):
        main_window.open_full_size(-1)


def make_main_window():
    main_window.contents = []
    y = 0
    for feed in sargasso.feeds:
        title = try_detail(feed.feed, 'title_detail')
        if title == '':
            title = feed.url
        if len(feed.new_entries):
            title_style = style_feed_title_new
        else:
            title_style = style_feed_title
        title = MainWindowIcon(main_window, 0, y, 700,
                title_style[0], title_style[1],
                title_style[2], title,
                title_style[3], title_style[4])
        title.feed = feed
        main_window.add_content(title)

        status = ''
        if feed.status == sargasso.STATUS_DONE:
            if len(feed.new_entries):
                status = '%i items (%i new)' % (len(feed.entries),
                        len(feed.new_entries))
            else:
                status = '%i items' % (len(feed.entries))
        elif feed.status == sargasso.STATUS_ERROR:
            status = 'Failed'
        elif feed.status in (sargasso.STATUS_IDLE, sargasso.STATUS_FETCHING):
            status = 'Fetching'
        status = MainWindowIcon(main_window, 700, y, 1000,
                title_style[0], title_style[1],
                title_style[2], status,
                title_style[3], title_style[4])
        status.feed = feed
        main_window.add_content(status)

        status.pos[3] = title.pos[3]
        y = title.y1

        if feed.status == sargasso.STATUS_ERROR:
            summary = MainWindowIcon(main_window, 0, y, 1000,
                    style_feed_error[0], style_feed_error[1],
                    style_feed_error[2], feed.error,
                    style_feed_error[3], style_feed_error[4])
        else:
            summary = MainWindowIcon(main_window, 0, y, 1000,
                    style_feed_tagline[0], style_feed_tagline[1],
                    style_feed_tagline[2],
                    try_detail(feed.feed, 'tagline_detail'),
                    style_feed_tagline[3], style_feed_tagline[4])
        summary.feed = feed
        main_window.add_content(summary)

        y = summary.y1 + 10
    main_window.set_extent(0, 0, 1000, y)
    if main_window.is_open:
        main_window.open_full_size()


class MainWindow(wimp.window):

    def click(self, icon, buttons, x, y, sx, sy):
        self.sx = sx
        self.sy = sy
        if wimp.window.click(self, icon, buttons, x, y, sx, sy):
            return True
        if buttons == 2:
            menu = wimp.menu('menu',
                    ('task', 'add_feed', '-remove_feed', 'update'))
            reopen = 1
            while reopen:
                selection, reopen = menu.menu(sx - 64, sy)
                if selection is None:
                    pass
                elif selection[0] == 0:
                    add_feed_box.icon_text(0, 'http://')
                    add_feed_box.open_centred()
                    add_feed_box.put_caret(0)
                elif selection[0] == 2:
                    for feed in sargasso.feeds:
                        feed.update()
            return True


class MainWindowIcon(wimp.paragraph):

    def click(self, buttons, x, y):
        feed = self.feed
        if buttons == 2:
            title = try_detail(feed.feed, 'title_detail')
            if title == '':
                title = feed.url
            title = title.encode('latin1', 'replace')
            if 30 < len(title):
                title = title[:30] + '...'
            else:
                title = title
            menu = wimp.menu('menu',
                    ('task', 'add_feed',
                    wimp.lookup('remove_feed_x') % title,
                    'update'))
            reopen = 1
            while reopen:
                selection, reopen = menu.menu(self.window.sx - 64,
                        self.window.sy)
                if selection is None:
                    pass
                elif selection[0] == 0:
                    add_feed_box.icon_text(0, 'http://')
                    add_feed_box.open_centred()
                    add_feed_box.put_caret(0)
                elif selection[0] == 1:
                    if feed.window:
                        feed.window.remove()
                    sargasso.remove_feed(feed.url)
                    make_main_window()
                    main_window.refresh(0, 0, 1000, 10000)
                    save_choices()
                elif selection[0] == 2:
                    for feed in sargasso.feeds:
                        feed.update()
            return True
        if feed.status == sargasso.STATUS_DONE:
            if feed.window.is_open:
                feed.window.open_centred(-1)
            else:
                make_feed_window(feed)
                feed.window.open_centred(-1)
                if feed.new_entries:
                    feed.new_entries = []
                    make_main_window()
                    main_window.refresh(0, 0, 1000, 10000)
        return True


def make_feed_window(feed):
    w = feed.window
    w.contents = []
    w.y1 = 0
    link = feed.feed.get('link', '')
    title = try_detail(feed.feed, 'title_detail')
    tagline = try_detail(feed.feed, 'tagline_detail')
    modified = feed.feed.get('modified', '')
    type = feed_type[feed.version]
    add_para(w, style_feed_title, title, link)
    add_para(w, style_feed_tagline, tagline, link)
    add_para(w, style_feed_date, modified, link)
    add_para(w, style_feed_type, type, link)
    for entry in feed.entries:
        link = entry.get('link', '')
        entry_title = try_detail(entry, 'title_detail')
        entry_author = entry.get('author', '')
        entry_modified = entry.get('modified', '')
        entry_summary = try_detail(entry, 'summary_detail')
        if entry in feed.new_entries:
            add_para(w, style_entry_title_new, entry_title, link)
        else:
            add_para(w, style_entry_title, entry_title, link)
        add_para(w, style_entry_author, entry_author, link)
        add_para(w, style_entry_date, entry_modified, link)
        add_para(w, style_entry_summary, entry_summary, link)
        if 'content' in entry:
            for content in entry.content:
                value = read_detail(content)
                add_para(w, style_entry_summary, value, link)
    w.set_extent(0, 0, 900, w.y1)
    w.title(title[:98])


def add_para(w, style, text, url):
    if text == '':
        return
    icon = FeedWindowIcon(w, 0, w.y1, 900, style[0], style[1],
            style[2], text, style[3], style[4])
    w.add_content(icon)
    icon.url = url
    w.y1 = icon.y1


class FeedWindowIcon(wimp.paragraph):

    def click(self, buttons, x, y):
        if buttons == 2:
            return False
        swi.swi('URI_Dispatch', '0s0', self.url)
        return True


def try_detail(feed_or_item, detail_name):
    if detail_name not in feed_or_item:
        return ''
    return read_detail(feed_or_item[detail_name])


def read_detail(detail):
    if detail['type'] == 'text/plain':
        t = detail['value']
    elif detail['type'] == 'text/html':
        t = remove_html(detail['value'])
    else:
        t = ''
    return ' '.join(t.split())


def remove_html(s):
    return ''.join(map(lambda z: z[-1],
                       map(lambda z: z.split('>'),
                           s.split('<'))))


def load_choices():
    try:
        f = open('Choices:Sargasso.Feeds')
    except IOError:
        return
    for line in f:
        sargasso.add_feed(line[:-1])


def save_choices():
    os.mkdir('<Choices$Write>.Sargasso')
    try:
        f = open('<Choices$Write>.Sargasso.Feeds', 'w')
    except IOError:
        return
    for feed in sargasso.feeds:
        f.write(feed.url + '\n')
    f.close()


class AddFeedBox(wimp.window):

    def click(self, icon, buttons, x, y, sx, sy):
        if icon == 2:
            self.close()
            return True
        elif icon == 1:
            url = self.icon_text(0)
            scheme, host, path, params, query, fragment = urlparse.urlparse(url)
            if scheme != 'http':
                wimp.warning(wimp.lookup('http'))
                return True
            sargasso.add_feed(url)
            sargasso.feeds[-1].last_status = sargasso.feeds[-1].status
            sargasso.feeds[-1].window = wimp.window('main')
            save_choices()
            self.close()
            return True
        return False

    def key(self, icon, key):
        if key == 0x1b:
            self.close()
        elif key == 0xd:
            self.click(1, 0, 0, 0, 0, 0)
        else:
            wimp.window.key(self, icon, key)
