#!/usr/bin/env python
# -*- coding: latin-1 -*-
##
# makeWikiIndex
# Génère une page 'index.htm' de liens vers les pages HTML du répertoire courant
#   (pages reconnues si elles proviennent des sites
#    everything-list, MathWorld, OEIS, PlanetMath,
#    Wikibooks, Wikipedia, Wikiquote ou Wikisource)
# (c) Olivier Pirson --- DragonSoft
# http://www.opimedia.be/DS/
# Débuté le 7 août 2006
# v.01.00 --- 10 août 2006
# v.01.01 --- 17 août 2006
# v.01.02 --- 24 août 2006
#         --- 21 septembre 2006
# v.01.03 --- 30 novembre 2006
# v.01.04 --- 7 octobre 2007
#         --- 2 mars 2008
# v.01.05 --- 19 mars 2008
#         --- 26 mars 2008
# v.01.06 --- 7 avril 2008
# v.01.07 --- 21 mai 2008
# v.01.08 --- 24 juillet 2008 : correction <tr>
# v.01.09 --- 12 novembre 2008 : prépare Python 3
# v.01.10 --- 3 mars 2009 : changé regexp Wikipédia
# v.01.11 --- 13 mars 2009 : target="_blank"
# v.01.12 --- 3 avril 2009 : Wikipédia: discussion
# v.01.13 --- 27 septembre 2009 : nouveau site web
# v.01.30 --- 5 novembre 2009 : Python 3
# v.01.31 --- 30 décembre 2009 : <h1...>(...)</h1>
#                                nb_files et progression *
#                                signale les "doublons"
#         --- 15 mars 2010 : nouveau site web
#         --- 2 janvier 2012 : nouveau site web
###########################################################
VERSION = 'v.01.31 --- 2012 January 2'

import codecs
import glob
import os
import re
import sys
import time

assert sys.version_info[0] >= 3, ('DSPython require Python 3 or better', sys.version)

try:
    if not 'profile' in dir():
        import psyco
        psyco.full()
except ImportError:
    pass



##############
# Constantes #
##############
RE_ENDHEAD = re.compile('</head>', re.I)  # regexp pour la fin de l'en-tête HTML

RE_FORMAT = re.compile(r'<meta\s+.*?content=".*?charset=\s*(.+?)\s*".*>', re.I)
            # regexp pour le format en lecture du fichier

RE_SITE = re.compile(r'<title>\s*(.+?)\s*</title>', re.I)  # regexp pour identifier le site

RE_SITE_EVERYTHING = re.compile('(everything-list)@eskimo.com', re.I)
                     # regexp pour identifier le site everything-list

RE_SITE_WIKIQUOTE = re.compile('title="wikiquote\s*\((\w\w)\)', re.I)
                    # regexp pour identifier le site Wikiquote


RE_TITLE = {}   # dictionnaire de regexps pour le titre
RE_DATE = {}    # dictionnaire de regexps pour la date de dernière modification
RE_AUTHOR = {}  # dictionnaire de regexps pour l'auteur

RE_TITLE[None] = re.compile('<title>(.*?)</title>', re.I)
RE_DATE[None] = re.compile(
    '<meta.+?name="(?:Last-Modified|date-revision).*?".*?content="(.*?)">', re.I)
RE_AUTHOR[None] = None

# everything-list
RE_TITLE['everything-list'] = RE_TITLE[None]
RE_DATE['everything-list'] = re.compile(
    r'<span class="date".*?>\s*\S+?\s+(\d{2}.*?\d{4}).*?</span>', re.I)
RE_AUTHOR['everything-list'] = re.compile(
    r'<span class="sender".*?>\s*(?:"|&quot;)*(.*?)(?:"|&quot;)*\s*</span>', re.I)

# MathWorld
RE_TITLE['MathWorld'] = re.compile(
    r'<title>(.*?)\s*--\s*?from.+?(?:MathWorld|Eric Weisstein).*?</title>',
    re.I)
RE_DATE['MathWorld'] = RE_DATE[None]
RE_AUTHOR['MathWorld'] = None

# OEIS
RE_TITLE['OEIS'] = re.compile('<input.*value="id:(.*?)"', re.I)
RE_DATE['OEIS'] = re.compile('<td.+<a href=".+" title="(.+?)"', re.I)
RE_AUTHOR['OEIS'] = None

