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

# Generate a prefdl to stdout or a file, taking input from stdin
# Decode a prefdl to stdout, taking input from stdin or a file

from __future__ import absolute_import, division, print_function
import six
from six.moves import input
import sys, argparse
import Prefdl

# pylint: disable=no-member
stdinBuf = sys.stdin if six.PY2 or not sys.stdin else sys.stdin.buffer
stdoutBuf = sys.stdout if six.PY2 else sys.stdout.buffer
stderrBuf = sys.stderr if six.PY2 else sys.stderr.buffer
# pylint: enable=no-member

usage = """prefdl [args] <filename>

  Generate a prefdl to stdout or a file, taking input from stdin

  Prompt the user to enter the prefdl information on stdin and generate
  a prefdl to <filename> in proper compressed format.  If stdin is not a
  tty, then prefdl chooses quiet operation, and does not generate any
  output to stdout (unless - is selected as the outfile)

prefdl [args] --decode <filename>

  Decode a prefdl to stdout, taking input from stdin or a file
  and generating decoded output to stdout.  Exit code is 1 if
  the prefdl appears invalid

Spaces are ignored when inputting data.
Readline-style line editing and history is supported.
Up to 20 deviations are allowed.

If "-" is specified as <filename>, then stdout (or stdin) is used
-q forces quiet operation.


"""

parser = argparse.ArgumentParser(usage=usage)
parser.add_argument( 'filename' )
parser.add_argument( "-q", "--quiet", action="store_true",
                   help="generate no output. (except fdl, possibly)" )
parser.add_argument( "-d", "--decode", action="store_true",
                   help="decode a prefdl, rather than generating one. "
                   "Pass '-' to specify stdin as the source." )
parser.add_argument( "-e", "--encode", action="store_true",
                   help="Encode a prefdl from an input file. "
                   "Pass '-' to specify stdin as the source." )
parser.add_argument( "-o", "--output", dest="output_file", 
                   help="Set an output file. "
                   "Defaults to stdout if not specified." )
parser.add_argument( "-f", "--force", action="store_true",
                   help="Ignore an invalid CRC when decoding." )
parser.add_argument( "--swFeatures", action="store",
                   help="Provide a dict for SW defined features" )
parser.add_argument( "--signatureList", action="store",
                   help="Provide list of prefdl variables in signature" )
parser.add_argument( "--certificate", action="store", help="Provide certificate" )
parser.add_argument( "--signature", action="store", help="Provide signature" )
parser.add_argument( "--ztpToken", action="store",
                     help="Provide JWT token to validate the switch in SecureZTP" )
parser.add_argument( "-u", "--usedefault", action = "store_true",
                    help="Use defaults from a file created by reading an idprom "\
                    "and write to that file.")
parser.add_argument( "-r", "--remove", action="store_true",
                   help="Remove options attribute from prefdl." )
parser.add_argument( "--skip-validation", action="append", dest="skipValidations",
                     metavar="FIELD",
                     help="Provide prefdl field to skip validation of "\
                     "(may be specified multiple times)." )

Prefdl.add_prefdl_args( parser )

args = parser.parse_args()

if args.force and not args.decode:
   parser.error( "--force option can only be used with --decode" )

if args.output_file and ( args.usedefault or args.remove ):
   parser.error( "--output option cannot be used with --usedefault or --remove" )

if args.decode and ( args.pca or args.sn or args.sku or
                        args.kvn or args.deviations ):
   parser.error( "Cannot specify dut descriptions or the command line "
                 "with the --decode option" )

output = args.filename

args.quiet = args.quiet or not sys.stdin or not sys.stdin.isatty()

def askfor( question, validate, what, isOptional ):
   
   if args.quiet:
      while True:
         assert stdinBuf
         line = stdinBuf.readline()[:-1]
         if not line.startswith( b"#" ):
            break
      if isOptional and line == b'':
         return b''
      ans = validate( line )
      if ans is None:
         sys.stderr.write( b"Invalid input for %s: '%s'\n" % (what, ans ) )
         raise ValueError
   else:
      # pylint: disable-next=import-outside-toplevel,unused-import
      import readline # pylint: disable=unused-variable
      while True:
         line = six.ensure_binary( input( question + " " ) )
         if isOptional and  line == b'': 
            return b''
         ans = validate( line )
         if ans is not None: 
            break
         print( "Invalid format, try again" )
   return ans

def exitError( e ):
   sys.stderr.write( "%s\n" % e ) # pylint: disable=consider-using-f-string
   sys.exit( 1 )

def main():
   if args.decode:
      if output == "-":
         fp = stdinBuf
      else:
         fp = open( output, "rb" ) # pylint: disable=consider-using-with
      try:
         stdoutBuf.write( Prefdl.decode( fp, **vars( args ) ) )
      except ( Prefdl.InvalidData, IndexError, RuntimeError ) as e:
         exitError( e )
      if fp is not stdinBuf:
         fp.close()

   elif args.encode:
      if output == "-":
         fpIn = stdinBuf
      else:
         fpIn = open( output, "rb" ) # pylint: disable=consider-using-with
      
      if args.output_file:
         # pylint: disable-next=consider-using-with
         fpOut = open( args.output_file, "wb+" )
      else:
         fpOut = stdoutBuf
      try:
         Prefdl.encode( fpIn, fpOut, args.skipValidations )
      except ValueError as e:
         exitError( e )   
      if fpIn is not stdinBuf:
         fpIn.close()
      if fpOut is not stdoutBuf:
         fpOut.close()
      
   else:
      try:
         askfunc = askfor if sys.stdin.isatty() else None
         if args.prefdlVersion is not None:
            # ensure bytes
            args.prefdlVersion = args.prefdlVersion.encode( 'ascii' )
         Prefdl.generate( output, askfunc, **vars( args ) )
      except ( Prefdl.InvalidData, ValueError ) as e:
         exitError( e )

if __name__ == "__main__":
   main()
