'''
skbedit
+++++++

Usage::

    from pyroute2 import IPRoute

    # Assume you are working with eth1 interface
    IFNAME = "eth1"

    ipr = IPRoute()
    ifindex = ipr.link_lookup(ifname=IFNAME)

    # First create parent qdisc
    ipr.tc("add", "htb", index=ifindex, handle=0x10000)

    # Then add a matchall filter with skbedit action
    # Simple action example
    action = {"kind": "skbedit",
              "priority": 0x10001 # Also known as "1:1" in TC format
              }
    ipr.tc("add-filter", "matchall", index=ifindex, parent=0x10000,
           prio=1, action=action)

    # Extended action example
    action = {"kind": "skbedit",
              "priority": 0x10001, # Also known as "1:1" in TC format
              "mark": 0x1337,
              "mask": 0xFFFFFFFF,
              "ptype": "host"
              }
    ipr.tc("add-filter", "matchall", index=ifindex, parent=0x10000,
           prio=1, action=action)

NOTES:
    Here is the list of all supported options::
    - mark: integer
    - mask: integer
    - priority: integer
    - ptype: "host", "otherhost", "broadcast" or "multicast"
    - queue: integer
'''


from pyroute2.netlink import nla
from pyroute2.netlink.rtnl.tcmsg.common import tc_actions


# Packet types defined in if_packet.h
PACKET_HOST = 0
PACKET_BROADCAST = 1
PACKET_MULTICAST = 2
PACKET_OTHERHOST = 3


def convert_ptype(value):
    types = {'host': PACKET_HOST,
             'otherhost': PACKET_OTHERHOST,
             'broadcast': PACKET_BROADCAST,
             'multicast': PACKET_MULTICAST,
             }

    res = types.get(value.lower())
    if res is not None:
        return res
    raise ValueError('Invalid ptype specified! See tc-skbedit man '
                     'page for valid values.')


def get_parameters(kwarg):
    ret = {'attrs': []}
    attrs_map = (('priority', 'TCA_SKBEDIT_PRIORITY'),
                 ('queue', 'TCA_SKBEDIT_QUEUE_MAPPING'),
                 ('mark', 'TCA_SKBEDIT_MARK'),
                 ('ptype', 'TCA_SKBEDIT_PTYPE'),
                 ('mask', 'TCA_SKBEDIT_MASK'),
                 )

    # Assign TCA_SKBEDIT_PARMS first
    parms = {}
    parms['action'] = tc_actions['pipe']
    ret['attrs'].append(['TCA_SKBEDIT_PARMS', parms])

    for k, v in attrs_map:
        r = kwarg.get(k, None)
        if r is not None:
            if k == 'ptype':
                r = convert_ptype(r)
            ret['attrs'].append([v, r])

    return ret


class options(nla):
    nla_map = (('TCA_SKBEDIT_UNSPEC', 'none'),
               ('TCA_SKBEDIT_TM', 'tca_parse_tm'),
               ('TCA_SKBEDIT_PARMS', 'tca_parse_parms'),
               ('TCA_SKBEDIT_PRIORITY', 'uint32'),
               ('TCA_SKBEDIT_QUEUE_MAPPING', 'uint16'),
               ('TCA_SKBEDIT_MARK', 'uint32'),
               ('TCA_SKBEDIT_PAD', 'hex'),
               ('TCA_SKBEDIT_PTYPE', 'uint16'),
               ('TCA_SKBEDIT_MASK', 'uint32'),
               ('TCA_SKBEDIT_FLAGS', 'uint64'),
               )

    class tca_parse_parms(nla):
        # As described in tc_mpls.h, it uses
        # generic TC action fields
        fields = (('index', 'I'),
                  ('capab', 'I'),
                  ('action', 'i'),
                  ('refcnt', 'i'),
                  ('bindcnt', 'i'),
                  )

    class tca_parse_tm(nla):
        # See struct tcf_t
        fields = (('install', 'Q'),
                  ('lastuse', 'Q'),
                  ('expires', 'Q'),
                  ('firstuse', 'Q'),
                  )
