"""
Save_Blog_Entry.py - a Jython macro for jEdit that creates a blog entry
for my blog and regenerates the affected pages.  This is very specific
to my machine, blog, and configuration -- so it's not likely to be of
use w/o modifications.

Copyright (C) 2002,2003 Ollie Rutherfurd <oliver@rutherfurd.net>

$Id$
"""

# TODO: generate RSS summary
# TODO: have functions to "correct" latest by scanning archive for most recently updated entries?
# TODO: or would it be better for latest to always be latest? Update entries would be updated on
#       latest if they were already there, but not added if not? Would need to know where latest
#       entries came from.
# QUESTION: would it be better to using rst includes instead of manually aggregating?
# TODO: allow for header text, so 1st second level header doesn't get treated
#       as a sub-title
# TODO: add an about page
# TODO: need to be able to regenerate all
# TODO: use time of day for filename instead of an index number
# TODO: generate a permanent link along with each posting
# QUESTION: is a base href possible for rss? so absolute urls are not 
#           needed for images and whatnot.

from java.lang import Runnable
from javax.swing import SwingUtilities
from org.gjt.sp.jedit.io import VFSManager

import glob
import os
import time

ROOT = 'C:/sandbox/rutherfurd.net/trunk/weblog'

MONTHS= (
    'January',
    'February',
    'March',
    'April',
    'May',
    'June',
    'July',
    'August',
    'September',
    'October',
    'November',
    'December',
)

DAILY_UNDERLINE = '='
MONTH_UNDERLINE = '#'
YEAR_UNDERLINE = '^'
ENTRY_UNDERLINE = '-'
NUM_LATEST = 13 # baker's dozen

def getPathForDate(root=ROOT,date=None):
    """
    Returns path to directory for ``date`` under ``root``.
    If ``date`` isn't specified, it defaults to today.
    """
    if date is None:
        date = time.localtime()
    today = time.strftime('%Y/%m/%d',date)
    return os.path.join(root, today)


def getEntriesForDate(path,pattern='[0-9]*.txt'):
    return [os.path.join(path,f) for f in glob.glob(os.path.join(path, pattern))]

def getLatestEntryName(path):
    """
    does ..\2003\02\07\01.txt -> 2003.02.07.01.txt
    """
    num = os.path.splitext(os.path.split(path)[-1])[0]
    y,m,d = path.split(os.sep)[-4:-1]
    return '.'.join([y,m,d,num,'txt'])

def getNextFilename(root=ROOT,date=None):
    path = getPathForDate(root,date)
    entries = glob.glob(os.path.join(path, '[0-9]*.txt'))

    # split off just filenames, take last entry, split of extension
    # and add 1 to the base filename
    try:
        next = int([os.path.split(e)[-1] for e in entries][-1].split('.')[0]) + 1
    except IndexError:
        next = 1
    next = '%02d' % next

    return os.path.join(path,'%s.txt' % next)


def combineEntries(path):
    buff = []
    entries = getEntriesForDate(path)
    entries.sort()
    entries.reverse()
    for entry in entries:
        filename = os.path.join(path, entry)
        modtime = time.strftime('%I:%M %p', time.localtime(os.path.getmtime(filename)))
        moddate = time.strftime('%A, %B %d, %Y', time.localtime(os.path.getmtime(filename)))
        f = open(filename)
        buff.append(f.read())
        f.close()
        buff.append('*Saved %s at %s*' % (moddate,modtime))
        buff.append('')
    return '\n'.join(buff)


def aggregateDaysEntries(root=ROOT,date=None):
    if date is None:
        date = time.localtime()
    links = 'View `%(month)s <../index.html>`_, `%(year)s <../../index.html>`_, `latest entries <../../../index.html>`_, or `archive <../../../archive.html>`_' % \
        {
            'month': time.strftime('%B',date),
            'year': time.strftime('%Y',date)
        }
    return _aggregateDaysEntries(root,date) + '\n' + links


