#!/usr/bin/env python
# This file is part of python-ly, https://pypi.python.org/pypi/python-ly
#
# Copyright (c) 2008 - 2015 by Wilbert Berendsen
#
# 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 3
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
# See http://www.gnu.org/licenses/ for more information.

"""
generate all scheme words in the _scheme_data.py file
"""
from __future__ import unicode_literals

import os
import re
try:
    from urllib.request import urlopen
except:
    from urllib import urlopen


VERSION = "2.24"
GUILE_URL = "https://www.gnu.org/software/guile/docs/docs-2.2/guile-ref/Procedure-Index.html#Procedure-Index"
LY_FUNCTION_URL = "http://lilypond.org/doc/v2.24/Documentation/internals/scheme-functions"
SCM_PATH = os.path.join(os.environ['HOME'], 'lilies/2.24.0/share/lilypond/2.24.0/scm/lily')

GUILE_IGNONED_TYPE = (
    'Apply Traps',
    'Common Trap Options',
    'Continuations',
    'Debug on Error',
    'Display Backtrace',
    'Encryption',
    'Entry Traps',
    'Exit Traps',
    'Garbage Collection Functions',
    'getopt-long Reference',
    'Internationalization',
    'Location Traps',
    'Network Databases',
    'Network Socket Address',
    'Network Sockets and Communication',
    'Procedure Traps',
    'Signals',
    'Threads',
    'Trap Utilities',
    'Source Traps',
    'Step Traps',
    'SRFI-37',
    'Stepping and Continuing',
    'System asyncs',
    'System Identification',
    'User asyncs',
    'User Information',
    'User level options interfaces',
)

GUILE_OTHER_WORDS = ['else', 'set!']

def writeList(file_, name, lst):
    file_.write("{} = [\n".format(name))
    for word in sorted(set(lst)):
        file_.write("    '{0}',\n".format(word))
    file_.write("]\n\n")

def replace(word):
    word = word.replace('&lt;', '<')
    word = word.replace('&gt;', '>')
    word = word.replace('&amp;', '&')
    return word

guilePage = urlopen(GUILE_URL).read()
guilePat = re.compile(r"<code>([a-z\d\+\?\!\*&;/=:-]+)</code></a>:</td><td>&nbsp;</td><td valign=\"top\"><a href=\".+\">(.+)</a>")

lyFunctionPage = urlopen(LY_FUNCTION_URL).read()
lyFunctionPat = re.compile(r'<u>Function:</u> <b><code>(ly:.+)</code></b>')

defineFunc = re.compile(r'\((?:define\*?|define-safe)-public\s+\(([-><a-zA-Z:\?]+)[\s\)]')
defineMacro = re.compile(r'(?m)^\((?:defmacro\*?|define-syntax(?:-rule)?)-public\s+\(?([-><a-zA-Z:\?]+)[\s\)]')
defineVar = re.compile(r'\((?:define\*?|define-safe)-public\s+([-><a-zA-Z:\?]+)[\s\)]')
startWithLy = re.compile(r"(ly:[-><a-zA-Z:\?]+)[\s\)]")


schemeDataPath = os.path.join(os.path.split(__file__)[0], '_scheme_data.py')

def main():
    with open(schemeDataPath, "w") as f:
        f.write("#generated by makeschemedata.py\n\nversion=\"{}\"\n\n".format(VERSION))

        guileWords = []
        for m in guilePat.finditer(guilePage.decode('utf-8')):
            if m.group(2) not in GUILE_IGNONED_TYPE:
                proc = m.group(1)
                if not proc.startswith('gds-'):
                    guileWords.append(replace(proc))
        guileWords += GUILE_OTHER_WORDS


        lyFunctions = []
        lyVars = []
        lyCons = []
        for word in lyFunctionPat.finditer(lyFunctionPage.decode('utf-8')):
            lyFunctions.append(replace(word.group(1)))

        for filename in os.listdir(SCM_PATH):
            path = os.path.join(SCM_PATH, filename)
            with open(path) as inp:
                text = inp.read()
                for m in defineFunc.finditer(text):
                    lyFunctions.append(m.group(1))
                    if m.group(1) == "name":
                        print("defineFunc")

                for m in defineMacro.finditer(text):
                    lyFunctions.append(m.group(1))
                    if m.group(1) == "name":
                        print("defineMacro")

                for m in startWithLy.finditer(text):
                    lyFunctions.append(m.group(1))

                for m in defineVar.finditer(text):
                    word = m.group(1)
                    if word.isupper():
                        lyCons.append(word)
                    else:
                        lyVars.append(word)

        for word in lyFunctions:
            if word in lyVars:
                lyVars.remove(word)

        writeList(f, 'scheme_keywords', guileWords)
        writeList(f, 'scheme_functions', lyFunctions)
        writeList(f, 'scheme_variables', lyVars)
        writeList(f, 'scheme_constants', lyCons)


if __name__ == "__main__":
    main()
