# Copyright 2013 Google, Inc. All Rights Reserved.
#
# Google Author(s): Behdad Esfahbod, Roozbeh Pournader

from fontTools.ttLib.tables.DefaultTable import DefaultTable
import logging

log = logging.getLogger("fontTools.merge")


def add_method(*clazzes, **kwargs):
    """Returns a decorator function that adds a new method to one or
    more classes."""
    allowDefault = kwargs.get("allowDefaultTable", False)

    def wrapper(method):
        done = []
        for clazz in clazzes:
            if clazz in done:
                continue  # Support multiple names of a clazz
            done.append(clazz)
            assert allowDefault or clazz != DefaultTable, "Oops, table class not found."
            assert (
                method.__name__ not in clazz.__dict__
            ), "Oops, class '%s' has method '%s'." % (clazz.__name__, method.__name__)
            setattr(clazz, method.__name__, method)
        return None

    return wrapper


def mergeObjects(lst):
    lst = [item for item in lst if item is not NotImplemented]
    if not lst:
        return NotImplemented
    lst = [item for item in lst if item is not None]
    if not lst:
        return None

    clazz = lst[0].__class__
    assert all(type(item) == clazz for item in lst), lst

    logic = clazz.mergeMap
    returnTable = clazz()
    returnDict = {}

    allKeys = set.union(set(), *(vars(table).keys() for table in lst))
    for key in allKeys:
        try:
            mergeLogic = logic[key]
        except KeyError:
            try:
                mergeLogic = logic["*"]
            except KeyError:
                raise Exception(
                    "Don't know how to merge key %s of class %s" % (key, clazz.__name__)
                )
        if mergeLogic is NotImplemented:
            continue
        value = mergeLogic(getattr(table, key, NotImplemented) for table in lst)
        if value is not NotImplemented:
            returnDict[key] = value

    returnTable.__dict__ = returnDict

    return returnTable


@add_method(DefaultTable, allowDefaultTable=True)
def merge(self, m, tables):
    if not hasattr(self, "mergeMap"):
        log.info("Don't know how to merge '%s'.", self.tableTag)
        return NotImplemented

    logic = self.mergeMap

    if isinstance(logic, dict):
        return m.mergeObjects(self, self.mergeMap, tables)
    else:
        return logic(tables)