# PlanetMath
RE_TITLE['PlanetMath'] = re.compile(r'<title>.*PlanetMath:\s*(.*?)</title>', re.I)
RE_DATE['PlanetMath'] = re.compile(r'This is.+?born.+?modified.*?(\d{4}-\d{2}-\d{2})', re.I)
RE_AUTHOR['PlanetMath'] = None

# Wiki...
RE_TITLE['Wikibooks'] = RE_TITLE['Wikipedia.en'] = RE_TITLE['Wikipedia.fr'] \
                        = RE_TITLE['Wikiquote.en'] = RE_TITLE['Wikiquote.fr'] \
                        = RE_TITLE['Wikisource'] \
                        = re.compile(r'<h1.*>.*?([^<>]*).*?</h1>', re.I)

RE_DATE['Wikibooks'] = RE_DATE['Wikipedia.en'] = RE_DATE['Wikipedia.fr'] \
                       = RE_DATE['Wikiquote.en'] = RE_DATE['Wikiquote.fr'] \
                       = RE_DATE['Wikisource'] \
                       = re.compile(r'<li\s+id=".*?lastmod">.*?(\d{1,2}\D+\d{4}).*?</li>', re.I)
RE_AUTHOR['Wikibooks'] = RE_AUTHOR['Wikipedia.en'] = RE_AUTHOR['Wikipedia.fr'] \
                         = RE_AUTHOR['Wikiquote.en'] = RE_AUTHOR['Wikiquote.fr'] \
                         = RE_AUTHOR['Wikisource'] = None



#############
# Fonctions #
#############
def extract_infos(filename, quiet=False, verbose=False):
    """Renvoie un tuple (nom du site, titre, date, auteur).
    Si verbose
    alors envoie le nom des fichiers traités sur la sortie standard
    pre: filename: nom du fichier"""
    if verbose:
        print(filename.replace('\u2013', '-'), end=' - ')
        sys.stdout.flush()

    site, format = identify(filename, quiet)

    if verbose:
        print(site, end='')
        if format != None:
            print('-', end='')
            try:
                print(format, end='')
            except:
                print('?', end='')
        else:
            print('-', end='')
        sys.stdout.flush()

    title = date = author = None

    site_str = ('' if site == None
                else site)
    if site not in RE_TITLE.keys():
        site = None

    try:
        f = codecs.open(filename, 'r', format, 'replace')
    except IOError:
        print('Failed open %r for reading!!!' % filename, file=sys.stderr)
        sys.stderr.flush()
        return (None, None, None, None)
    # Parcours le fichier à la recherche du titre et de la date
    try:
        for l in f:
            if title == None:
                r = RE_TITLE[site].search(l)
                if r != None:
                    title = r.group(1)
            if date == None:
                r = RE_DATE[site].search(l)
                if r != None:
                    date = r.group(1)
            if (author == None) and (RE_AUTHOR[site] != None):
                r = RE_AUTHOR[site].search(l)
                if r != None:
                    author = r.group(1)
            elif (title != None) and (date != None):
                break
    except:
        print('Failed reading %r!!!' % filename, file=sys.stderr)
        sys.stderr.flush()
        return (site_str,
                title if title != None
                else filename,
                date, author)
    f.close()

    if title == None:
        title = '[%s]' % filename
    if date == None:
        date = ''

    if verbose:
        print('-', end='')
        try:
            print(title, end='')
        except:
            print('?', end='')
        print('-', end='')
        try:
            print(date, end='')
        except:
            print('?', end='')
        if author != None:
            print('-', end='')
            try:
                print(author, end='')
            except:
                print('?', end='')
        print()
        sys.stdout.flush()
    return (site_str, title, date, author)


def help_msg():
    """Affiche le message d'aide sur la sortie des erreurs
    et termine le programme par un code d'erreur 1"""
    print("""makeWikiIndex [-s] [-h] [-q] [-v] [title]

  Make a 'index.htm' file
    with links to the '*.htm*' files of the current directory
    (pages recognized if came from sites everything-list,
     MathWorld, OEIS, PlanetMath,
     Wikibooks, Wikipedia, Wikiquote or Wikisource)
  (c) Olivier Pirson --- DragonSoft --- http://www.opimedia.be/DS/
             %s
  -s: print namesite at the begining of each line
  title: title of the HTML file 'index.htm'

  -h: help
  -q: quiet (no print 'Unknow site...' on standard output)
  -v: verbose (print filename and infos on error ouptut)""" % VERSION, file=sys.stderr)
    sys.exit(1)


