# Copyright (c) 2004 Jean-Yves Lefort
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. Neither the name of Jean-Yves Lefort nor the names of its contributors
#    may be used to endorse or promote products derived from this software
#    without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# RealAudio is a registered trademark of RealNetworks, Inc.

import string, re, datetime, ST, gtk
from ST import _

### constants #################################################################

class FIELD:
    DATE, DESCRIPTION, URL = range(3)

BASIC_CH_HOME		= "http://www.basic.ch/"
BASIC_CH_ROOT		= "http://basic.ch/"

re_dj			= re.compile(r"javascript:openWindow\('(showtl.cfm\?showid=[0-9]+)'.*<b>(<font.*\">)?(.*?)(</font>)?</b>")
re_genre		= re.compile('(sans-serif"><b>|<font color="000000">)(.*?)<')
re_date			= re.compile(r">([0-9][0-9])\.([0-9][0-9])\.([0-9][0-9])<")
re_stream		= re.compile(r'a href="/(ram/.*?)".*?text-decoration:none">(.*)</a>')

### helpers ###################################################################

class struct:
    def __init__ (self, **entries):
        self.__dict__.update(entries)

def parse_error (err):
    handler.notice(_("parse error: %s") % (err))

# Convert a 2-digits year to a 4-digits year, conforming to the POSIX
# or X/Open standard -- nice y2k69 bug ;)
def yy2yyyy (yy):
    if yy >= 69 and yy <= 99:
        return 1900 + yy
    else:
        return 2000 + yy

### transfer callbacks ########################################################

def categories_line_cb (line, categories, info):
    match = re_dj.search(line)
    if match is not None:
        if info.category is not None:
            parse_error("incomplete category")

        info.category = ST.Category()
        info.category.name = match.group(1)
        info.category.url_postfix = match.group(1)
        info.category.label = string.capwords(ST.sgml_ref_expand(match.group(3)))
        categories.append(info.category)
    else:
        match = re_genre.search(line)
        if match is not None:
            if info.category is None:
                parse_error("misplaced genre")
            else:
                info.category.label = info.category.label + " (" + ST.sgml_ref_expand(match.group(2)) + ")"
                info.category = None

def streams_line_cb (line, streams, info):
    match = re_date.search(line)
    if match is not None:
        if info.stream is not None:
            parse_error("incomplete stream")

        date = datetime.date(yy2yyyy(int(match.group(3))),
                             int(match.group(2)),
                             int(match.group(1)))

        info.stream = ST.Stream()
        info.stream.fields[FIELD.DATE] = date.strftime("%x")
    else:
        match = re_stream.search(line)
        if match is not None:
            if info.stream is None:
                parse_error("misplaced stream")
            else:
                info.stream.name = match.group(1)
                info.stream.fields[FIELD.URL] = BASIC_CH_ROOT + info.stream.name
                info.stream.fields[FIELD.DESCRIPTION] = ST.sgml_ref_expand(match.group(2))
                streams.append(info.stream)
                info.stream = None

### handler implementation ####################################################

class BasicChHandler (ST.Handler):
    def __new__ (cls):
        self = ST.Handler.__new__(cls, "basic.ch.py")

        self.label = "basic.ch"
        self.description = _("basic.ch Internet Radio - Live and Archived DJ Mixes")
        self.home = BASIC_CH_HOME
        self.icon = gtk.gdk.pixbuf_new_from_file(ST.find_icon("basic.ch.png"))

        category = ST.Category()
        category.name = "__main"
        category.label = _("Live")
        self.stock_categories = category,

        self.add_field(ST.HandlerField(FIELD.DATE,
                                       _("Date"),
                                       str,
                                       ST.HANDLER_FIELD_VISIBLE,
                                       _("The mix recording date")))
        self.add_field(ST.HandlerField(FIELD.DESCRIPTION,
                                       _("Description"),
                                       str,
                                       ST.HANDLER_FIELD_VISIBLE,
                                       _("The mix description")))
        self.add_field(ST.HandlerField(FIELD.URL,
                                       _("URL"),
                                       str,
                                       ST.HANDLER_FIELD_VISIBLE
                                       | ST.HANDLER_FIELD_START_HIDDEN,
                                       _("The mix listen URL")))

        return self

    def reload (self, category):
        session = ST.TransferSession()
        
        if not hasattr(self, "categories"):
            categories = []
            session.get_by_line(BASIC_CH_ROOT + "downtest.cfm",
                                flags = ST.TRANSFER_UTF8 | ST.TRANSFER_PARSE_HTTP_CHARSET | ST.TRANSFER_PARSE_HTML_CHARSET,
                                body_cb = categories_line_cb,
                                body_args = (categories, struct(category = None)))
            self.categories = categories

        streams = []
        if category.url_postfix is None:
            # main category
            stream = ST.Stream()
            stream.name = "basic.ram"
            stream.fields[FIELD.DATE] = _("Now")
            stream.fields[FIELD.DESCRIPTION] = _("Live stream")
            stream.fields[FIELD.URL] = BASIC_CH_ROOT + stream.name
            streams.append(stream)
        else:
            session.get_by_line(BASIC_CH_ROOT + category.url_postfix,
                                flags = ST.TRANSFER_UTF8 | ST.TRANSFER_PARSE_HTTP_CHARSET | ST.TRANSFER_PARSE_HTML_CHARSET,
                                body_cb = streams_line_cb,
                                body_args = (streams, struct(stream = None)))

        return (self.categories, streams)

    def stream_get_stock_field (self, stream, stock_field):
        if stock_field == ST.HANDLER_STOCK_FIELD_NAME:
            return stream.fields[FIELD.DATE]
        elif stock_field == ST.HANDLER_STOCK_FIELD_DESCRIPTION:
            return stream.fields[FIELD.DESCRIPTION]
        elif stock_field == ST.HANDLER_STOCK_FIELD_URI_LIST:
            return stream.fields[FIELD.URL],

    def stream_tune_in (self, stream):
        ST.action_run("play-ra", stream.fields[FIELD.URL])

    def stream_record (self, stream):
        ST.action_run("record-ra", stream.fields[FIELD.URL])
    
### initialization ############################################################

def init ():
    global handler

    if not ST.check_api_version(2, 0):
        raise RuntimeError, _("API version mismatch")
    
    ST.action_register("play-ra", _("Listen to a RealAudio%s stream") % ("\302\256"), "realplay %q")
    ST.action_register("record-ra", _("Record a RealAudio%s stream") % ("\302\256"))

    handler = BasicChHandler()
    ST.handlers_add(handler)

init()
