#!/usr/bin/env python

###########################################################################
# Copyright (c) 2010 Minh Van Nguyen <nguyenminh2@gmail.com>
#               2014 Harald Schilly <harald.schilly@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# http://www.gnu.org/licenses/
###########################################################################

# README
#
# Updating the online list of Sage components. To use this script, run it thus:
#
# $ ./update_components.py

import copy
import os
from os.path import abspath, join
from subprocess import PIPE, Popen
import sys
import yaml

# script uses relative paths, switch to its location
os.chdir(os.path.dirname(os.path.abspath(sys.argv[0])))

raise Exception("update_components is half/half broken. It did rely on the spkges but what still should work is the list of components from the yaml config file.")

COMP_PAGE = abspath(join("..", "templates", "components.html"))
# STANDARD_DIR = abspath("../www/packages/standard")
PACKAGES_YAML = yaml.load(open(join("..", "conf", "packages.yaml")))
# dictionaries of current packages
# Each package is described by the format
# spkg name without version number: (package name, URL, brief description)
SPKG = PACKAGES_YAML['spkg']
# these components are embedded into other packages
SPKG_EMBEDDED = PACKAGES_YAML['spkg_embedded']
# these packages should be excluded
SPKG_EXCLUDE = ["examples", "extcode", "sage", "sage_scripts", "sage_root"]

##############################
# helper functions
##############################


def current_spkgs(spkgdir, spkg_db, spkg_exclude):
    r"""
    Return a list of the current packages in the standard spkg repository.

    INPUT:

    - spkgdir -- the directory where the standard spkg's are located.

    - spkg_db -- the database of standard packages.

    - spkg_exclude -- the spkg's to ignore.

    OUTPUT:

    A list of the current standard packages. If the given repository contains
    an spkg not in the spkg database, we exit the script. In that case, you
    might want to add that missing spkg to the spkg database of this script.
    """
    # get list of current standard packages
    P = Popen("ls %s/*.spkg" % spkgdir, shell=True, stdout=PIPE)
    P = P.stdout.readlines()
    P = [os.path.basename(s.strip()) for s in P]
    # get rid of everything starting from the first dot "."
    P = [s[:s.find(".")] for s in P]
    # get rid of everything starting from the last dash "-"
    P = [s[:s.rfind("-")] for s in P]
    # determine those spkg's that are in the database of standard spkg's
    spkg = []
    for s in P:
        if s in spkg_db:
            spkg.append(s)
        elif s in spkg_exclude:
            continue
        else:
            print("Unknown package '%s'. Consider adding this spkg to the spkg database of this script. Then re-run the script. Exiting..." % s)
            sys.stdout.flush()
            sys.exit(1)
    return spkg


def replace_special(s):
    r"""
    Replace each special character from the spkg description with an
    equivalent that is suitable for display on web pages. The special
    characters we consider usually include escape sequences specific to LaTeX.

    INPUT:

    - s -- a string that describes an spkg.

    OUTPUT:

    A string the same as 's'. However, all special characters (LaTeX macros
    for special characters) are replaced with equivalent HTML codes.
    """
    replace_table = [
        ('\\"o', "&ouml;"),
        ('\\"u', "&uuml;")
    ]
    cleansed_str = copy.copy(s)
    for candidate, target in replace_table:
        cleansed_str = cleansed_str.replace(candidate, target)
    return cleansed_str


def update_page(f, spkg_current, spkg_db, spkg_embedded, sdelim, edelim):
    r"""
    Update the components page with the current list of standard spkg's.

    INPUT:

    - f -- the components page to update.

    - spkg_current -- the current list of standard packages.

    - spkg_db -- the database of standard packages.

    - spkg_embedded -- the list of packages that are embedded in other spkg's.

    - sdelim -- the delimiter signifying the start of the list of components.

    - edelim -- the delimiter signifying the end of the list of components.

    OUTPUT:

    Update the given components page with the current list of standard
    packages.
    """
    page = "{# AUTOGENERATED COMPONENT LIST -- update_components.py #} \n"
    # get the package descriptions
    P = []
    for s in spkg_current:
        P.append(spkg_db[s])
    P += spkg_embedded.values()
    # sorting the list of packages, ignoring cases
    P = sorted(P, key=lambda x: x[0].lower())
    page = "<ol track='links-components'>\n"
    for s in P:
        # no URL provided for a component
        if s[1] == "":
            page += "<li>%s: %s</li>\n" % (s[0], replace_special(s[2]))
        else:
            page += '<li><a href="%s">%s</a>: %s</li>\n' %  \
                (s[1], s[0], replace_special(s[2]))
    page += "</ol>\n"
    with open(f, "w") as F:
        F.write(page)


def usage():
    """
    Print the usage information for this script.
    """
    print("".join(["Usage: ", sys.argv[0]]))
    sys.stdout.flush()

##############################
# the script starts here
##############################


if __name__ == "__main__":
    # sanity checks
    if len(sys.argv) != 1:
        # this script currently doesn't accept any argument
        usage()
        sys.exit(1)
    # if not os.path.isdir(STANDARD_DIR):
    #    print("Invalid standard spkg repository directory %s. Exiting..." % STANDARD_DIR)
    #    sys.stdout.flush()
    #    sys.exit(1)
    # now update the list of components
    update_page(
        COMP_PAGE,
        current_spkgs(SPKG, SPKG_EXCLUDE),
        SPKG,
        SPKG_EMBEDDED)
