from datetime import datetime, time
from pytz import utc

from django.conf import settings
from django.contrib.sites.models import Site
from django.contrib.syndication.views import Feed
from django.db import connection
from django.db.models import Q
from django.utils.feedgenerator import Rss201rev2Feed
from django.views.decorators.http import condition

from main.models import Arch, Repo, Package
from news.models import News
from releng.models import Release


class BatchWritesWrapper(object):
    def __init__(self, outfile):
        self.outfile = outfile
        self.buf = []

    def write(self, s):
        buf = self.buf
        buf.append(s)
        if len(buf) >= 40:
            self.outfile.write(''.join(buf))
            self.buf = []

    def flush(self):
        self.outfile.write(''.join(self.buf))
        self.outfile.flush()


class FasterRssFeed(Rss201rev2Feed):
    def write(self, outfile, encoding):
        '''
        Batch the underlying 'write' calls on the outfile because Python's
        default saxutils XmlGenerator is a POS that insists on unbuffered
        write/flush calls. This sucks when it is making 1-byte calls to write
        '>' closing tags and over 1600 write calls in our package feed.
        '''
        wrapper = BatchWritesWrapper(outfile)
        super(FasterRssFeed, self).write(wrapper, encoding)
        wrapper.flush()


def package_last_modified(request, *args, **kwargs):
    cursor = connection.cursor()
    cursor.execute("SELECT MAX(last_update) FROM packages")
    return cursor.fetchone()[0]


class PackageFeed(Feed):
    feed_type = FasterRssFeed

    link = '/packages/'

    def __call__(self, request, *args, **kwargs):
        wrapper = condition(last_modified_func=package_last_modified)
        return wrapper(super(PackageFeed, self).__call__)(request, *args, **kwargs)

    __name__ = 'package_feed'

    def get_object(self, request, arch='', repo=''):
        obj = dict()
        qs = Package.objects.normal().order_by('-last_update')

        if arch != '':
            # feed for a single arch, also include 'any' packages everywhere
            a = Arch.objects.get(name=arch)
            qs = qs.filter(Q(arch=a) | Q(arch__agnostic=True))
            obj['arch'] = a
        if repo != '':
            # feed for a single arch AND repo
            r = Repo.objects.get(name__iexact=repo)
            qs = qs.filter(repo=r)
            obj['repo'] = r
        else:
            qs = qs.filter(repo__staging=False)
        obj['qs'] = qs[:50]
        return obj

    def title(self, obj):
        s = settings.BRANDING_DISTRONAME+': Recent package updates'
        if 'repo' in obj and 'arch' in obj:
            s += ' (%s [%s])' % (obj['arch'].name, obj['repo'].name.lower())
        elif 'repo' in obj:
            s += ' [%s]' % (obj['repo'].name.lower())
        elif 'arch' in obj:
            s += ' (%s)' % (obj['arch'].name)
        return s

    def description(self, obj):
        s = 'Recently updated packages in the '+settings.BRANDING_DISTRONAME+' package repositories'
        if 'arch' in obj:
            s += ' for the \'%s\' architecture' % obj['arch'].name.lower()
            if not obj['arch'].agnostic:
                s += ' (including \'any\' packages)'
        if 'repo' in obj:
            s += ' in the [%s] repository' % obj['repo'].name.lower()
        s += '.'
        return s

    subtitle = description

    def items(self, obj):
        return obj['qs']

    item_guid_is_permalink = False

    def item_guid(self, item):
        # http://diveintomark.org/archives/2004/05/28/howto-atom-id
        date = item.last_update
        return 'tag:%s,%s:%s%s' % (Site.objects.get_current().domain,
                date.strftime('%Y-%m-%d'), item.get_absolute_url(),
                date.strftime('%Y%m%d%H%M'))

    def item_pubdate(self, item):
        return item.last_update

    def item_title(self, item):
        return '%s %s %s' % (item.pkgname, item.full_version, item.arch.name)

    def item_description(self, item):
        return item.pkgdesc

    def item_categories(self, item):
        return (item.repo.name, item.arch.name)


def news_last_modified(request, *args, **kwargs):
    cursor = connection.cursor()
    cursor.execute("SELECT MAX(last_modified) FROM news")
    return cursor.fetchone()[0]


class NewsFeed(Feed):
    feed_type = FasterRssFeed

    title = settings.BRANDING_DISTRONAME+': Recent news updates'
    link = '/news/'
    description = 'The latest and greatest news from the '+settings.BRANDING_DISTRONAME+' distribution.'
    subtitle = description

    def __call__(self, request, *args, **kwargs):
        wrapper = condition(last_modified_func=news_last_modified)
        return wrapper(super(NewsFeed, self).__call__)(request, *args, **kwargs)

    __name__ = 'news_feed'

    def items(self):
        return News.objects.select_related('author').order_by(
                '-postdate', '-id')[:10]

    item_guid_is_permalink = False

    def item_guid(self, item):
        return item.guid

    def item_pubdate(self, item):
        return item.postdate

    def item_updateddate(self, item):
        return item.last_modified

    def item_author_name(self, item):
        return item.author.get_full_name()

    def item_title(self, item):
        return item.title

    def item_description(self, item):
        return item.html()


class ReleaseFeed(Feed):
    feed_type = FasterRssFeed

    title = settings.BRANDING_DISTRONAME+': Releases'
    link = '/download/'
    description = 'Release ISOs'
    subtitle = description

    __name__ = 'release_feed'

    def items(self):
        return Release.objects.filter(available=True)[:10]

    def item_title(self, item):
        return item.version

    def item_description(self, item):
        return item.info_html()

    def item_pubdate(self, item):
        return datetime.combine(item.release_date, time()).replace(tzinfo=utc)

    def item_updateddate(self, item):
        return item.last_modified

    item_guid_is_permalink = False

    def item_guid(self, item):
        # http://diveintomark.org/archives/2004/05/28/howto-atom-id
        date = item.release_date
        return 'tag:%s,%s:%s' % (Site.objects.get_current().domain,
                date.strftime('%Y-%m-%d'), item.get_absolute_url())

    def item_enclosure_url(self, item):
        domain = Site.objects.get_current().domain
        proto = 'https'
        return "%s://%s/%s.torrent" % (proto, domain, item.iso_url())

    def item_enclosure_length(self, item):
        if item.torrent_data:
            torrent = item.torrent()
            return torrent['file_length'] or ""
        return ""

    item_enclosure_mime_type = 'application/x-bittorrent'

# vim: set ts=4 sw=4 et: