# Source code for fontTools.ufoLib.converters

    """
    Conversion functions.
    """

    # adapted from the UFO spec

    def convertUFO1OrUFO2KerningToUFO3Kerning(kerning, groups, glyphSet=()):
        # gather known kerning groups based on the prefixes
        firstReferencedGroups, secondReferencedGroups = findKnownKerningGroups(groups)
        # Make lists of groups referenced in kerning pairs.
        for first, seconds in list(kerning.items()):
            if first in groups and first not in glyphSet:
                if not first.startswith("public.kern1."):
                    firstReferencedGroups.add(first)
            for second in list(seconds.keys()):
                if second in groups and second not in glyphSet:
                    if not second.startswith("public.kern2."):
                        secondReferencedGroups.add(second)
        # Create new names for these groups.
        firstRenamedGroups = {}
        for first in firstReferencedGroups:
            # Make a list of existing group names.
            existingGroupNames = list(groups.keys()) + list(firstRenamedGroups.keys())
            # Remove the old prefix from the name
            newName = first.replace("@MMK_L_", "")
            # Add the new prefix to the name.
            newName = "public.kern1." + newName
            # Make a unique group name.
            newName = makeUniqueGroupName(newName, existingGroupNames)
            # Store for use later.
            firstRenamedGroups[first] = newName
        secondRenamedGroups = {}
        for second in secondReferencedGroups:
            # Make a list of existing group names.
            existingGroupNames = list(groups.keys()) + list(secondRenamedGroups.keys())
            # Remove the old prefix from the name
            newName = second.replace("@MMK_R_", "")
            # Add the new prefix to the name.
            newName = "public.kern2." + newName
            # Make a unique group name.
            newName = makeUniqueGroupName(newName, existingGroupNames)
            # Store for use later.
            secondRenamedGroups[second] = newName
        # Populate the new group names into the kerning dictionary as needed.
        newKerning = {}
        for first, seconds in list(kerning.items()):
            first = firstRenamedGroups.get(first, first)
            newSeconds = {}
            for second, value in list(seconds.items()):
                second = secondRenamedGroups.get(second, second)
                newSeconds[second] = value
            newKerning[first] = newSeconds
        # Make copies of the referenced groups and store them
        # under the new names in the overall groups dictionary.
        allRenamedGroups = list(firstRenamedGroups.items())
        allRenamedGroups += list(secondRenamedGroups.items())
        for oldName, newName in allRenamedGroups:
            group = list(groups[oldName])
            groups[newName] = group
        # Return the kerning and the groups.
        return newKerning, groups, dict(side1=firstRenamedGroups, side2=secondRenamedGroups)

    def findKnownKerningGroups(groups):
        """
        This will find kerning groups with known prefixes.
        In some cases not all kerning groups will be referenced
        by the kerning pairs. The algorithm for locating groups
        in convertUFO1OrUFO2KerningToUFO3Kerning will miss these
        unreferenced groups. By scanning for known prefixes
        this function will catch all of the prefixed groups.

        These are the prefixes and sides that are handled:
        @MMK_L_ - side 1
        @MMK_R_ - side 2

        >>> testGroups = {
        ...     "@MMK_L_1" : None,
        ...     "@MMK_L_2" : None,
        ...     "@MMK_L_3" : None,
        ...     "@MMK_R_1" : None,
        ...     "@MMK_R_2" : None,
        ...     "@MMK_R_3" : None,
        ...     "@MMK_l_1" : None,
        ...     "@MMK_r_1" : None,
        ...     "@MMK_X_1" : None,
        ...     "foo" : None,
        ... }
        >>> first, second = findKnownKerningGroups(testGroups)
        >>> sorted(first) == ['@MMK_L_1', '@MMK_L_2', '@MMK_L_3']
        True
        >>> sorted(second) == ['@MMK_R_1', '@MMK_R_2', '@MMK_R_3']
        True
        """
        knownFirstGroupPrefixes = [
            "@MMK_L_"
        ]
        knownSecondGroupPrefixes = [
            "@MMK_R_"
        ]
        firstGroups = set()
        secondGroups = set()
        for groupName in list(groups.keys()):
            for firstPrefix in knownFirstGroupPrefixes:
                if groupName.startswith(firstPrefix):
                    firstGroups.add(groupName)
                    break
            for secondPrefix in knownSecondGroupPrefixes:
                if groupName.startswith(secondPrefix):
                    secondGroups.add(groupName)
                    break
        return firstGroups, secondGroups

    def makeUniqueGroupName(name, groupNames, counter=0):
        # Add a number to the name if the counter is higher than zero.
        newName = name
        if counter > 0:
            newName = "%s%d" % (newName, counter)
        # If the new name is in the existing group names, recurse.
        if newName in groupNames:
            return makeUniqueGroupName(name, groupNames, counter + 1)
        # Otherwise send back the new name.
        return newName

    def test():
        """
        No known prefixes.

        >>> testKerning = {
        ...     "A" : {
        ...         "A" : 1,
        ...         "B" : 2,
        ...         "CGroup" : 3,
        ...         "DGroup" : 4
        ...     },
        ...     "BGroup" : {
        ...         "A" : 5,
        ...         "B" : 6,
        ...         "CGroup" : 7,
        ...         "DGroup" : 8
        ...     },
        ...     "CGroup" : {
        ...         "A" : 9,
        ...         "B" : 10,
        ...         "CGroup" : 11,
        ...         "DGroup" : 12
        ...     },
        ... }
        >>> testGroups = {
        ...     "BGroup" : ["B"],
        ...     "CGroup" : ["C"],
        ...     "DGroup" : ["D"],
        ... }
        >>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning(
        ...     testKerning, testGroups, [])
        >>> expected = {
        ...     "A" : {
        ...         "A": 1,
        ...         "B": 2,
        ...         "public.kern2.CGroup": 3,
        ...         "public.kern2.DGroup": 4
        ...     },
        ...     "public.kern1.BGroup": {
        ...         "A": 5,
        ...         "B": 6,
        ...         "public.kern2.CGroup": 7,
        ...         "public.kern2.DGroup": 8
        ...     },
        ...     "public.kern1.CGroup": {
        ...         "A": 9,
        ...         "B": 10,
        ...         "public.kern2.CGroup": 11,
        ...         "public.kern2.DGroup": 12
        ...     }
        ... }
        >>> kerning == expected
        True
        >>> expected = {
        ...     "BGroup": ["B"],
        ...     "CGroup": ["C"],
        ...     "DGroup": ["D"],
        ...     "public.kern1.BGroup": ["B"],
        ...     "public.kern1.CGroup": ["C"],
        ...     "public.kern2.CGroup": ["C"],
        ...     "public.kern2.DGroup": ["D"],
        ... }
        >>> groups == expected
        True

        Known prefixes.

        >>> testKerning = {
        ...     "A" : {
        ...         "A" : 1,
        ...         "B" : 2,
        ...         "@MMK_R_CGroup" : 3,
        ...         "@MMK_R_DGroup" : 4
        ...     },
        ...     "@MMK_L_BGroup" : {
        ...         "A" : 5,
        ...         "B" : 6,
        ...         "@MMK_R_CGroup" : 7,
        ...         "@MMK_R_DGroup" : 8
        ...     },
        ...     "@MMK_L_CGroup" : {
        ...         "A" : 9,
        ...         "B" : 10,
        ...         "@MMK_R_CGroup" : 11,
        ...         "@MMK_R_DGroup" : 12
        ...     },
        ... }
        >>> testGroups = {
        ...     "@MMK_L_BGroup" : ["B"],
        ...     "@MMK_L_CGroup" : ["C"],
        ...     "@MMK_L_XGroup" : ["X"],
        ...     "@MMK_R_CGroup" : ["C"],
        ...     "@MMK_R_DGroup" : ["D"],
        ...     "@MMK_R_XGroup" : ["X"],
        ... }
        >>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning(
        ...     testKerning, testGroups, [])
        >>> expected = {
        ...     "A" : {
        ...         "A": 1,
        ...         "B": 2,
        ...         "public.kern2.CGroup": 3,
        ...         "public.kern2.DGroup": 4
        ...     },
        ...     "public.kern1.BGroup": {
        ...         "A": 5,
        ...         "B": 6,
        ...         "public.kern2.CGroup": 7,
        ...         "public.kern2.DGroup": 8
        ...     },
        ...     "public.kern1.CGroup": {
        ...         "A": 9,
        ...         "B": 10,
        ...         "public.kern2.CGroup": 11,
        ...         "public.kern2.DGroup": 12
        ...     }
        ... }
        >>> kerning == expected
        True
        >>> expected = {
        ...     "@MMK_L_BGroup": ["B"],
        ...     "@MMK_L_CGroup": ["C"],
        ...     "@MMK_L_XGroup": ["X"],
        ...     "@MMK_R_CGroup": ["C"],
        ...     "@MMK_R_DGroup": ["D"],
        ...     "@MMK_R_XGroup": ["X"],
        ...     "public.kern1.BGroup": ["B"],
        ...     "public.kern1.CGroup": ["C"],
        ...     "public.kern1.XGroup": ["X"],
        ...     "public.kern2.CGroup": ["C"],
        ...     "public.kern2.DGroup": ["D"],
        ...     "public.kern2.XGroup": ["X"],
        ... }
        >>> groups == expected
        True

        >>> from .validators import kerningValidator
        >>> kerningValidator(kerning)
        (True, None)

        Mixture of known prefixes and groups without prefixes.

        >>> testKerning = {
        ...     "A" : {
        ...         "A" : 1,
        ...         "B" : 2,
        ...         "@MMK_R_CGroup" : 3,
        ...         "DGroup" : 4
        ...     },
        ...     "BGroup" : {
        ...         "A" : 5,
        ...         "B" : 6,
        ...         "@MMK_R_CGroup" : 7,
        ...         "DGroup" : 8
        ...     },
        ...     "@MMK_L_CGroup" : {
        ...         "A" : 9,
        ...         "B" : 10,
        ...         "@MMK_R_CGroup" : 11,
        ...         "DGroup" : 12
        ...     },
        ... }
        >>> testGroups = {
        ...     "BGroup" : ["B"],
        ...     "@MMK_L_CGroup" : ["C"],
        ...     "@MMK_R_CGroup" : ["C"],
        ...     "DGroup" : ["D"],
        ... }
        >>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning(
        ...     testKerning, testGroups, [])
        >>> expected = {
        ...     "A" : {
        ...         "A": 1,
        ...         "B": 2,
        ...         "public.kern2.CGroup": 3,
        ...         "public.kern2.DGroup": 4
        ...     },
        ...     "public.kern1.BGroup": {
        ...         "A": 5,
        ...         "B": 6,
        ...         "public.kern2.CGroup": 7,
        ...         "public.kern2.DGroup": 8
        ...     },
        ...     "public.kern1.CGroup": {
        ...         "A": 9,
        ...         "B": 10,
        ...         "public.kern2.CGroup": 11,
        ...         "public.kern2.DGroup": 12
        ...     }
        ... }
        >>> kerning == expected
        True
        >>> expected = {
        ...     "BGroup": ["B"],
        ...     "@MMK_L_CGroup": ["C"],
        ...     "@MMK_R_CGroup": ["C"],
        ...     "DGroup": ["D"],
        ...     "public.kern1.BGroup": ["B"],
        ...     "public.kern1.CGroup": ["C"],
        ...     "public.kern2.CGroup": ["C"],
        ...     "public.kern2.DGroup": ["D"],
        ... }
        >>> groups == expected
        True
        """

    if __name__ == "__main__":
        import doctest
        doctest.testmod()