#!/usr/bin/python
__all__ = ["calculateDefects"]

attributes = {}
attributes['sf'] = ('prec','flex','resl','team','pmat')
attributes['em'] = ('rely','data','ruse','docu','cplx',
                    'time','stor','pvol','acap','pcap',
                    'pcon','aexp','pexp','ltex','tool',
                    'site','sced')
attributes['dr'] = ('aa','pr','etat')

lookup_table = {}
lookup_table['requirements'] = {}             #XL:0  VL:1  L :2  N :3  H :4  VH:5  XH:6
lookup_table['requirements']['sf'] = {'prec': (None, 1.43, 1.22, 1.00, 0.92, 0.84, 0.70),
                                      'flex': (None, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00),
                                      'resl': (None, 1.32, 1.16, 1.00, 0.94, 0.87, 0.76),
                                      'team': (None, 1.34, 1.17, 1.00, 0.94, 0.87, 0.75),
                                      'pmat': (None, 1.38, 1.19, 1.00, 0.93, 0.85, 0.73)}
lookup_table['requirements']['em'] = {'rely': (None, 1.43, 1.22, 1.00, 0.85, 0.70, None),
                                      'data': (None, None, 0.93, 1.00, 1.04, 1.07, None),
                                      'ruse': (None, None, 0.95, 1.00, 1.02, 1.03, 1.05),
                                      'docu': (None, 1.16, 1.08, 1.00, 0.93, 0.86, None),
                                      'cplx': (None, 0.76, 0.88, 1.00, 1.10, 1.21, 1.32),
                                      'time': (None, None, None, 1.00, 1.03, 1.05, 1.08),
                                      'stor': (None, None, None, 1.00, 1.03, 1.05, 1.08),
                                      'pvol': (None, None, 0.86, 1.00, 1.05, 1.10, 1.16),
                                      'acap': (None, 1.33, 1.17, 1.00, 0.87, 0.75, None),
                                      'pcap': (None, 1.00, 1.00, 1.00, 1.00, 1.00, None),
                                      'pcon': (None, 1.22, 1.11, 1.00, 0.91, 0.82, None),
                                      'aexp': (None, 1.24, 1.12, 1.00, 0.91, 0.81, None),
                                      'pexp': (None, 1.11, 1.05, 1.00, 0.95, 0.90, None),
                                      'ltex': (None, 1.07, 1.04, 1.00, 0.97, 0.93, None),
                                      'tool': (None, 1.09, 1.05, 1.00, 0.96, 0.92, None),
                                      'site': (None, 1.20, 1.10, 1.00, 0.95, 0.89, 0.83),
                                      'sced': (None, 1.18, 1.09, 1.00, 0.92, 0.85, None)}
lookup_table['requirements']['dr'] = {'aa'  : (None, 0.00, 0.00, 0.10, 0.27, 0.34, 0.40),
                                      'pr'  : (None, 0.00, 0.25, 0.40, 0.50, 0.58, 0.70),
                                      'etat': (None, 0.00, 0.23, 0.40, 0.50, 0.57, 0.60)}
lookup_table['design'] = {}                   #XL    VL    L     N     H     VH    XH
lookup_table['design']['sf']       = {'prec': (None, 1.34, 1.17, 1.00, 0.94, 0.87, 0.75),
                                      'flex': (None, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00),
                                      'resl': (None, 1.43, 1.22, 1.00, 0.92, 0.84, 0.70),
                                      'team': (None, 1.26, 1.13, 1.00, 0.95, 0.90, 0.80),
                                      'pmat': (None, 1.65, 1.33, 1.00, 0.89, 0.78, 0.61)}
lookup_table['design']['em']       = {'rely': (None, 1.45, 1.23, 1.00, 0.85, 0.69, None),
                                      'data': (None, None, 0.91, 1.00, 1.05, 1.10, None),
                                      'ruse': (None, None, 0.98, 1.00, 1.00, 1.01, 1.02),
                                      'docu': (None, 1.18, 1.09, 1.00, 0.93, 0.85, None),
                                      'cplx': (None, 0.71, 0.86, 1.00, 1.13, 1.27, 1.41),
                                      'time': (None, None, None, 1.00, 1.06, 1.13, 1.20),
                                      'stor': (None, None, None, 1.00, 1.06, 1.12, 1.18),
                                      'pvol': (None, None, 0.83, 1.00, 1.06, 1.13, 1.20),
                                      'acap': (None, 1.20, 1.10, 1.00, 0.91, 0.83, None),
                                      'pcap': (None, 1.17, 1.09, 1.00, 0.93, 0.85, None),
                                      'pcon': (None, 1.25, 1.13, 1.00, 0.90, 0.80, None),
                                      'aexp': (None, 1.22, 1.11, 1.00, 0.91, 0.82, None),
                                      'pexp': (None, 1.17, 1.09, 1.00, 0.93, 0.86, None),
                                      'ltex': (None, 1.13, 1.07, 1.00, 0.91, 0.88, None),
                                      'tool': (None, 1.10, 1.05, 1.00, 0.96, 0.91, None),
                                      'site': (None, 1.20, 1.10, 1.00, 0.95, 0.89, 0.83),
                                      'sced': (None, 1.19, 1.10, 1.00, 0.92, 0.84, None)}
