/*------------------------------------------------------------------------------------*/

/**
 * creates branch substitution attributes and metrics
 * @name selection.substitution_mapper
 * @param {Matrix} matrix
 * @param {AssociativeList} tree
 * @param {AssociativeList} ambig_lookup
 * @param {AssociativeList} pairwise_counts
 * @param {AssociativeList} genetic_code
 * @returns a dictionary with the following entries
 *    "codon" : {
 *        "each branch name" : {
 *            "codon" :          { an array of strings; codon per site }
 *         }
 *     },
 *    "amino-acid" : {
 *        "each branch name" : {
 *            { an array of strings; amino-acid per site }
 *         }
 *     },
 *    "synonymous substitution count" : {
 *        "each branch name" : {
 *            { an array of floats: for each site, # of synonymous substitutions at this branch/site; by convention set to 0 at the root node }
 *         }
 *     },
 *    "non-synonymous substitution count" : {
 *        "each branch name" : {
 *            { an array of floats: for each site, # of non-synonymous substitutions at this branch/site; by convention set to 0 at the root node }
 *         }
 *     }
 *
 */
lfunction selection.substitution_mapper (matrix, tree, ambig_lookup, pairwise_counts, code_to_codon, genetic_code) {

    site_count   = Columns (matrix);
    branch_count = Rows (matrix);
    aa_mapping   = genetic_code.DefineCodonToAAMapping (genetic_code);
    integer_mapping = genetic_code.DefineIntegerToAAMapping (genetic_code, TRUE);

    result      = {utility.getGlobalValue("terms.codon") : {},
                   utility.getGlobalValue("terms.amino_acid") : {},
                   utility.getGlobalValue("terms.synonymous_sub_count") : {},
                   utility.getGlobalValue("terms.nonsynonymous_sub_count") : {}};

    code_lookup = {"-1" : "-"};

    for (b = 0; b < branch_count; b += 1) {

        bname  = (tree[b+1])["Name"];
        parent = (tree[b+1])["Parent"] - 1;

        branch_info = {utility.getGlobalValue("terms.codon") : {1, site_count},
                       utility.getGlobalValue("terms.amino_acid") : {1, site_count},
                       utility.getGlobalValue("terms.synonymous_sub_count") : {1, site_count},
                       utility.getGlobalValue("terms.nonsynonymous_sub_count") : {1, site_count}};


        for (s = 0; s < site_count; s += 1) {
            code        = matrix[b][s];
            parent_code = matrix[parent][s];

            (branch_info[utility.getGlobalValue("terms.codon")])[s] = code_to_codon[code];

            if (Type(code_lookup [code]) != "String") {
                if (code >= 0) {
                    code_lookup [code] = aa_mapping [code_to_codon[code]];

                } else {
                    collect_residues = {};
                    utility.ForEach ( ambig_lookup[-code-2], "_for_each_", "`&collect_residues`[`&integer_mapping`[_for_each_]] = 1");
                    code_lookup [code] = Join ("", utility.Keys (collect_residues));
                }
            }

            if (code >= 0) {
                (branch_info[utility.getGlobalValue("terms.synonymous_sub_count")]) [s]     = (pairwise_counts[utility.getGlobalValue("terms.genetic_code.OPS")])[parent_code][code];
                (branch_info[utility.getGlobalValue("terms.nonsynonymous_sub_count")]) [s] = (pairwise_counts[utility.getGlobalValue("terms.genetic_code.OPN")])[parent_code][code];
            } else {
                if (code != -1) {
                    resolution = (ambig_lookup[-code-2])$(ambig_lookup[-code-2])["_MATRIX_ELEMENT_ROW_"];
                    resolution = resolution[resolution];
                    // TODO: Appropriate syntax for following lines OPS, OPN
                    (branch_info[utility.getGlobalValue("terms.synonymous_sub_count")]) [s] = + utility.Map (resolution, "_mapper_", "(`&pairwise_counts`[\"OPS\"])[`&parent_code`][_mapper_]");
                    (branch_info[utility.getGlobalValue("terms.nonsynonymous_sub_count")]) [s] = + utility.Map (resolution, "_mapper_", "(`&pairwise_counts`[\"OPN\"])[`&parent_code`][_mapper_]");
                }

            }

            (branch_info[utility.getGlobalValue("terms.amino_acid")])[s] = code_lookup[code];
        }

        utility.ForEach (utility.Keys (branch_info), "slac.substituton_mapper.key",
                         "(`&result`[slac.substituton_mapper.key])[`&bname`] = `&branch_info`[slac.substituton_mapper.key]");

     }

    return result;

}
