import itertools


'''
options list is ordered by channel (0-9).
v2 used John G's chart as a reference.
v3 incorporates feedback from Ken J to add switch status and limit counter func.
v4 incorporates mV input channels from draft of RIO data sheet, and combines the
Form C options into one since the behavior is based on wiring, not
configuration.
v5 adds the form relay options back in based on input from Ken: We are
calculating how many I/O configurations we can support in the field, not just
how many ways you can configure RIO.
v6 Removes counter which is a feature, not a signal type, adds other TC types
so all signals are represented, and updates labels based on latest groov Manage
build
v7 generate output for inspection; readability improvements
v8 combined all TC types back into one option to keep numbers conservative
v9 expanded ICTD support to first 8 channels due to design improvements
v10 added logic to exclude prohibited configurations
v11 refactoring for readability and consistency
v12 more accurate filtering logic
v13 added thermistor option
v14 added resistor option

A = AI 0-10 V
B = AI 0-20 mA
C = AI ICTD
D = AI TC 
E = DI (No feature)
F = DI (Counter) [deprecated]
G = AO 0-20 mA
H = AO 0-10 V
I = DO
J = Form C relay NO
K = Form C relay NC
L = DI Switch Status (No feature)
M = AI +/- 300 mV
N = AI TC E [deprecated]
O = AI TC J [deprecated]
P = AI TC K [deprecated]
Q = AI TC N [deprecated]
R = AI TC R [deprecated]
S = AI TC S [deprecated]
T = AI TC T [deprecated]
U = AI Thermistor
V = AI 0-400 K Ohms
'''

def is_excluded(config):
    '''checks an I/O `config` against rules for excluded combinations.
    returns True if excluded. only one rule, currently.'''
    return config[:4].count('D') > 0 and config[:4].count('I') > 0

def all_cfgs():
    '''calculate all possible I/O configurations, filtering out excluded
    combinations, and returning an iterator over the entire list as strings.
    `options` list is ordered by channel.'''
    options = ['ABCDEILMUV',
               'ABCDEILMUV',
               'ABCDEILMUV',
               'ABCDEILMUV',
               'ACEGHILUV',
               'ACEGHILUV',
               'ACEGHILUV',
               'ACEGHILUV',
               'JK',
               'JK']
    return map(''.join,
               itertools.filterfalse(is_excluded, 
                   itertools.product(*options)))

def unique_cfgs():
    '''calculate only unique I/O configurations by creating a sorted
    representation of each, then filtering out duplicates using set
    exclusivity. returns an unsorted set of sorted strings.'''
    unique = set()
    for each_cfg in all_cfgs():
        unique.add(str.join('', sorted(each_cfg)))
    return unique


print("Calculating all I/O configurations...")
count_all = 0
for each_cfg in all_cfgs():
    count_all = count_all + 1
print(count_all)

print("Calculating unique I/O configurations...")
unique = unique_cfgs()
print(len(unique))

print("Writing unique configurations to file...")
filename = 'rio_configs.csv'
with open(filename, 'w') as f:
    for each_cfg in sorted(unique):
        f.write(each_cfg + ',\n')

print("\nDone! Configurations stored at " + filename)
input("\nPress ENTER key to exit.")
