#!/usr/bin/env python3
# Copyright (c) 2007-2019 Arista Networks, Inc.  All rights reserved.
# Arista Networks, Inc. Confidential and Proprietary.

import sys
import Pci
import ScdUtil
import argparse

pciAddressHelp = "[[DDDD:]BB:]SS[.F] D=Domain, B=BusNumber, S=Slot, F=Function"
addrHelp = "a register address within the SCD." \
   " Can be decimal, binary (0b...) or hex (0x...)"
dataHelp = "a 32bit value. Can be decimal, binary or hex"
scdIdHelp = "integer valued scd id."
moduleTypeHelp = "integer valued scd module type."
exampleList = \
"""  e.g., scd list
      list the details of scds in the system
  e.g., scd 0000:bb:00.0 list
      list the details of the scd with specific pci address
"""
exampleWrite = \
"""  e.g., scd write 0x5000 0b101101
      write scd address 5000 with 101 101 (turning on two leds).
"""
exampleRead = \
"""  e.g. scd read 0x5000
      print contents of scd address 5000 to console
"""

def formatPciAddr( pciAddress ):
   if pciAddress is None:
      return None
   return str( Pci.Address( pciAddress ) )

def parseArgs( args ):
   """Helper for parsing command line arguments"""
   # Preparser parses optional scdId and moduleType args
   # so the main parser only needs to worry about pciAddress
   parser = argparse.ArgumentParser( add_help=False )
   parser.add_argument( '-i', type=lambda x: None if x is None else int(x) )
   parser.add_argument( '-t', type=lambda x: None if x is None else int(x) )
   namespace, args = parser.parse_known_args( args )
   #add placeholder pciaddress if one wasn't passed
   if args[ 0 ] in [ "list", "write", "read" ]:
      args.insert( 0, None )

   # Start main parser
   parser = argparse.ArgumentParser( prog='scd',
      formatter_class=argparse.RawDescriptionHelpFormatter,
      epilog="Note: scdId or moduleType cannot be passed in with pciAddress." )

   parser.add_argument( 'pciAddress' , metavar='<pciaddress>', 
         nargs='?', type=formatPciAddr, help=pciAddressHelp)
   # Add these optional args again, even though they are already parsed by
   # the preparser, for helptext.
   parser.add_argument( '-i', metavar='<scdId>', help=scdIdHelp )
   parser.add_argument( '-t', metavar='<moduleType>', help=moduleTypeHelp )

   subparsers = parser.add_subparsers( dest="method", 
         help="scd subcommands.\n'scd <subcommand> -h' for details.")
   
   listHelp = "list information on scds in system"
   list_parser = subparsers.add_parser( 'list', epilog=exampleList, 
         formatter_class=argparse.RawDescriptionHelpFormatter,
         help=listHelp, description=listHelp)
   list_parser.set_defaults( run=ScdUtil.scdList )
   
   readHelp = "read a scd register"
   read_parser = subparsers.add_parser( 'read', epilog=exampleRead, 
         formatter_class=argparse.RawDescriptionHelpFormatter,
         help=readHelp, description=readHelp)
   read_parser.add_argument( 'address', metavar='<addr>', 
         type=lambda x: int( x, 0 ), help=addrHelp ) 
   read_parser.set_defaults( data=None )
   read_parser.set_defaults( run=ScdUtil.scdIO )

   writeHelp = "write a value to a scd register"
   write_parser = subparsers.add_parser( 'write', epilog = exampleWrite, 
         formatter_class=argparse.RawDescriptionHelpFormatter,
         help=writeHelp, description=writeHelp)
   write_parser.add_argument( 'address', metavar='<addr>', 
         type=lambda x: int( x, 0 ), help=addrHelp )
   write_parser.add_argument( 'data', metavar='<data>',
         type=lambda x: int( x, 0 ), help=dataHelp, default=None )
   write_parser.set_defaults( run=ScdUtil.scdIO )
   parsed = parser.parse_args(args, namespace=namespace )
   if parsed.pciAddress and ( parsed.i is not None or parsed.t is not None):
      parser.error( "scdId or moduleType cannot be passed in with pciAddress." )
   return parsed


def main( args ):
   """Parse arguments and call appropriate function"""
   #remove first argument 'scd' before parsing
   parsed = parseArgs( args [ 1: ] )
   if parsed.run == ScdUtil.scdIO: # pylint: disable=comparison-with-callable
      ScdUtil.scdIO(parsed.method, parsed.pciAddress, parsed.t,
               parsed.i, parsed.address, parsed.data)
   elif parsed.run == ScdUtil.scdList: # pylint: disable=comparison-with-callable
      ScdUtil.scdList(parsed.pciAddress, parsed.t, parsed.i)
   else:
      pass

if __name__ == "__main__":
   main( sys.argv )
