# This is the build script for the QScintilla Python bindings.
#
# Copyright (c) 2023 Riverbank Computing Limited <info@riverbankcomputing.com>
# 
# This file is part of QScintilla.
# 
# This file may be used under the terms of the GNU General Public License
# version 3.0 as published by the Free Software Foundation and appearing in
# the file LICENSE included in the packaging of this file.  Please review the
# following information to ensure the GNU General Public License version 3.0
# requirements will be met: http://www.gnu.org/copyleft/gpl.html.
# 
# If you do not wish to use this file under the terms of the GPL version 3.0
# then you may purchase a commercial license.  For more information contact
# info@riverbankcomputing.com.
# 
# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.


import os

from pyqtbuild import PyQtBindings, PyQtProject
from sipbuild import Option


class QScintilla(PyQtProject):
    """ The QScintilla project. """

    def __init__(self):
        """ Initialise the project. """

        super().__init__()

        self.bindings_factories = [Qsci]

        # If there is a 'src' subdirectory then we are part of an sdist rather
        # than a full source distribution.  If part of an sdist then the
        # QScintilla source is compiled along with the bindings.  Otherwise an
        # external (ie. already built) QScintilla library is used (which may be
        # static or dynamic).
        self.qsci_external_lib = not os.path.isdir('src')

    def apply_user_defaults(self, tool):
        """ Set default values for user options that haven't been set yet. """

        super().apply_user_defaults(tool)

        if not self.qsci_external_lib:
            # If a directory to install the .api files was given then add the
            # bundled .api files as well.
            if self.api_dir:
                self.wheel_includes.append(
                        ('qsci/api/python/*.api', self.api_dir))

        if self.qsci_translations_dir:
            self.wheel_includes.append(
                    ('src/*.qm', self.qsci_translations_dir))

    def get_options(self):
        """ Return the list of configurable options. """

        options = super().get_options()

        # The directory within the wheel to install the translation files to.
        options.append(
                Option('qsci_translations_dir',
                        help="the QScintilla translation files will be installed in DIR",
                        metavar="DIR", tools=['wheel']))

        return options


class Qsci(PyQtBindings):
    """ The Qsci bindings. """

    def __init__(self, project):
        """ Initialise the bindings. """

        if project.qsci_external_lib:
            qmake_CONFIG = ['qscintilla2']
        else:
            qmake_CONFIG = []

        super().__init__(project, 'Qsci', qmake_CONFIG=qmake_CONFIG)

    def apply_user_defaults(self, tool):
        """ Set default values for user options that haven't been set yet. """

        project = self.project
        qt6 = (project.builder.qt_version >= 0x060000)

        # Set the name of the .sip file now that we know the Qt version number.
        self.sip_file = 'qscimod6.sip' if qt6 else 'qscimod5.sip'

        if self.project.qsci_external_lib:
            if self.qsci_features_dir is not None:
                os.environ['QMAKEFEATURES'] = os.path.abspath(
                        self.qsci_features_dir)

            if self.qsci_include_dir is not None:
                self.include_dirs.append(
                        os.path.abspath(self.qsci_include_dir))

            if self.qsci_library_dir is not None:
                self.library_dirs.append(
                        os.path.abspath(self.qsci_library_dir))
        else:
            # We configure CONFIG and QT textually because it's too late to
            # update qmake_CONFIG and qmake_QT.
            self.builder_settings.append('QT += widgets')

            if project.py_platform != 'ios':
                self.builder_settings.append('QT += printsupport')

            if project.py_platform in ('darwin', 'ios') and not qt6:
                self.builder_settings.append('QT += macextras')

            self.builder_settings.append(
                    'CONFIG += warn_off thread exceptions')

            self.define_macros.extend(
                    ['SCINTILLA_QT', 'SCI_LEXER',
                        'INCLUDE_DEPRECATED_FEATURES'])

            self._add_internal_lib_sources()

        super().apply_user_defaults(tool)

    def get_options(self):
        """ Return the list of configurable options. """

        options = super().get_options()

        if self.project.qsci_external_lib:
            # The directory containing the features file.
            options.append(
                    Option('qsci_features_dir',
                            help="the qscintilla2.prf features file is in DIR",
                            metavar="DIR"))

            # The directory containing the include directory.
            options.append(
                    Option('qsci_include_dir',
                            help="the Qsci include file directory is in DIR",
                            metavar="DIR"))

            # The directory containing the library.
            options.append(
                    Option('qsci_library_dir',
                            help="the QScintilla library is in DIR",
                            metavar="DIR"))

        return options

    def handle_test_output(self, test_output):
        """ Handle the output from the external test program and return True if
        the bindings are buildable.
        """

        project = self.project

        installed_version = int(test_output[0])
        installed_version_str = test_output[1]

        if project.version != installed_version:
            project.progress(
                    "QScintilla v{0} is required but QScintilla v{1} is "
                    "installed.".format(project.version_str,
                            installed_version_str))
            return False

        return True

    def is_buildable(self):
        """ Return True if the bindings are buildable. """

        # We need to check the compatibility of an external QScintilla library.
        if self.project.qsci_external_lib:
            return super().is_buildable()

        return True

    def _add_dir_sources(self, dname):
        """ Add the headers and sources from a particular directory. """

        for fn in os.listdir(dname):
            # Skip the printer support on iOS.
            if self.project.py_platform == 'ios' and fn.startswith('qsciprinter.'):
                continue

            if fn.endswith('.h'):
                self.headers.append(os.path.join(dname, fn))
            elif fn.endswith('.cpp'):
                self.sources.append(os.path.join(dname, fn))

    def _add_internal_lib_sources(self):
        """ Add to the lists of include directories, header files and source
        files to build the QScintilla library.
        """

        include_dirs = ['src']

        for dn in ('include', 'lexers', 'lexlib', 'src'):
            include_dirs.append(os.path.join('scintilla', dn))

        self._add_dir_sources(os.path.join('src', 'Qsci'))

        for dn in include_dirs:
            self._add_dir_sources(dn)

        self.include_dirs.extend(include_dirs)
