# Copyright (c) 2012-2014 The CEF Python authors. All rights reserved.
# License: New BSD License.
# Website: http://code.google.com/p/cefpython/

# First, it copies all .pyx files from upper directory to setup/.
# Then, fixes repeating of "include" statements in pyx files.

# Only the mainfile needs to have "include" statements,
# but we're using PyCharm and to get rid of "unresolved references"
# and other errors displayed in pycharm we are adding "include"
# statements in all of the pyx files.

# I'm not 100% sure how includes work in Cython, but I suspect that
# a few includes of the same file will include the same content more
# than once, it should work, but function and variable definitions are
# duplicated, it is some kind of overhead and it could lead to some
# problems in the future, better to fix it now.

# It also checks cdef & cpdef functions whether they are not missing "except *",
# it is required to add it when returning non-python type.

import glob
import os
import re
import shutil
import sys

def ExceptAllMissing(content):

    # This is not perfect, won't detect C++ custom types, but will find
    # the built-in types, templates and pointers.
    patterns = []
    patterns.append(
        r"\bcp?def\s+"
        "((int|short|long|double|char|unsigned|float|double|cpp_bool"
                "|cpp_string|cpp_wstring|uint64_t|uintptr_t|void"
                "|CefString)\s+)+"
        "\w+\([^)]*\)\s*(with\s+(gil|nogil))?\s*:")
    patterns.append(
        r"\bcp?def\s+"
        # A template ends with bracket: CefRefPtr[CefBrowser]
        # or a pointer ends with asterisk: CefBrowser*
        "[^\s]+[\]*]\s+"
        "\w+\([^)]*\)\s*(with\s+(gil|nogil))?\s*:")
    patterns.append(
        r"\bcp?def\s+"
        # A reference, eg. CefString&
        "[^\s]+&\s+"
        "\w+\([^)]*\)\s*(with\s+(gil|nogil))?\s*:")

    for pattern in patterns:
        match = re.search(pattern, content)
        if match: break

    if match:
        lineNumber = (content.count("\n", 0, match.start()) + 1)
        return lineNumber

print("\n")
mainfile = "cefpython.pyx"

pyxfiles = glob.glob("../../*.pyx")
if not len(pyxfiles):
    print("ERROR: no .pyx files found in root")
    sys.exit(1)
pyxfiles = [file for file in pyxfiles if file.find(mainfile) == -1]
# Now, pyxfiles contains all pyx files except the mainfile (cefpython.pyx),
# we do not fix includes in mainfile.

pyxfiles2 = glob.glob("../../handlers/*.pyx")
if not len(pyxfiles2):
    print("ERROR: no .pyx files found in handlers/")
    sys.exit(1)

pyxfiles = pyxfiles + pyxfiles2

# So that this is the right directory we're in.
if os.path.exists("setup"):
    print("Wrong directory, we should be inside setup!")
    sys.exit(1)

# Remove old pyx files in setup directory.
oldpyxfiles = glob.glob("./*.pyx")
print("Removing old pyx files in /setup/: %s" % oldpyxfiles)
for pyxfile in oldpyxfiles:
    if os.path.exists(pyxfile):
        os.remove(pyxfile)

# Copying pyxfiles and reading its contents.
print("Copying .pyx files to /setup/: %s" % pyxfiles)

# Copying cefpython.pyx
# and Fix includes in cefpython.pyx
# * include "handlers/focus_handler.pyx" becomes include "focus_handler.pyx"
shutil.copy("../../%s" % mainfile, "./%s" % mainfile)
with open("./%s" % mainfile, "r") as fo:
    content = fo.read()
    (content, subs) = re.subn(r"^include \"handlers/",
                              "include \"",
                              content,
                              flags=re.MULTILINE)
with open("./%s" % mainfile, "w") as fo:
    fo.write(content)
    print("%s includes fixed in %s" % (subs, mainfile))


# Rest of the files will be copied in for loop below.

print("Fixing includes in .pyx files:")
for pyxfile in pyxfiles:
    newfile = "./%s" % os.path.basename(pyxfile)
    shutil.copy(pyxfile, newfile)
    pyxfile = newfile
    with open(pyxfile, "r") as pyxfileopened:
        content = pyxfileopened.read()
        lineNumber = ExceptAllMissing(content)
        if lineNumber:
            print("WARNING: 'except *' missing in a cdef/cpdef function, "
                  "in file %s on line %d" % (os.path.basename(pyxfile), lineNumber))
            sys.exit(1)
        # Do not remove the newline - so that line numbers are exact with originals.
        (content, subs) = re.subn(r"^include[\t ]+[\"'][^\"'\n\r]+[\"'][\t ]*", "", content, flags=re.MULTILINE)
        if subs:
            print("%s includes removed in: %s" % (subs, os.path.basename(pyxfile)))
    # Reading and writing with the same handle using "r+" mode doesn't work,
    # you need to seek(0) and write the same amount of bytes that was in the
    # file, otherwise old data from the end of file stays.
    with open(pyxfile, "w") as pyxfileopened:
        pyxfileopened.write(content)

print("\n")
