#
# Copyright (C) 2017,  Netronome Systems, Inc.  All rights reserved.
#

import os, sys, struct, pprint, threading
from urlparse import urlparse
from contextlib import contextmanager

class RTEError(Exception): pass
class RTECommError(RTEError): pass
class RTERPCNotImplemented(RTEError): pass
class RTEReturnError(RTEError): pass

RTE_RETURN_CODES = [
    'SUCCESS',
    'ERROR',          # error reason string provided
    'ARGINVALID',     # invalid argument
    'IOERROR',        # platform IO error
    'MEMORYERROR',    # memory allocation error
    'FILEIOERROR',    # file IO error
    'NOTLOADED',      # firmware not loaded
    'HWINCOMPATIBLE', # platform doesn't support operation
]
    
# XXX a hack for now, but these should be shared definitions for thrift/grpc
class LogLevel(object):
    UNKNOWN = 0
    DISABLE = 1
    FATAL = 2
    ERROR = 3
    WARN = 4
    INFO = 5
    DEBUG = 6
    EXTRA = 7
    HEAVY = 8

class P4CounterType(object):
    Global = 'P4CounterType.Global'
    Direct = 'P4CounterType.Direct'
    Static = 'P4CounterType.Static'

class P4CounterClass(object):
    Invalid     = 'P4CounterClass.Invalid'
    Unspecified = 'P4CounterClass.Unspecified'
    Packets     = 'P4CounterClass.Packets'
    Bytes       = 'P4CounterClass.Bytes'
    Both        = 'P4CounterClass.Both'

class RegisterType(object):
    Global = 'RegisterType.Global'
    Direct = 'RegisterType.Direct'
    Static = 'RegisterType.Static'

class MeterType(object):
    Invalid = 'MeterType.Invalid'
    Global  = 'MeterType.Global'
    Direct  = 'MeterType.Direct'
    Static  = 'MeterType.Static'

class MeterClass(object):
    Invalid     = 'MeterClass.Invalid'
    Unspecified = 'MeterClass.Unspecified'
    Packets     = 'MeterClass.Packets'
    Bytes       = 'MeterClass.Bytes'

class MatchType(object):
    Unspecified = 'MatchType.Unspecified'
    Valid       = 'MatchType.Valid'
    Exact       = 'MatchType.Exact'
    LPM         = 'MatchType.LPM'
    Ternary     = 'MatchType.Ternary'
    Range       = 'MatchType.Range'


#class RegisterFieldDesc(object):
#class RegisterType(object):
    
def REV_MAP(m):
    res = {}
    for k, v in m.items():
        res['v'] = k
    return res

class NullCtx():
    def __enter__(*args): pass
    def __exit__(*exc_info): pass    

class RTEModule(object):
    def __init__(self, rte):
        self.rte = rte

class DesignBase(RTEModule):
    def Load(self, elf_fw, pif_design, pif_config):
        raise RTERPCNotImplemented

    def Unload(self):
        raise RTERPCNotImplemented

    def ConfigReload(self, pif_config):
        raise RTERPCNotImplemented

    def LoadStatus(self):
        raise RTERPCNotImplemented
        
class CountersBase(RTEModule):
    def ResolveToCounterId(self, counter):
        if isinstance(counter, (int, long)):
            counter_id = counter
        elif isinstance(counter, basestring):
            counter_id = self.GetP4CounterByName(counter)['id']
        elif isinstance(counter, dict):
            counter_id = counter['id']
        else:
            raise RTEError, "Unexpected counter parameter type %s"%type(counter)

    def GetP4Counters(self):
        return [(counter, self.GetP4Counter(counter)) for counter in self.ListP4Counters()]

    def GetP4CounterByName(self, counter_name):
        for counter in self.ListP4Counters():
            if counter['name'] == counter_name:
                return counter
        raise RTEError, "Counter '%s' not found"%counter_name

    def ExtractRteValue(self, rv):
        raise RTERPCNotImplemented

    def ListP4Counters(self):
        raise RTERPCNotImplemented

    def GetP4Counter(self, counter):
        raise RTERPCNotImplemented

    def ClearP4Counter(self, counter):
        raise RTERPCNotImplemented

    def ClearAllP4Counters(self):
        raise RTERPCNotImplemented
        
    def GetSystemCounters(self):
        raise RTERPCNotImplemented

    def ClearAllSysCounters(self):
        raise RTERPCNotImplemented

