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

import argparse
import sys

# A `''.join` function.
joinMultiLines = None

def parseArgs():
   parser = argparse.ArgumentParser()
   parser.add_argument( '-d', '--delimiter', type=str, action='store', default=' ',
                        help='A string which to use when joining mutiline input. '
                             'e.g., \' \\n \'. Defaults to a single space.' )
   args = parser.parse_args( sys.argv[ 1: ] )

   global joinMultiLines
   joinMultiLines = args.delimiter.join

def splitIndentFromLine( s ):
   return len( s ) - len( s.lstrip() ), s.strip()

class ListWithClear( list ):
   '''
   Useful and more readable than `l *= 0` or `l[:] = []`.
   '''
   def clear( self ):
      self *= 0

def handleIndentedEof( endIndentSpaces, rawLines, outputBuffer ):
   '''
   Process a configlet like:
   router general
       command that should not be included
       control-functions
           code unit UNIT_A
               function foo() {
                  return true;
               }
           EOF
   The resulting command consists of:
   - the last line indented as much as the "EOF". (code unit ...),
   - all lines indented more than the "EOF".
   '''
   command = [ rawLines.pop().strip() ]
   while rawLines:
      line = rawLines.pop()
      outputBuffer.pop()
      indent, line = splitIndentFromLine( line )
      if indent >= endIndentSpaces:
         # Child line. Add it.
         command.append( line )
         if indent == endIndentSpaces:
            # This was the last line. (code unit ...)
            break
   command.reverse()

   # The lines less indented than the "EOF" should be used as a prefix.
   prefix = []
   for line in rawLines:
      indent, line = splitIndentFromLine( line )
      if indent < endIndentSpaces:
         prefix.append( line )

   outputBuffer.append( joinMultiLines( prefix + command ) )

def handleTopLevelEof( rawLines, outputBuffer ):
   '''
   Process a configlet like:
   banner motd
   line one
       line two
     multi line input
   EOF
   What we do: since the raw lines are stuff since the last "!" or "EOF",
   we just strip-join them.
   '''
   outputBuffer.clear()
   outputBuffer.append( joinMultiLines( l.strip() for l in rawLines ) )

def handleSibling( indent, line, modeCmds ):
   # Handle comments specially. We don't want a comment to become
   # a mode prefix.
   if line and not line.startswith( "!" ):
      modeCmds[ -1 ] = line
      line = " ".join( modeCmds )
   elif indent:
      # Prefix indented comments
      line = " ".join( modeCmds + [ line ] )

   return line

def handleChild( indent, line, indentSpaces, modeCmds ):
   # Handle comments specially. We don't want a comment to become
   # a mode prefix.
   if line:
      if line.startswith( "!" ):
         # Prefix indented comments
         line = " ".join( modeCmds ) + " " + line
      else:
         modeCmds.append( line )
         indentSpaces.append( indent )
         line = " ".join( modeCmds )

   return line

def handleParent( indent, line, indentSpaces, modeCmds ):
   while indentSpaces[ -1 ] > indent:
      indentSpaces.pop()
      modeCmds.pop()
   modeCmds[ -1 ] = line

   return " ".join( modeCmds )

def flushBuffer( outputBuffer, rawLines ):
   if outputBuffer:
      print( "\n".join( outputBuffer ) )
      outputBuffer.clear()
   rawLines.clear()

def main():
   parseArgs()

   rawLines = ListWithClear()
   outputBuffer = ListWithClear()

   try:
      modeCmds = [ "" ]
      indents = [ 0 ]

      for line in sys.stdin:
         rawLines.append( line.rstrip() )
         indent, line = splitIndentFromLine( line )
         lastIndent = indents[ -1 ]

         if line == "EOF":
            if indent:
               handleIndentedEof( indent, rawLines, outputBuffer )
            else:
               handleTopLevelEof( rawLines, outputBuffer )
               flushBuffer( outputBuffer, rawLines )
         elif not indent and line == "!":
            outputBuffer.append( line )
            flushBuffer( outputBuffer, rawLines )
         elif indent == lastIndent:
            if line != "!":
               outputBuffer.append( handleSibling( indent, line, modeCmds ) )
         elif indent > lastIndent:
            outputBuffer.append( handleChild( indent, line, indents, modeCmds ) )
         else: # `indent` is less.
            if line != "!":
               outputBuffer.append( handleParent( indent, line, indents, modeCmds ) )

      flushBuffer( outputBuffer, rawLines )

   except ( KeyboardInterrupt, OSError ):
      sys.exit( 1 )

if __name__ == '__main__':
   main()
