#! /usr/bin/python

import datetime
import locale
import os
import re
import time

max_entries = 8
max_name = 31

def list_dirs (dir):
    return filter (lambda x: os.path.isdir (os.path.join (dir, x)), os.listdir (dir))

def find (dir, test):
    dir = re.sub ( "/*$", '/', dir)
    result = []
    for (root, dirs, files) in os.walk (dir):
        result += test (root, dirs, files)
    return result

def find_files (dir, pattern='.*'):
    '''
    Return list of files under DIR matching the regex pattern.
    '''
    if type ('') == type (pattern):
        pattern = re.compile (pattern)
    def test (root, dirs, files):
        return [os.path.join (root, f) for f in files if pattern.search (f)]
    return find (dir, test)

def find_dirs (dir, pattern='.*'):
    '''
    Return list of dirs under DIR matching the regex pattern.
    '''
    if type ('') == type (pattern):
        pattern = re.compile (pattern)
    def test (root, dirs, files):
        return [os.path.join (root, d) for d in dirs if pattern.search (d)]
    return find (dir, test)

def delinkify (file_name):
    first = True
    for component in file_name.split ('/'):
        if first:
            file_name = ''
            first = False
        file_name += '/' + component
        while os.path.islink (file_name):
            file_name = os.readlink (file_name)
    return file_name

class SystemFailed:
    def __init__ (self, s):
        pass

def system (command, raise_on_error=True):
    print 'executing: %(command)s' % locals ()
    status = os.system (command)
    if status and raise_on_error:
        raise SystemFailed ('Command failed: %(command)s' % locals ())

def symlink (source, dest, raise_on_error=True):
    print 'link %(source)s -> %(dest)s' % locals ()
    os.symlink (source, dest)

def get_served (log_file):
    f = file (log_file).read ()
    lst = re.findall ('\n(.* [0-9]{2}:[0-9]{2}:[0-9]{2}) peder .*: Fullpath : (.*)', f)
    year = datetime.datetime.now ().year
    return map (lambda x: (time.strptime ('2008 ' + x[0], '%Y %b %d %H:%M:%S')[:3], x[1]), lst)

def weekdag (date):
    locale.setlocale (locale.LC_ALL, 'nl_NL')
    s = date.strftime ("%A")
    locale.setlocale (locale.LC_ALL, 'C')
    return s

def get_relative_day (date, now=None): 
    if not now:
        now = datetime.datetime.now ()
    diff = date.date () - now.date ()

    if not diff.days:
        relative = 'vandaag'
    elif diff.days == -1:
        relative = 'gisteren'
    elif diff.days == -2:
        relative = 'eergisteren'
    elif diff.days == 1:
        relative = 'morgen'
    elif diff.days == 1:
        relative = 'overmorgen'
    elif diff.days > -7:
        # relative = date.strftime ("%A")
        relative = weekdag (date)
    else:
        relative = date.strftime ("%Y-%m-%d")
    past = -diff.days
    return '%(past) 3d %(relative)s ' % locals ()

def get_recent_days (served, max_days=max_entries):
    served.reverse ()
    prev_date = None
    days = 0
    lst = []
    i = 0
    year = datetime.datetime.now ().year
    for date, file_name in served:
        i += 1
        if prev_date != date:
            prev_date = date
            days += 1
            if days > max_days:
                break
            day = get_relative_day (datetime.datetime (*date))
        lst.append ((day, i, file_name))
    return lst
    
def get_created (dir):
    lst = []
    for file_name in find_files (dir, '.*'):
        created = os.stat (file_name).st_mtime
        year, month, day = time.localtime (created)[:3]
        lst.append (((year, month, day), file_name))
    return sorted (lst)
                    
def create_ushare_tree (dir, lst, per_dir=True):
    if not lst:
        return
    n = len (lst)
    tmp = os.path.dirname (dir) + '/.' + os.path.basename (dir)
    system ('rm -rf %(tmp)s' % locals ())
    last_dir_name = None
    full_dir = None
    for day, i, file_name in lst:
        if not os.path.exists (file_name):
            print 'no such file:', file_name
            continue
        file_name = delinkify (file_name)
        day_dir = '%(tmp)s/%(day)s' % locals ()
        if not os.path.isdir (day_dir):
            os.makedirs (day_dir, 0755)
        base = os.path.basename (file_name)
        c = n - i
        if not per_dir:
            symlink ('%(file_name)s' % locals (),
                     '%(tmp)s/%(day)s/%(c) 3d: %(base)s' % locals ())
        else:
            # Reduce the number of daily/recent entries by grouping
            # into dirs.
            if os.path.exists ('%(full_dir)s/%(base)s' % locals ()):
                last_dir_name = ''
            dir_name = os.path.basename (os.path.dirname (file_name))
            if last_dir_name != dir_name:
                full_dir = '%(tmp)s/%(day)s/%(c) 3d: %(dir_name)s' % locals ()
                last_dir_name = dir_name
                if not os.path.exists (full_dir):
                    os.makedirs (full_dir, 0755)
                    origin = os.path.dirname (file_name)
                    symlink ('%(origin)s' % locals (),
                             '%(full_dir)s/00 - bron' % locals ())
            symlink ('%(file_name)s' % locals (),
                     '%(full_dir)s/%(base)s' % locals ())
    system ('rm -rf %(dir)s' % locals ())
    system ('mv %(tmp)s %(dir)s' % locals ())
                    
def get_tree_per_letter (dir):
    tree = [[]]
    i = 0
    for d in sorted (list_dirs (dir)):
        if len (tree[-1]) and tree[-1][0][0] != d[0]:
            tree.append ([])
        tree[-1] += [d]
    return tree

def balance_tree (tree):
    # FIXME, hw?
    i = 1
    while i < len (tree):
        print 'i:', i
        print tree[i]
        n = len (tree[i])
        n_p = len (tree[i-1])
        n_n = max_entries    
        if i + 1 < len (tree):
            n_n = len (tree[i+1])
        if n > max_entries:
            if n_p + n < max_entries * 2:
                lst = tree[i-1] + tree[i]
                tree[i-1] = lst[:max_entries]
                tree[i] = lst[max_entries:]
            elif n_n + n < max_entries * 2:
                lst = tree[i] + tree[i+1]
                tree[i] = lst[:max_entries]
                tree[i+1] = lst[max_entries:]
            elif n_p + n_n + n < max_entries * 3:
                lst = tree[i-1] + tree[i] + tree[i+1]
                tree[i-1] = lst[:max_entries]
                tree[i] = lst[max_entries:max_entries*2]
                tree[i+1] = lst[max_entries*2:]
            else:
                tree = tree[:i] + [tree[i][:max_entries]] + [tree[i][max_entries:]] + tree[i+1:]
        elif n_p + n <= max_entries:
            tree = tree[0:i-1] + [tree[i-1] + tree[i]] + tree[i+1:]
            continue
        i += 1
    return tree

def get_top_tree (tree):
    n = len (tree)
    c = (n / max_entries) + 1
    a = n / c
    top_tree = []
    for i in range (0, c):
        if i + 1 == c:
            top_tree += [tree[i*a:]]
        else:
            top_tree += [tree[i*a:(i+1)*a]]
    return top_tree

def abbrev_name (a, b):
    return (a[:max_name/2-2] + ' .. ' + b[:max_name/2-2]).upper ()

def create_alpha_tree (archive, dir):
    system ('rm -rf %(dir)s/*..*' % locals ())
    tree = balance_tree (get_tree_per_letter (archive))
    for i in tree:
        print len(i), i
    top_tree = get_top_tree (tree)
    print top_tree
    for nodes in top_tree:
        node_name = abbrev_name (nodes[0][0], nodes[-1][-1])
        node_dir = dir + '/' + node_name
        os.makedirs (node_dir, 0755)
        for lst in nodes:
            lst_name = abbrev_name (lst[0], lst[-1])
            lst_dir = node_dir + '/' + lst_name
            os.makedirs (lst_dir, 0755)
            for dir_name in lst:
                dir_dir = lst_dir + '/' + dir_name
                symlink (archive + '/' + dir_name, dir_dir)

archive = '/home/janneke/Muziek/mp3'
ushare = '/home/janneke/var/ushare/Muziek'
def main ():
    locale.setlocale (locale.LC_ALL, 'C')
    create_alpha_tree (archive, ushare)
    create_ushare_tree (ushare + '/recent',
                        get_recent_days (get_served ('/var/log/ushare.log')))
    create_ushare_tree (ushare + '/nieuw',
                        get_recent_days (get_created (archive)))
#    system ('sudo killall -HUP ushare', raise_on_error=False)

if __name__ == '__main__':
    main ()