def _aggregateDaysEntries(root=ROOT,date=None):
    buff = []
    path = getPathForDate(root,date)
    if date is None:
        date = time.localtime()
    entries = os.listdir(path)
    title = time.strftime('Weblog Entries for %A, %B %d, %Y', date)

    buff.append(DAILY_UNDERLINE * len(title))
    buff.append(title)
    buff.append(DAILY_UNDERLINE * len(title))
    buff.append('')
    text = combineEntries(path)
    buff.append(text)
    return '\n'.join(buff)


def aggregateMonthEntries(root=ROOT,date=None):
    if date is None:
        date = time.localtime()
    links = 'View `%(year)s <../index.html>`_, `latest entries <../../index.html>`_, or `archive <../../archive.html>`_' % {'year': time.strftime('%Y',date)}
    return _aggregateMonthsEntries(root,date) + '\n' + links


def _aggregateMonthsEntries(root=ROOT,date=None):
    buff = []
    if date is None:
        date = time.localtime()
    y,m = date[0:2] # year, month
    d = (y,m,0,0,0,0,0,0,0)
    y = str(y)
    m = '%02d' % m
    title = time.strftime('Weblog Entries for %B %Y', d)
    buff.append(MONTH_UNDERLINE * len(title))
    buff.append(title)
    buff.append(MONTH_UNDERLINE * len(title))
    days = [d for d in glob.glob(os.path.join(root,y,m,'[0-9]*')) if os.path.isdir(d)]
    days.reverse()
    for day in days:
        # doing this do figure out the weekday for the day we're looking at
        daydate = time.localtime(time.mktime((y,m, int(os.path.split(day)[-1]),0,0,0,0,0,0)))
        buff.append('')
        day = time.strftime('%A, %B %d, %Y', daydate)
        buff.append(DAILY_UNDERLINE * len(day))
        buff.append(day)
        buff.append(DAILY_UNDERLINE * len(day))
        buff.append('')
        text = combineEntries(getPathForDate(root,daydate))
        buff.append(text)
    return '\n'.join(buff)


def aggregateYearsEntries(root=ROOT,date=None):
    """ XXX document, defaults to current year"""
    buff = []
    if date is None:
        date = time.localtime()
    y = str(date[0])
    buff.append(YEAR_UNDERLINE * 30)
    buff.append('All Entries for %s' % y)
    buff.append(YEAR_UNDERLINE * 30)
    buff.append('')
    months = glob.glob(os.path.join(root, y, '[0-9][0-9]'))
    months.reverse()
    for month in months:
        m = (int(y), int(os.path.split(month)[-1]), 0,0,0,0,0,0)
        monthyear = time.strftime('%B %Y', m)
        buff.append(_aggregateMonthsEntries(root,m))
    buff.append('View `latest entries <../index.html>`_ or `archive <../archive.html>`_')
    return '\n'.join(buff)


def generateArchivePage(root=ROOT):
    buff = []
    title = 'Weblog Archive'
    buff.append('@' * len(title))
    buff.append(title)
    buff.append('@' * len(title))
    buff.append('')

    years = glob.glob(os.path.join(root, '20[0-9][0-9]'))
    years.reverse()
    for year_path in years:
        year = os.path.split(year_path)[-1]
        year_section = '`%(year)s <%(year)s/index.html>`_' % {'year': year}
        buff.append(YEAR_UNDERLINE * len(year_section))
        buff.append(year_section)
        buff.append(YEAR_UNDERLINE * len(year_section))
        buff.append('')
        months = glob.glob(os.path.join(year_path, '[0-9][0-9]'))
        months.reverse()
        for month_path in months:
            month = os.path.split(month_path)[-1]
            buff.append(
                '* `%(monthname)s <%(year)s/%(month)s/index.html>`_' % 
                {'year': year, 
                'month': month, 
                'monthname': MONTHS[int(month)-1]})
        buff.append('')
        buff.append('Go to `latest entries <index.html>`_')
    return '\n'.join(buff)