def identify(filename, quiet=False):
    """Renvoie (site, format)
    où site correspond aux sites
       'everything-list', 'MathWorld', 'OEIS', 'PlanetMath',
       'Wikibooks', 'Wikipedia.en', 'Wikipedia.fr', 'Wikiquote.en', 'Wikiquote.fr', 'Wikisource'
       ou le champ <title>...</title>
       trouvé dans le fichier, ou None si pas trouvé
    et format est le format référencé par un "charset=..." dans le fichier
       ou None si pas trouvé
    Si not quiet
    alors envoie les fichiers non reconnus parmi les sites gérés
          sont indiquées sur la sortie standard
    pre: filename: nom du fichier"""
    site = format = None

    try:
        f = codecs.open(filename, 'r', 'UTF-8', 'replace')
    except IOError:
        print('Failed open %r for reading!!!' % filename, file=sys.stderr)
        sys.stderr.flush()
        return (None, None)

    # Parcours le fichier
    try:
        for l in f:
            if site == None:
                r = RE_SITE.search(l)
                if r != None:
                    site = r.group(1)
                else:
                    r = RE_SITE_WIKIQUOTE.search(l)
                    if r != None:
                        site = 'wikiquote.%s' % r.group(1)
                    else:
                        r = RE_SITE_EVERYTHING.search(l)
                        if r != None:
                            site = r.group(1)

            if format == None:
                r = RE_FORMAT.search(l)
                if r != None:
                    format = r.group(1)
            if RE_ENDHEAD.search(l) != None:
                break
    except:
        print('Failed reading %r!!!' % filename, file=sys.stderr)
        sys.stderr.flush()
        return (filename, None)
    f.close()

    # Fixe les résultats pour les 4 sites identifiés par le programme
    if site != None:
        site_lower = site.lower()
        if 'wikipedia' in site_lower:
            site = 'Wikipedia.en'
        elif 'wikipédia' in site_lower:
            site = 'Wikipedia.fr'
        elif 'everything-list' == site_lower:
            site = 'everything-list'
        elif ('mathworld' in site_lower) or ('eric weisstein' in site_lower):
            site = 'MathWorld'
        elif 'planetmath' in site_lower:
            site = 'PlanetMath'
        elif 'wikibooks' in site_lower:
            site = 'Wikibooks'
        elif 'wikiquote.en' == site_lower:
            site = 'Wikiquote.en'
        elif 'wikiquote.fr' == site_lower:
            site = 'Wikiquote.fr'
        elif 'wikisource' in site_lower:
            site = 'Wikisource'
        elif (('encyclopedia of integer sequences' in site_lower) or ('oeis' in site_lower)
              or (('encyclo' in site_lower) and ('suites de nombres entiers' in site_lower))):
            site = 'OEIS'
        elif not quiet:
            try:
                print('Unknow site of %s: %s!!!' % (filename, site))
            except:
                print('Unknow site!!!')
            sys.stdout.flush()

    return (site, format)



########
# Main #
########
if __name__ == '__main__':
    if os.path.isfile('index.htm'):
        print("'index.htm' file already exist!!! Delete it to execute this script.\n", file=sys.stderr)
        help_msg()

    # Gestion des options
    title_index = None  # titre de la page HTML 'index.htm'

    opt_namesite = False  # mode affiche le nom du site pour chaque ligne
    opt_quiet = False     # mode silencieux
    opt_verbose = False   # mode verbeux

    for i in range(1, len(sys.argv)):
        if sys.argv[i] == '-h':
            help_msg()
        elif sys.argv[i] == '-q':
            opt_quiet = True
        elif sys.argv[i] == '-s':
            opt_namesite = True
        elif sys.argv[i] == '-v':
            opt_verbose = True
        elif title_index == None and sys.argv[i][0] != '-':
            title_index = sys.argv[i]
        else:
            help_msg()
    if title_index == None:
        title_index = 'makeWikiIndex --- ' + VERSION

    d = {}  # dictionnaire dont les clés seront des tuples (nom du site, titre)
    #           et les éléments une liste de tuple (titre, date, nom du fichier, auteur)

    # Parcours les fichiers '*.htm*' du répertoire courant
    print('Walk files...', file=sys.stderr, end='')
    sys.stderr.flush()
    nb_files = 0
    for f in glob.glob('*.htm*'):
        nb_files += 1
        if nb_files%100 == 0:
            print('*', file=sys.stderr, end='')
            sys.stderr.flush()

        site, title, date, author = extract_infos(f, quiet=opt_quiet, verbose=opt_verbose)

        if site == 'everything-list':
            r = re.search(r'^Re:\s*(.*)', title, re.I)
            if r != None:
                title = '<i>Re:</i>'
        elif (site == 'Wikipedia.en') or (site == 'Wikiquote.en'):
            r = re.search(r'^Talk:\s*(.*)', title, re.I)
            if r != None:
                title = '<i>Talk:</i>'
            else:
                r = re.search(r'^Image:\s*(.*)', title, re.I)
                if r != None:
                    title = '<i>Image:</i>'
        elif ((site == 'Wikipedia.fr') or (site == 'Wikiquote.fr')
              or (site == 'Wikibooks') or (site == 'Wikisource')):
            r = re.search(r'^Discu(?:ter|ssion):?\s*(.*)', title, re.I)
            if r != None:
                title = '<i>Discuter:</i>'
            else:
                r = re.search(r'^Image:\s*(.*)', title, re.I)
                if r != None:
                    title = '<i>Image:</i>'
        else:
            r = None
        title_key = (title.lower() if r == None
                     else r.group(1).lower())

        if (site, title_key) in d:  # déjà rencontré un fichier du même site et de "même" titre
            if title_key == title.lower():  # le lien à ajouter est "primaire"
                d[(site, title_key)].insert(0, (title, date, f, author))
            else:                   # le lien à ajouter est "secondaire"
                d[(site, title_key)].append((title, date, f, author))
        else:                       # c'est le premier fichier de ce site avec ce titre
            d[(site, title_key)] = [(title, date, f, author)]

    # Clés de d triées
    print('\nSort...', file=sys.stderr)
    sys.stderr.flush()
    keys = sorted(d, key=lambda t: (t[0].lower(), t[1].lower()))

    # Création du fichier 'index.htm'
    print('Make "index.htm"...', file=sys.stderr)
    sys.stderr.flush()
    try:
        fout = codecs.open('index.htm', 'w', 'latin-1', 'replace')
    except IOError:
        sys.exit("Failed open 'index.htm' for writing!!!")
    # En-tête HTML
    print("""<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
  <meta name="creator" content="makeWikiIndex --- %s">
  <meta name="date-revision-yyyymmdd" content="%s">
  <title>%s</title>
  <style type="text/css"><!--
    a.relative:link {background-color:inherit;
                     color:navy;}
    a.relative:visited {background-color:inherit;
                        color:black;}
  --></style>
</head>
<body>
<h3 style="display:inline;margin-bottom:0pt">%s</h3> (%u files)
<table summary="list of links">""" % (VERSION, time.strftime('%Y%m%d'), title_index, title_index, nb_files),
          file=fout)
    # Parcours d dans l'ordre
    for k in keys:
        print(('<tr><td><font size="-1">%s</font></td>' % k[0]) if opt_namesite  # site
              else '<tr>',
              file=fout)
        (title, date, filename, author) = d[k][0]
        if k[1].lower() != title.lower():
            # le premier élément de la ligne est "secondaire"
            print('  <td>&nbsp;</td><td>%s</td>' % k[1], file=fout)  # title

        # Vérifie les "doublons"
        if len(d[k]) > 2:
            print('%r: ' % repr(k[1]), file=sys.stderr, end='')
            lt = []
            for t in d[k]:
                lt.append(repr(t[2]))
            print('%s !' % ', '.join(lt), file=sys.stderr)
            sys.stderr.flush()

        # Parcours les éléments de la ligne
        for (title, date, filename, author) in d[k]:
            if author == None:
                print(('  <td>&nbsp;</td><td><a class="relative" href="%s" target="_blank">%s</a>'
                               + ' <font size="-1">%s</font></td>')
                      % (filename, title, date),
                      file=fout)
            else:
                print(('  <td>&nbsp;</td><td><a class="relative" href="%s" target="_blank">%s</a>'
                               + ' <font size="-1"><i>(%s)</i> %s</font></td>')
                      % (filename, title, author, date),
                      file=fout)
        print('</tr>\n', file=fout)
    # Fin HTML
    print("""</table>
</body>
</html>""", file=fout)
    fout.close()