class TablesBase(RTEModule):
    def GetTableByName(self, table_name):
        for table in self.List():
            if table['tbl_name'] == table_name:
                return table
        raise RTEError, "Table '%s' not found"%table_name

    def ResolveToTableId(self, tbl_id):
        if isinstance(tbl_id, int):
            return tbl_id
        elif isinstance(tbl_id, basestring):
            return self.GetTableByName(tbl_id)['tbl_id']
        else:
            raise RTEError, 'Unsupported table name type: %s'%type(tbl_id)

    def AddRule(self, tbl_id, rule_name, default_rule, match, actions, priority = None, timeout = None):
        raise RTERPCNotImplemented

    def EditRule(self, tbl_id, rule_name, default_rule, match, actions, priority = None, timeout = None):
        raise RTERPCNotImplemented

    def DeleteRule(self, tbl_id, rule_name, default_rule, match, actions):
        raise RTERPCNotImplemented

    def List(self):
        raise RTERPCNotImplemented
    
    def ListRules(self, tbl_id):
        raise RTERPCNotImplemented

    def GetVersion(self):
        raise RTERPCNotImplemented

class RegistersBase(RTEModule):
    def GetRegisterByName(self, register_name):
        for reg in self.List():
            if reg['name'] == register_name:
                return reg
        raise RTEError, "Register '%s' not found"%register_name

    def List(self):
        raise RTERPCNotImplemented
    
    def ResolveToRegisterArrayArg(self, register, index, count):
        raise RTERPCNotImplemented
            
    def Get(self, register, index=0, count=1):
        raise RTERPCNotImplemented
            
    def Clear(self, register, index=0, count=1):
        raise RTERPCNotImplemented

    def Set(self, register, values, index=0, count=1):
        raise RTERPCNotImplemented

    def SetField(self, register, field_id, value, index=0, count=1):
        raise RTERPCNotImplemented

class TrafficClassBase(RTEModule):
    def Get(self, port_id):
        raise RTERPCNotImplemented
            
    def Set(self, port_id, cfgs):
        raise RTERPCNotImplemented

    def Commit(self, port_id):
        raise RTERPCNotImplemented

class MetersBase(RTEModule):
    def List(self):
        raise RTERPCNotImplemented
            
    def GetConfig(self, meter_id):
        raise RTERPCNotImplemented

    def SetConfig(self, meter_id, configs):
        raise RTERPCNotImplemented

class DigestsBase(RTEModule):
    def List(self):
        raise RTERPCNotImplemented
    
    def Register(self, digest_id):
        raise RTERPCNotImplemented

    def Deregister(self, digest_regid):
        raise RTERPCNotImplemented
    
    def Get(self, digest_handle):
        raise RTERPCNotImplemented

class MulticastBase(RTEModule):
    def List(self):
        raise RTERPCNotImplemented
    
    def SetConfig(self, group_id, ports):
        raise RTERPCNotImplemented

class SystemBase(RTEModule):
    def Shutdown(self):
        raise RTERPCNotImplemented

    def Ping(self):
        raise RTERPCNotImplemented
    
    def Echo(self, echo_msg):
        raise RTERPCNotImplemented

    def GetVersion(self):
        raise RTERPCNotImplemented

    def GetLogLevel(self):
        raise RTERPCNotImplemented

    def SetLogLevel(self, level):
        raise RTERPCNotImplemented

    def GetPortInfo(self):
        raise RTERPCNotImplemented
    

class DebugCtlBase(RTEModule):
    def Execute(self, debug_id, debug_data):
        raise RTERPCNotImplemented
    
    def SetRuleBreakpoint(self, table_name, rule_name, enable):
        self.Execute('netro_rule_bpt', 'table %s rule %s enabled %s'%(table_name, rule_name, int(enable)))
        
    def GetRuleBreakpoint(self, table_name, rule_name):
        res = self.Execute('netro_rule_bpt', 'table %s rule %s'%(table_name, rule_name))
        name, val = res.split(None, 1)
        assert name=='enabled'
        return bool(int(val))

    def SetMacConfig(self, nbi0_config, nbi1_config):
        for (conf_id, conf_json) in (('nbi_mac8_json', nbi0_config), 
                                     ('nbi_mac9_json', nbi1_config)):
            if conf_json:
                with open(conf_json, "rb") as f:
                    self.Execute(conf_id, f.read())

class ParserValueSetsBase(RTEModule):
    def List(self):
        raise RTERPCNotImplemented

    def Clear(self, pvs_id):
        raise RTERPCNotImplemented

    def Add(self, pvs_id, pvs_entries):
        raise RTERPCNotImplemented

    def Retrieve(self, pvs_id):
        raise RTERPCNotImplemented