def compactLatest(root,num):
    """
    .. NOTE:: 01.txt is oldest
    """
    entries = glob.glob(os.path.join(root, '[0-9]*.txt'))
    """
    assume num = 10, len(entries) = 20
    entriy[20] -> entry[10]
    ...
    entry[10] -> entry[0]
    """
    offset = len(entries) - num
    for i in range(offset):
        os.unlink(entries[i])   # remove entry we're renaming to
        os.rename(entries[i+offset], entries[i])
    return entries[:num]


class MyBlogAggregator(Runnable):

    def __init__(self,view,latest=None,root=ROOT,date=None):
        self.view = view
        self.latest = latest
        self.root = root
        if date is None:
            date = time.localtime()
        self.date = date

    def run(self):
        # wait for pending request (waiting for buffer to be saved)
        VFSManager.waitForRequests()

        today = time.localtime()
        #if today[0:3] == self.date[0:3] and self.latest:
        # if there's an entry in the latest which matches
        # this one, update it, otherwise add it if needed
        if self.latest:
            latest_path = os.path.join(self.root,getLatestEntryName(self.latest))

            # slurp latest entry
            f = open(self.latest)
            entry = f.read()
            f.close()

            # latest entry already exists, just update it
            if not os.path.exists(latest_path):
                entries = compactLatest(self.root,NUM_LATEST)

            # ??? add an "updated note" if not new?
            f = open(latest_path,'w')
            f.write(entry)
            f.close()

        # aggregate all the entries for the date we just added an
        # entry for
        path = getPathForDate(self.root, self.date)
        buff = aggregateDaysEntries(self.root, self.date)
        f = open(os.path.join(path, 'index.txt'), 'w')
        #print >> f, buff
        f.write(buff)
        f.close()

        # aggregate all daily aggregates for the month we just
        # added or edited an entry for
        year,month = self.date[0:2]
        year = str(year)
        month = '%02d' % month
        buff = aggregateMonthEntries(self.root, self.date)
        f = open(os.path.join(self.root, year, month, 'index.txt'),'w')
        #print >> f, buff
        f.write(buff)
        f.close()

        # aggregate all entries for the year
        buff = aggregateYearsEntries(self.root, self.date)
        f = open(os.path.join(self.root, year, 'index.txt'), 'w')
        #print >> f, buff
        f.write(buff)
        f.close()

        # generate archive page
        buff = generateArchivePage(self.root)
        f = open(os.path.join(self.root, 'archive.txt'),'w')
        #print >> f, buff
        f.write(buff)
        f.close()

        year, month = time.localtime()[:2]
        year = str(year)
        month = '%02d' % month
        text = combineEntries(self.root)
        f = open(os.path.join(self.root, '_recent.txt'), 'w')
        buff = []
        buff.append(text)
        buff.append('View `%(monthname)s <%(year)s/%(month)s/index.html>`_, `%(year)s <%(year)s/index.html>`_ or `archive <archive.html>`_.' % {'year': year, 'month': month, 'monthname': MONTHS[int(month)-1]})
        f.write('\n'.join(buff))
        f.close()

        self.view.getStatus().setMessageAndClear('Finishing Generating Blog.')
        self.view = None


if __name__ in ('__main__','main'):

    isnew = init.buffer.isNewFile()
    if isnew:
        path = getNextFilename()
        directory = os.path.split(path)[0]
        if not os.path.exists(directory):
            os.makedirs(directory)
    else:
        path = init.buffer.getPath()

    init.buffer.save(init.view, path)

    # don't regenerate latest if not a new entry
    if not isnew:
        y,m,d = [int(p) for p in path.split(os.sep)[-4:-1]]
        date = (y,m,d,0,0,0,0,0,0)
    else:
        date = time.localtime()

    SwingUtilities.invokeLater(MyBlogAggregator(init.view, path, ROOT, date))


# :indentSize=4:lineSeparator=\n:noTabs=true:tabSize=4:

