from pyroute2.netlink import nla
from pyroute2.netlink.rtnl import TC_H_ROOT
from pyroute2.netlink.rtnl.tcmsg.common import time2tick
from pyroute2.netlink.rtnl.tcmsg.common import percent2u32

parent = TC_H_ROOT


def get_parameters(kwarg):
    delay = time2tick(kwarg.get('delay', 0))  # in microsecond
    limit = kwarg.get('limit', 1000)  # fifo limit (packets) see netem.c:230
    loss = percent2u32(kwarg.get('loss', 0))  # int percentage
    gap = kwarg.get('gap', 0)
    duplicate = kwarg.get('duplicate', 0)
    jitter = time2tick(kwarg.get('jitter', 0))  # in microsecond

    opts = {
        'delay': delay,
        'limit': limit,
        'loss': loss,
        'gap': gap,
        'duplicate': duplicate,
        'jitter': jitter,
        'attrs': []
    }

    # correlation (delay, loss, duplicate)
    delay_corr = percent2u32(kwarg.get('delay_corr', 0))
    loss_corr = percent2u32(kwarg.get('loss_corr', 0))
    dup_corr = percent2u32(kwarg.get('dup_corr', 0))
    if delay_corr or loss_corr or dup_corr:
        # delay_corr requires that both jitter and delay are != 0
        if delay_corr and not (delay and jitter):
            raise Exception('delay correlation requires delay'
                            ' and jitter to be set')
        # loss correlation and loss
        if loss_corr and not loss:
            raise Exception('loss correlation requires loss to be set')
        # duplicate correlation and duplicate
        if dup_corr and not duplicate:
            raise Exception('duplicate correlation requires '
                            'duplicate to be set')

        opts['attrs'].append(['TCA_NETEM_CORR', {'delay_corr': delay_corr,
                                                 'loss_corr': loss_corr,
                                                 'dup_corr': dup_corr}])

    # reorder (probability, correlation)
    prob_reorder = percent2u32(kwarg.get('prob_reorder', 0))
    corr_reorder = percent2u32(kwarg.get('corr_reorder', 0))
    if prob_reorder != 0:
        # gap defaults to 1 if equal to 0
        if gap == 0:
            opts['gap'] = gap = 1
        opts['attrs'].append(['TCA_NETEM_REORDER',
                             {'prob_reorder': prob_reorder,
                              'corr_reorder': corr_reorder}])
    else:
        if gap != 0:
            raise Exception('gap can only be set when prob_reorder is set')
        elif corr_reorder != 0:
            raise Exception('corr_reorder can only be set when '
                            'prob_reorder is set')

    # corrupt (probability, correlation)
    prob_corrupt = percent2u32(kwarg.get('prob_corrupt', 0))
    corr_corrupt = percent2u32(kwarg.get('corr_corrupt', 0))
    if prob_corrupt:
        opts['attrs'].append(['TCA_NETEM_CORRUPT',
                             {'prob_corrupt': prob_corrupt,
                              'corr_corrupt': corr_corrupt}])
    elif corr_corrupt != 0:
        raise Exception('corr_corrupt can only be set when '
                        'prob_corrupt is set')

    # TODO
    # delay distribution (dist_size, dist_data)
    return opts


class options(nla):
    nla_map = (('TCA_NETEM_UNSPEC', 'none'),
               ('TCA_NETEM_CORR', 'netem_corr'),
               ('TCA_NETEM_DELAY_DIST', 'none'),
               ('TCA_NETEM_REORDER', 'netem_reorder'),
               ('TCA_NETEM_CORRUPT', 'netem_corrupt'),
               ('TCA_NETEM_LOSS', 'none'),
               ('TCA_NETEM_RATE', 'none'))

    fields = (('delay', 'I'),
              ('limit', 'I'),
              ('loss', 'I'),
              ('gap', 'I'),
              ('duplicate', 'I'),
              ('jitter', 'I'))

    class netem_corr(nla):
        '''correlation'''
        fields = (('delay_corr', 'I'),
                  ('loss_corr', 'I'),
                  ('dup_corr', 'I'))

    class netem_reorder(nla):
        '''reorder has probability and correlation'''
        fields = (('prob_reorder', 'I'),
                  ('corr_reorder', 'I'))

    class netem_corrupt(nla):
        '''corruption has probability and correlation'''
        fields = (('prob_corrupt', 'I'),
                  ('corr_corrupt', 'I'))
