#! /usr/bin/env python
# encoding: utf-8
# WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file

typos = {
    'feature': 'features',
    'sources': 'source',
    'targets': 'target',
    'include': 'includes',
    'export_include': 'export_includes',
    'define': 'defines',
    'importpath': 'includes',
    'installpath': 'install_path',
    'iscopy': 'is_copy',
    'uses': 'use',
}
meths_typos = ['__call__', 'program', 'shlib', 'stlib', 'objects']
import sys
from waflib import Logs, Build, Node, Task, TaskGen, ConfigSet, Errors, Utils
from waflib.Tools import ccroot


def check_same_targets(self):
    mp = Utils.defaultdict(list)
    uids = {}

    def check_task(tsk):
        if not isinstance(tsk, Task.Task):
            return
        if hasattr(tsk, 'no_errcheck_out'):
            return
        for node in tsk.outputs:
            mp[node].append(tsk)
        try:
            uids[tsk.uid()].append(tsk)
        except KeyError:
            uids[tsk.uid()] = [tsk]

    for g in self.groups:
        for tg in g:
            try:
                for tsk in tg.tasks:
                    check_task(tsk)
            except AttributeError:
                check_task(tg)
    dupe = False
    for (k, v) in mp.items():
        if len(v) > 1:
            dupe = True
            msg = '* Node %r is created more than once%s. The task generators are:' % (
                k, Logs.verbose == 1 and " (full message on 'waf -v -v')" or ""
            )
            Logs.error(msg)
            for x in v:
                if Logs.verbose > 1:
                    Logs.error('  %d. %r', 1 + v.index(x), x.generator)
                else:
                    Logs.error('  %d. %r in %r', 1 + v.index(x), x.generator.name, getattr(x.generator, 'path', None))
            Logs.error('If you think that this is an error, set no_errcheck_out on the task instance')
    if not dupe:
        for (k, v) in uids.items():
            if len(v) > 1:
                Logs.error(
                    '* Several tasks use the same identifier. Please check the information on\n   https://waf.io/apidocs/Task.html?highlight=uid#waflib.Task.Task.uid'
                )
                tg_details = tsk.generator.name
                if Logs.verbose > 2:
                    tg_details = tsk.generator
                for tsk in v:
                    Logs.error('  - object %r (%r) defined in %r', tsk.__class__.__name__, tsk, tg_details)


def check_invalid_constraints(self):
    feat = set()
    for x in list(TaskGen.feats.values()):
        feat.union(set(x))
    for (x, y) in TaskGen.task_gen.prec.items():
        feat.add(x)
        feat.union(set(y))
    ext = set()
    for x in TaskGen.task_gen.mappings.values():
        ext.add(x.__name__)
    invalid = ext & feat
    if invalid:
        Logs.error(
            'The methods %r have invalid annotations:  @extension <-> @feature/@before_method/@after_method',
            list(invalid)
        )
    for cls in list(Task.classes.values()):
        if sys.hexversion > 0x3000000 and issubclass(cls, Task.Task) and isinstance(cls.hcode, str):
            raise Errors.WafError(
                'Class %r has hcode value %r of type <str>, expecting <bytes> (use Utils.h_cmd() ?)' % (cls, cls.hcode)
            )
        for x in ('before', 'after'):
            for y in Utils.to_list(getattr(cls, x, [])):
                if not Task.classes.get(y):
                    Logs.error('Erroneous order constraint %r=%r on task class %r', x, y, cls.__name__)
        if getattr(cls, 'rule', None):
            Logs.error('Erroneous attribute "rule" on task class %r (rename to "run_str")', cls.__name__)


def replace(m):
    oldcall = getattr(Build.BuildContext, m)

    def call(self, *k, **kw):
        ret = oldcall(self, *k, **kw)
        for x in typos:
            if x in kw:
                if x == 'iscopy' and 'subst' in getattr(self, 'features', ''):
                    continue
                Logs.error('Fix the typo %r -> %r on %r', x, typos[x], ret)
        return ret

    setattr(Build.BuildContext, m, call)


def enhance_lib():
    for m in meths_typos:
        replace(m)

    def ant_glob(self, *k, **kw):
        if k:
            lst = Utils.to_list(k[0])
            for pat in lst:
                sp = pat.split('/')
                if '..' in sp:
                    Logs.error("In ant_glob pattern %r: '..' means 'two dots', not 'parent directory'", k[0])
                if '.' in sp:
                    Logs.error("In ant_glob pattern %r: '.' means 'one dot', not 'current directory'", k[0])
        return self.old_ant_glob(*k, **kw)

    Node.Node.old_ant_glob = Node.Node.ant_glob
    Node.Node.ant_glob = ant_glob

    def ant_iter(self, accept=None, maxdepth=25, pats=[], dir=False, src=True, remove=True, quiet=False):
        if remove:
            try:
                if self.is_child_of(self.ctx.bldnode) and not quiet:
                    quiet = True
                    Logs.error(
                        'Calling ant_glob on build folders (%r) is dangerous: add quiet=True / remove=False', self
                    )
            except AttributeError:
                pass
        return self.old_ant_iter(accept, maxdepth, pats, dir, src, remove, quiet)

    Node.Node.old_ant_iter = Node.Node.ant_iter
    Node.Node.ant_iter = ant_iter
    old = Task.is_before

    def is_before(t1, t2):
        ret = old(t1, t2)
        if ret and old(t2, t1):
            Logs.error('Contradictory order constraints in classes %r %r', t1, t2)
        return ret

    Task.is_before = is_before

    def check_err_features(self):
        lst = self.to_list(self.features)
        if 'shlib' in lst:
            Logs.error('feature shlib -> cshlib, dshlib or cxxshlib')
        for x in ('c', 'cxx', 'd', 'fc'):
            if not x in lst and lst and lst[0] in [x + y for y in ('program', 'shlib', 'stlib')]:
                Logs.error('%r features is probably missing %r', self, x)

    TaskGen.feature('*')(check_err_features)

    def check_err_order(self):
        if not hasattr(self, 'rule') and not 'subst' in Utils.to_list(self.features):
            for x in ('before', 'after', 'ext_in', 'ext_out'):
                if hasattr(self, x):
                    Logs.warn('Erroneous order constraint %r on non-rule based task generator %r', x, self)
        else:
            for x in ('before', 'after'):
                for y in self.to_list(getattr(self, x, [])):
                    if not Task.classes.get(y):
                        Logs.error('Erroneous order constraint %s=%r on %r (no such class)', x, y, self)

    TaskGen.feature('*')(check_err_order)

    def check_compile(self):
        check_invalid_constraints(self)
        try:
            ret = self.orig_compile()
        finally:
            check_same_targets(self)
        return ret

    Build.BuildContext.orig_compile = Build.BuildContext.compile
    Build.BuildContext.compile = check_compile

    def use_rec(self, name, **kw):
        try:
            y = self.bld.get_tgen_by_name(name)
        except Errors.WafError:
            pass
        else:
            idx = self.bld.get_group_idx(self)
            odx = self.bld.get_group_idx(y)
            if odx > idx:
                msg = "Invalid 'use' across build groups:"
                if Logs.verbose > 1:
                    msg += '\n  target %r\n  uses:\n  %r' % (self, y)
                else:
                    msg += " %r uses %r (try 'waf -v -v' for the full error)" % (self.name, name)
                raise Errors.WafError(msg)
        self.orig_use_rec(name, **kw)

    TaskGen.task_gen.orig_use_rec = TaskGen.task_gen.use_rec
    TaskGen.task_gen.use_rec = use_rec

    def _getattr(self, name, default=None):
        if name == 'append' or name == 'add':
            raise Errors.WafError('env.append and env.add do not exist: use env.append_value/env.append_unique')
        elif name == 'prepend':
            raise Errors.WafError('env.prepend does not exist: use env.prepend_value')
        if name in self.__slots__:
            return super(ConfigSet.ConfigSet, self).__getattr__(name, default)
        else:
            return self[name]

    ConfigSet.ConfigSet.__getattr__ = _getattr


def options(opt):
    enhance_lib()
