import re


def apply_substitutions(context, text_in):
    """
    Apply substitutions from the supplied `context` to the text supplied as `text_in`,
    returning the resulting string.  This has been introduced to make it easier to 
    create meaningful links in help pages, with values HOST, SITE, COLL and BASE
    added to the context to make references to the current host, site, collection
    and entity base URI respectively.

    Substitutions are made for the following patterns found in `text_in`

    `$name` followed by a non-alphanumeric, non-':' character
    `$name:`
    `$[curie]`
    `$$`

    In the first two, `name` consists of a sequence of alphabetic, numeric or '_' 
    characters, and the pattern is replaced by the corresponding value from the context.

    In the this pattern, `curie` may contain several additional characters that may occur 
    in a compact URI (CURIE); the pattern is replaced by the corresponding context value.

    The final pattern is an escape sequence for inserting a single '$' into the output 
    which might otherwise be treated as a context value substitution.

    If no corresponding context value is found for a substitution pattern, the pattern is
    copied as-is to the output.

    Any other occurrence of '$' (i.e. not part of any pattern above) is untouched.

    >>> context = { 'aa': '-aa-', 'bb': '-bb-', 'c:c': '-cc-'}
    >>> apply_substitutions(context, "foo bar") == 'foo bar'
    True
    >>> apply_substitutions(context, "foo $aa bar") == 'foo -aa- bar'
    True
    >>> apply_substitutions(context, "foo $bb:bar") == 'foo -bb-bar'
    True
    >>> apply_substitutions(context, "foo $[c:c] bar") == 'foo -cc- bar'
    True
    >>> apply_substitutions(context, "foo $$ bar") == 'foo $ bar'
    True
    >>> apply_substitutions(context, "foo $dd bar") == 'foo $dd bar'
    True
    >>> apply_substitutions(context, "foo $ee bar") == 'foo $ee bar'
    True
    >>> apply_substitutions(context, "foo $[f:f] bar") == 'foo $[f:f] bar'
    True
    >>> apply_substitutions(context, "foo $aa $bb: $[c:c] $[f:f] bar") == 'foo -aa- -bb- -cc- $[f:f] bar'
    True
    """
    def sub_fn(matchobj):
        matched = matchobj.group(1) or matchobj.group(2)
        if matchobj.group(3):
            return "$"
        elif matched in context:
            return context[matched]
        return matchobj.group(0)
    namechars  = "_A-Za-z0-9"
    curiechars = "-@.~+*=:;,/?#!"+namechars
    #                          1----)       2----)     3--)
    sub_re     = re.compile("\$([%s]+):?|\$\[([%s]+)\]|\$(\$)"%(namechars, curiechars))
    text_out   = sub_re.sub(sub_fn, text_in)
    return text_out