#!/usr/bin/env python
# alignlog - parse portions of alignment log
#
# Author: David Mastronarde
#
# $Id: alignlog,v f14c4827a786 2024/09/09 17:48:15 mast $
#

progname = 'alignlog'
prefix = 'ERROR: ' + progname + ' - '
output = 0

# Like doing awk from starting to ending line
def pyawk(lines, start, end, excludeEnd = False, skipMatch = None, skipEmpty = False, \
             separator = False):
   reStart = re.compile(start)
   reEnd = re.compile(end)
   gotStart = False
   gotAny = False
   for l in lines:
      if not gotStart and re.search(reStart, l):
         gotStart = True
         if separator and gotAny:
            prnstr(' ')
         gotAny = True
      putout = gotStart
      if gotStart and re.search(reEnd, l):
         gotStart = False
         putout = not excludeEnd

      if putout:
         if (not skipEmpty or len(l.rstrip())) and \
                (not skipMatch or l.find(skipMatch) < 0):
            prnstr(l.rstrip())


# Like doing grep on a single simple string
def pygrep(lines, string, skipMatch = None):
   parts = string.split('|')
   for l in lines:
      for strn in parts:
         if l.find(strn) >= 0 and (not skipMatch or l.find(skipMatch) < 0):
            prnstr(l.rstrip())
            break


# Put out a separator if there has been output already
def separator():
   global output
   if output:
      prnstr(' ')
      prnstr('* * * * * * * * * * * * * * * * * * * * * * * * * * * *')
      prnstr(' ')
   output = 1


#### MAIN PROGRAM  ####
#
# load System Libraries
import os, sys, re

#
# Setup runtime environment
if os.getenv('IMOD_DIR') != None:
   IMOD_DIR = os.environ['IMOD_DIR']
   if sys.platform == 'cygwin' and sys.version_info[0] > 2:
      IMOD_DIR = IMOD_DIR.replace('\\', '/')
      if IMOD_DIR[1] == ':' and IMOD_DIR[2] == '/':
         IMOD_DIR = '/cygdrive/' + IMOD_DIR[0].lower() + IMOD_DIR[2:]
   sys.path.insert(0, os.path.join(IMOD_DIR, 'pylib'))
   from imodpy import *
   addIMODbinIgnoreSIGHUP()
else:
   sys.stdout.write(prefix + " IMOD_DIR is not defined!\n")
   sys.exit(1)

#
# load IMOD Libraries
from pip import *

clipsize = ''

# Fallbacks from ../manpages/autodoc2man 3 1 alignlog
options = [":m:B:", ":e:B:", ":s:B:", ":l:B:", ":c:B:", ":r:B:", ":a:B:", ":b:B:",
           ":w:B:", ":p:B"]

PipEnableEntryOutput(0)
(numOpts, numNonOpts) = PipReadOrParseOptions(sys.argv, options, progname, 1, 1, 0)

infile = 'align.log'
if numNonOpts:
   infile = PipGetNonOptionArg(0)

if infile == 'a' or infile == 'b':
   infile = 'align' + infile + '.log'
if not os.path.exists(infile):
   exitError('Alignment log file ' + infile + ' does not exist')
lines = readTextFile(infile)

if not numOpts:
   sys.exit(0)

# Get options that are needed for 
error = PipGetBoolean('e', 0)
solution = PipGetBoolean('s', 0)
angle = PipGetBoolean('a', 0)
weight = PipGetBoolean('w', 0)
project = PipGetBoolean('p', 0)

# initialize variables as 1's and set up for searches
# Sorry, replicating grep output status with these variables
newsurface = 1    # for angle
pipinput = 1      # for angle
newratios = 1     # for error
oneratio = 1      # for error
nobeamtilt = 1    # for solution
noprogentry = 1   # for angle
nolocals = 1      # for angle
noCrossVal = 1    # for error or weight
projSkewLine = ''
rotAtMinTiltLine = ''
noCppVersion = 1

totalRat = re.compile('Ratio .*to total unknown')
formerRat = re.compile('Ratio .*formerly')

# find values of needed variables
for l in lines:
   if angle and newsurface and l.find('SURFACE ANALYSIS') >= 0:
      newsurface = 0
   if angle and pipinput and l.find("to do series") >= 0:
      pipinput = 0
   if error and newratios and re.search(totalRat, l):
      newratios = 0
   if error and oneratio and re.search(formerRat, l):
      oneratio = 0
   if solution and nobeamtilt and l.find("olved beam tilt") >= 0:
      nobeamtilt = 0
   if angle and noprogentry and l.find("ntries to program") >= 0:
      noprogentry = 0
   if (angle or weight or error or project) and nolocals and \
      l.find("Doing local area") >= 0:
      nolocals = 0
   if (angle or error or weight) and noCrossVal and l.find('leave-out') >= 0:
      noCrossVal = 0
   if solution and 'Projection skew is' in l:
      projSkewLine = l.strip()
   if solution and 'minimum tilt, rotation' in l:
      rotAtMinTiltLine = l.strip()
   if noCppVersion and 'Opened NEW file' in l:
      noCppVersion = 0