lookup_table['design']['dr']       = {'aa'  : (None, 0.00, 0.00, 0.13, 0.28, 0.44, 0.50),
                                      'pr'  : (None, 0.00, 0.28, 0.40, 0.54, 0.70, 0.78),
                                      'etat': (None, 0.00, 0.23, 0.43, 0.54, 0.65, 0.70)}
lookup_table['code'] = {}                     #XL    VL    L     N     H     VH    XH
lookup_table['code']['sf']         = {'prec': (None, 1.24, 1.12, 1.00, 0.95, 0.90, 0.81),
                                      'flex': (None, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00),
                                      'resl': (None, 1.41, 1.21, 1.00, 0.92, 0.84, 0.71),
                                      'team': (None, 1.18, 1.09, 1.00, 0.96, 0.92, 0.86),
                                      'pmat': (None, 1.58, 1.30, 1.00, 0.90, 0.79, 0.63)}
lookup_table['code']['em']         = {'rely': (None, 1.45, 1.23, 1.00, 0.85, 0.69, None),
                                      'data': (None, None, 0.91, 1.00, 1.05, 1.10, None),
                                      'ruse': (None, None, 0.98, 1.00, 1.00, 1.01, 1.02),
                                      'docu': (None, 1.18, 1.09, 1.00, 0.92, 0.85, None),
                                      'cplx': (None, 0.71, 0.86, 1.00, 1.13, 1.27, 1.41),
                                      'time': (None, None, None, 1.00, 1.06, 1.13, 1.20),
                                      'stor': (None, None, None, 1.00, 1.05, 1.10, 1.15),
                                      'pvol': (None, None, 0.82, 1.00, 1.08, 1.15, 1.22),
                                      'acap': (None, 1.11, 1.05, 1.00, 0.95, 0.90, None),
                                      'pcap': (None, 1.32, 1.16, 1.00, 0.88, 0.76, None),
                                      'pcon': (None, 1.30, 1.15, 1.00, 0.88, 0.77, None),
                                      'aexp': (None, 1.13, 1.07, 1.00, 0.94, 0.88, None),
                                      'pexp': (None, 1.16, 1.08, 1.00, 0.94, 0.86, None),
                                      'ltex': (None, 1.22, 1.11, 1.00, 0.91, 0.82, None),
                                      'tool': (None, 1.25, 1.13, 1.00, 0.90, 0.90, None),
                                      'site': (None, 1.18, 1.09, 1.00, 0.95, 0.90, 0.85),
                                      'sced': (None, 1.19, 1.10, 1.00, 0.92, 0.84, None)}
lookup_table['code']['dr']         = {'aa'  : (None, 0.00, 0.10, 0.20, 0.30, 0.48, 0.55),
                                      'pr'  : (None, 0.00, 0.30, 0.48, 0.60, 0.73, 0.83),
                                      'etat': (None, 0.00, 0.38, 0.58, 0.69, 0.78, 0.88)}
defects_introduced_stage_weight = {'requirements':10.0, 'design':20.0, 'code':30.0}

stages = ('requirements', 'design', 'code')

def calculateDefectsIntroduced(stage, attribute_values):
    '''calculate defects introduced for a specifed stage based on attribute_values'''
    return defects_introduced_stage_weight[stage]\
        * attribute_values['sloc']/1000.0\
        * reduce(lambda x, y: x*y, [lookup_table[stage]['sf'][attr][attribute_values[attr]] for attr in attributes['sf']])\
        * reduce(lambda x, y: x*y, [lookup_table[stage]['em'][attr][attribute_values[attr]] for attr in attributes['em']])


def calculateDefectsRemovedPercentage(stage,attribute_values):
    '''calculate defects removed percentage for a specifed stage based on attribute_values'''
    return reduce(lambda x, y: x*y, [1-lookup_table[stage]['dr'][attr][attribute_values[attr]] for attr in attributes['dr']])

def calculateDefectsNoConvert(attribute_values):
    '''calculates defects withon convert attribute_values'''
    return reduce(lambda x, y: x+y,
                    [calculateDefectsIntroduced(s,attribute_values)\
                    *calculateDefectsRemovedPercentage(s,attribute_values)\
                     for s in stages])


def convertAttributeValues (cocomo_values):
    '''Convert attributes values dictionary to expected attributes and values'''
    converted_cocomo_values = cocomo_values.copy()
    converted_cocomo_values['sloc'] = cocomo_values['kloc']*1000
    return converted_cocomo_values

def calculateDefects(attribute_values):
    '''Calculate defects based on attribute values after converting them'''
    return {
            'defects': calculateDefectsNoConvert(convertAttributeValues(attribute_values))
            }
