import itertools


def whitelist_barcodes(whitelist):
    """
    Calculate allowable barcodes from whitelist.

    Finds all 1-N substitutions that uniquely map to whitelist barcodes.

    Parameters
    ----------
        whitelist : str
            Path to file containing whitelist barcodes
    
    Returns
    -------
    dict
        Dictionary where keys are possible barcodes and values are whitelisted
        barcodes to map to.
    """
    barcodes = {}
    mapping = {}
    conflicting = {}
    # iterate through lines of whitelist barcodes
    with open(whitelist, 'r') as f:
        n_line = 0
        for line in f:
            n_line += 1
            print("{:0.2f}".format((n_line) / 147456), flush=True, end='\r') 
            bc = line.strip()
            # map barcode to barcode
            barcodes[bc] = bc
            # find sequences with a single substitution
            for i, base in itertools.product(range(len(bc)),
                                             ['A', 'T', 'C', 'G', 'N']):
                # check if substitution possible
                if bc[i] != base:
                    sub_bc = bc[:i] + base + bc[i + 1:]
                    if sub_bc not in conflicting and sub_bc not in mapping:
                        mapping[sub_bc] = bc
                    elif sub_bc in mapping:
                        conflicting[sub_bc] = ''
                        mapping.pop(sub_bc)
    # iterate through mapped barcodes, don't keep if substitution matches
    # whitelist barcode
    print('\n')
    print(f"mapping size: {len(mapping)}")
    print(f"conflicting substitutions: {len(conflicting)}")
    for k, v in mapping.items():
        if k not in barcodes:
            barcodes[k] = v
    return barcodes