# Go through the arguments
for optind in range(1,numOpts+1):
   opt = sys.argv[optind]
   if opt == '-m':
      separator()
      pyawk(lines, '^ *Variable mappings', '^$')

   if opt == '-e':
      separator()
      if not newratios:
         pyawk(lines, '^ *Final   F', '^  Ratio')
      elif oneratio:
         pyawk(lines, '^.*Final   F', '^  Ratio of', False, 'weight', True, True)
      else:
         pyawk(lines, '^.*Final   F', '^  Ratio to')

      if noCrossVal:
         pygrep(lines, 'Residual error|Ratio of local', 'weight')
      else:
         for l in lines:
            if ('Residual error' in l and 'weighted' not in l) or 'Ratio of local' in l:
               if 'Global' in l or 'Ratio of local' in l:
                  prnstr(' ')
               prnstr(l.rstrip())
            if 'leave-out error' in l and '   robust' not in l:
               ind = l.find('weighted')
               if ind > 0:
                  l = l[:ind]
               prnstr(l.rstrip())
               if nolocals == 0 and 'Global' in l:
                  prnstr(' ')

   if opt == '-s':
      separator()
      if nobeamtilt == 0:
         pygrep(lines, 'Beam tilt angle is')
         prnstr(' ')
      if projSkewLine:
         prnstr(projSkewLine)
         prnstr(' ')
      if rotAtMinTiltLine:
         prnstr(rotAtMinTiltLine)
         prnstr(' ')
      pyawk(lines, '^ view.*deltilt', '^$')

   if opt == '-l':
      separator()
      for l in lines:
         if l.find('Doing local area') >= 0 or l.find('on bottom and') >= 0 or \
                (l.find('Residual error') >= 0 and l.find('weighted') < 0):
            prnstr(l.rstrip())
            if l.find('Residual error mean') >= 0:
               prnstr(' ')

   if opt == '-c':
      separator()
      pyawk(lines, '^ *3-D point', '^ Midpoint', True)

   if opt == '-r':
      separator()
      pyawk(lines, '^ *Projection points', '^$')
      
   if opt == '-a':
      separator()
      if newsurface == 1:
         pyawk(lines, '^ Fit to all', '^ 1 to do', True)
      elif pipinput == 0:
         pyawk(lines, '^ SURFACE ANALYSIS', '^ 1 to do', True)
      elif noprogentry == 1 or nolocals == 0 or noCrossVal == 0:
         if noCrossVal == 0:
            pyawk(lines, '^ SURFACE ANALYSIS', 'Running ', True)
         elif noCppVersion == 0:
            pyawk(lines, '^ SURFACE ANALYSIS', 'Opened NEW file', True)
         else:
            pyawk(lines, '^ SURFACE ANALYSIS', 'file opened', True)
      else:
         pyawk(lines, '^ SURFACE ANALYSIS', 'ntries to program', True)

   if opt == '-b':
      separator()
      pygrep(lines, ' beam tilt =')

   if opt == '-w':
      separator()
      pyawk(lines, 'Starting robust', 'are < .5', separator = True)
      if nolocals == 0:
         prnstr(' ')
         pyawk(lines, 'Summary of robust', 'are < .5')
         prnstr(' ')

      if noCrossVal:
         pygrep(lines, 'Residual error weighted')
         if nolocals == 0:
            pygrep(lines, 'Weighted error ')

      else:
         numBen = 0
         for l in lines:
            if 'Residual error weighted' in l:
               prnstr(l.rstrip())
            if 'Weighted error local' in l:
               prnstr(' ')
               prnstr(l.rstrip())
            if 'robust leave-out error' in l or 'Benefit' in l:
               prnstr(l.rstrip())
               if 'Benefit' in l and numBen == 0 and nolocals == 0:
                  prnstr(' ')
                  numBen = 1

   if opt == '-p':
      separator()
      ratioLine = ''
      ratioMatch = 'Ratio of total'
      cvMatch = 'Global'
      if nolocals == 0:
         ratioMatch = 'Ratio of local'
         cvMatch = 'Local'
      cvLines = []
      errLines = []
      for l in lines:
         if not ratioLine and ratioMatch in l:
            ratioLine = l
         if cvMatch in l and 'leave-out' in l:
            cvLines.append(l)
         if cvLines and 'Benefit from' in l:
            cvLines.append(l)
         if 'Residual error' in l and 'Local area' not in l:
            errLines.append(l)
         if nolocals == 0 and 'Weighted error' in l:
            errLines.append(l)
         
      outLines = [ratioLine] + errLines + cvLines
      for l in outLines:
         prnstr(l.rstrip())


sys.exit(0)
