#!/usr/bin/env python
# -*- coding: utf-8 -*-

# author: Ole Schuett

import re, sys
from os import path
from os.path import dirname, basename, normpath
import os

# pre-compiled regular expressions
re_module  = re.compile(r"(?:^|\n)\s*module\s+(\w+)\s.*\n\s*end\s*module",re.DOTALL)
re_useonly = re.compile(r"\n\s*use\s+(\w+)\s*,\s*only\s*:(.*)(?=\n)")
re_use     = re.compile(r"\n\s*use\s+(\w+)\s*(?=\n)")
re_pub     = re.compile(r"\n\s*public\s*::\s*(.*)(?=\n)")


#=============================================================================
def main():
    src_dir = "../src"
    parsed_files = {}
    for root, dir, files in os.walk(src_dir):
        if("preprettify" in root):
            continue
        if(".svn" in root):
            continue
        for fn in files:
            if(not fn.endswith(".F")):
                continue
            absfn = root+"/"+fn

            parsed_files[absfn] =  parse_file(absfn)

    all_used_symboles = set()
    for p in parsed_files.values():
        for m, syms in p['use']:
            for s in syms:
                all_used_symboles.add(m+"@"+s)

    n = 0
    for fn, p in parsed_files.items():
        if(len(p['mod']) != 1):
            continue
        m = p['mod'][0]
        if(m+"@*" in all_used_symboles):
            continue
        unused = []
        for s in p['pub']:
            if(m+"@"+s not in all_used_symboles):
                unused.append(s)
                n += 1
        if(len(unused) > 0):
            #print("%s USElessly declares PUBLIC: "%fn+ ", ".join(unused)+"\n")
            clean_publics(fn, unused)

    #print("Found %d unUSEd PUBLIC symbols."%n)


#=============================================================================
def clean_publics(fn, unused):
    content = open(fn).read()
    new_content = ""
    active = False
    protected = False
    new_public = []
    for line in content.split("\n"):
        if(line.strip().startswith("!API")):
            protected = True
        if(re.match(r"\s*public\s*::.*", line, re.IGNORECASE)):
            if(protected):
                protected = False
            else:
                active = True

        if(not active):
            new_content += line + "\n"
            continue

        prefix, symbols, comment = re.match("^(.*::)?([^!]*)(!.*)?$", line).groups()
        old_symbols = symbols.strip(" &,").split(",")
        new_symbols = []
        for s in old_symbols:
            s = s.strip()
            if(s.lower() not in unused):
                new_symbols.append(s)

        if(len(new_symbols) > 0):
            new_public.append( (", ".join(new_symbols), comment) )

        without_comment = re.sub("!.*", "", line).strip()
        if(len(without_comment) > 0 and without_comment[-1] != "&"):

            #flush new_public
            for i, entry in enumerate(new_public):
                if(i==0):
                    new_content += "  PUBLIC :: "
                else:
                    new_content += "            "
                new_content += entry[0]
                if(i < len(new_public)-1):
                    new_content += ",&"
                if(entry[1]):
                    new_content += "  " + entry[1]
                new_content += "\n"

            active = False
            new_public = []

    new_content = new_content[:-1] # remove last line break

    if(new_content != content):
        print("Fixed: ", fn)
        f = open(fn, "w")
        f.write(new_content)


#=============================================================================
def parse_file(fn):
    # print("parsing "+fn)
    content = open(fn).read()
    # re.IGNORECASE is horribly expensive. Converting to lower-case upfront
    content = content.lower()
    content = re.sub("!.*\n", "\n", content)
    content = re.sub("&\s*\n", "", content)
    content = re.sub("&", " ", content)

    mods = re_module.findall(content)

    uses = []
    matches = re_use.findall(content)
    for m in matches:
        uses.append((m.strip(), ("*",)))
        if(m.strip() not in ("iso_c_binding", "f77_blas", )):
            print("missing ONLY-clause: ", fn, m)

    matches = re_useonly.findall(content)
    for m in matches:
        syms = [p.split("=>")[-1].strip() for p in m[1].split(",")]
        uses.append((m[0].strip(), syms))

    publics = []
    matches = re_pub.findall(content)
    for m in matches:
        publics += [p.strip() for p in m.split(",")]

    return({"mod":mods, "use":uses, "pub":publics})

#===============================================================================
if(len(sys.argv)==2 and sys.argv[-1]=="--selftest"):
    pass #TODO implement selftest
else:
    main()

#EOF
