#!/usr/bin/python

import os
import sys
import pickle
import re

ANIM_LOOP=1
ANIM_EDGE=2

COMPARE_THRESH=20

ss = "0.25" 

bin_dir = "../"

def render(genome_file, frame_time=-1, num_frames=160, anim_type=ANIM_LOOP, seed='slartibartfast'):

    common_vars = 'format=png transparency=0 ss=' + ss + ' isaac_seed=' + seed + ' '

    if frame_time == -1: 

	################
	# Static frame #
	################

	# build the render command
	# bench flames render very fast, so bump size & q
	render_cmd  = 'env in="' + genome_file + '" qs=2 name_enable=1 '
	render_cmd += common_vars + bin_dir + 'flam3-render'

    else:

	###################
	# Animation frame #
	###################

	output_file = genome_file.split('.')[0]

	# Create the block of three frames for the animation
	genome_cmd = 'env frame=%d nframes=%d' % (frame_time,num_frames)

	# Command changes depending on loop or edge
	if anim_type==ANIM_LOOP:

	    genome_cmd += ' rotate="' + genome_file + '"'

	elif anim_type==ANIM_EDGE:

	    genome_cmd += ' inter="' + genome_file + '"'

	genome_cmd += ' ' + bin_dir + "flam3-genome > " + output_file + ".anim"

	# Run the genome command
        print "Genome: " + genome_cmd
	retval = os.system(genome_cmd)

	if retval != 0:
	    print "Error creating genome file.  Aborting test."
	    return retval,0


	# now we can build the render command
	render_cmd  = 'env in="' + output_file + '.anim" frame=%d ' % frame_time
	render_cmd += common_vars + ' out="' + output_file + '.png" ' + bin_dir + 'flam3-animate'

    # Render the image, and time the render as well
    print "Render: " + render_cmd
    start_time = os.times()[4]

    retval = os.system(render_cmd)

    if retval != 0:
	print "Error rendering flame.  Aborting test."
	return retval,0

    end_time = os.times()[4]

    # Calculate the time to render
    duration = end_time-start_time

    # Clean up if animation selected
    if frame_time != -1:
	os.system('rm ' + output_file + '.anim')

    return retval,duration

def compare(reference_img, test_img, alt_thresh=COMPARE_THRESH):

    # Create the compare cmd
    compare_cmd = 'env threshold=%d ./blendjpg "%s" "%s"' % (alt_thresh, reference_img, test_img)

    #print "Compare: " + compare_cmd
    # Compare images and return the blendjpg return value
    return(os.system(compare_cmd))

def issue_report(tref, ttrial):

    # Get max filename for first column
    colwidth = max(len(x[0]) for x in ttrial)

    print '\n'
    print ' '.ljust(colwidth) + "   R  RefS  TriS    PrcD  ImgCompare"  
    
    for fli in range(len(tref)):

	trefi = tref[fli]
	ttriali = ttrial[fli]

        if ttriali[2]==0:
	    succ = 'Y'
	    prcd = '%5.1f%%' % (100*(ttriali[3]-trefi[3])/trefi[3])
	    cmpr = compare('reference/'+trefi[1],'trial/'+ttriali[1])
	    if cmpr==0:
		prcd += '  COMPARE OK'
	    else:
		prcd += '  COMPARE FAIL'
	else:
	    succ = 'N'
	    prcd = ' FAIL  FAIL'

	outstr = ttriali[0].ljust(colwidth) + " : %1s  %4d  %4d  %s" % (succ,trefi[3],ttriali[3],prcd)

	print outstr
	    
def okfail(val):
    if val==0:
       return 'OK'
    else:
       return 'FAIL'
 

####

if len(sys.argv) < 2 or len(sys.argv) > 3 or sys.argv[1] not in ['trial','reference','consistency','report','fuzz']:
    print "flam3 regression test suite"
    print "Syntax: %s [reference|trial|report|consistency] [scale]"
    print "   reference   : renders test flames into 'reference' subdirectory"
    print "                 and stores timing information"
    print "   trial       : renders test flames into 'trial' subdirectory"
    print "                 and compares output images & render times with those"
    print "                 in 'reference'."
    print "   report      : regenerates the report based on the contents of"
    print "                 the reference and trial directories."
    print "   consistency : generates images for self-consistency of different settings."
    print "   fuzz        : checks the response of the system to bad input."
    print " "
    print "   scale       : scalar to adjust the output size of the images as a whole."
    sys.exit(1)

if sys.argv[1]=='report':
    reference=pickle.load(open('reference/reference.timing'))
    trial=pickle.load(open('trial/trial.timing'))
    issue_report(reference,trial)
    sys.exit(0)

if len(sys.argv)==3:
    ss = sys.argv[2]

outpath = sys.argv[1]

if not os.path.isdir(outpath):
    os.mkdir(outpath)
    
# Fuzz checks existing code for issues with errors in the xml
# like missing values, non-numeric characters in fields, etc.
if outpath=='fuzz':

    fuzz_out = []

    badlist = os.listdir('genomes')
    badfilt = re.compile('^bad.*\.flame')
    badgenes = filter(badfilt.search,badlist)
    
    for bg in badgenes:
       rtv,rtim = render('genomes/' + bg)    
       if rtv != 0 :
          fuzz_out.append([bg, 'failed properly']);
       else :
          fuzz_out.append([bg, 'DID NOT FAIL']);
    
    print '----------------'
       
    for r in fuzz_out:
       print r[0] + ': ' + r[1]
       


    sys.exit(0)
    
    


# Consistency checks existing code for issues with
# density estimation, size, and quality w.r.t brightness
# as well as some motion blur stuff
if outpath=='consistency':
    
    # Render the static genome with different settings
    rtv, rtim = render('genomes/consist.flame')
    
    rtv, rtim = render('genomes/repeatable.flame')
    os.system('mv repeat.png repeat_a.png');
    
    rtv, rtim = render('genomes/repeatable.flame')
    os.system('mv repeat.png repeat_b.png');
    
    rtv, rtim = render('genomes/repeatable.flame',-1,0,0,'beeblebrox')
    os.system('mv repeat.png repeat_c.png');

    # Resize the consist-2xsize image to the original size
    os.system('convert consist-2xsize.png -filter gaussian -resize 50% -depth 8 consist-2xresized.png')

    # Move all of the images to the output directory
    os.system('mv *.png %s' % outpath)

    # Compare all of the images against the default
    print "Consistency check"
    print "-----------------"
    cmpr = compare(outpath+'/consist-node.png',outpath+'/consist-orig.png')
    print "Orig vs. No DE   : Compare " + okfail(cmpr)
    cmpr = compare(outpath+'/consist-2xresized.png',outpath+'/consist-orig.png')
    print "Orig vs. 2X size : Compare " + okfail(cmpr)
    cmpr = compare(outpath+'/consist-4xqual.png',outpath+'/consist-orig.png')
    print "Orig vs. 4x Q    : Compare " + okfail(cmpr)
    cmpr = compare(outpath+'/consist-ss3.png',outpath+'/consist-orig.png')
    print "Orig vs. SS=3    : Compare " + okfail(cmpr)
    cmpr = compare(outpath+'/repeat_c.png',outpath+'/repeat_a.png')
    print "Two diff seeds   : Compare " + okfail(cmpr)
    cmpr = compare(outpath+'/repeat_b.png',outpath+'/repeat_a.png',1)
    print "Same seeds       : Compare " + okfail(cmpr)

    sys.exit(0)

if outpath=='trial':
    if not os.path.isfile('reference/reference.timing'):
	# issue error & exit
	print "Cannot find reference timing information.  Aborting."
	sys.exit(2)

##################################
# Enumerate all tests to execute #
##################################
timings = []

########################
# First, static images #
########################

# All variations tested individually
render_list = [ 'var%02d.flame' % x for x in range(54) ]


for rl in render_list:
    rtv, rtim = render('genomes/' + rl)
    ifile = rl.split('.')[0] + '.png'
    os.system('mv %s %s' % (ifile,outpath))
    timings.append([rl, ifile, rtv, rtim])


#########################
# Then animation frames #
#########################

# elements are [ file, frame number, num frames, ANIM_EDGE/ANIM_LOOP ]
anim_list = [ ['120073.flame', 75, 160, ANIM_LOOP],
              ['26208.flame', 41, 160, ANIM_LOOP],
              ['242_2575.flame',80,160, ANIM_EDGE],
	      ['243_00210.flame', 65, 160, ANIM_LOOP],
	      ['243_00212.flame', 121, 160, ANIM_LOOP],
	      ['243_00313.flame', 0, 160, ANIM_LOOP],
	      ['243_00305.flame', 50, 160, ANIM_EDGE],
	      ['243_00336_edge.flame', 91, 160, ANIM_EDGE],
              ['243_00358_edge.flame', 24, 160, ANIM_EDGE],
              ['243_00604_edge.flame', 107, 160, ANIM_EDGE],
              ['243_00447_edge.flame', 80, 160, ANIM_EDGE],
              ['243_2918_edge.flame', 80, 160, ANIM_EDGE],
              ['121566_old.flame', 75, 128, ANIM_EDGE],
              ['202.13926.old.flame', 40, 128, ANIM_EDGE],
              ['198-22682-sing.flame', 100,160, ANIM_EDGE] ]

for al in anim_list:
    rtv,rtim = render('genomes/' + al[0],al[1],al[2],al[3])
    ifile = al[0].split('.')[0] + '.png'
    os.system('mv genomes/%s %s/.' % (ifile,outpath))
    timings.append([al[0], ifile, rtv, rtim])
    
############################
# Now do the sequence test #
############################
frmlist = ['seq%05d.png' % x for x in range(0,440,20)]

render_cmd  = 'env in="genomes/testseq.flame" begin=0 end=420 dtime=20 prefix=%s/seq ' % outpath
render_cmd += 'format=png transparency=0 ss=' + ss + ' isaac_seed=slartibartfast '
render_cmd += bin_dir + 'flam3-animate'

start_time = os.times()[4]
retval = os.system(render_cmd)

if retval != 0:
   print "Error rendering flame sequence"
   rtim=0
else:
   end_time = os.times()[4];
   rtim=end_time-start_time
    
for x in frmlist:
   timings.append(['testseq:'+x,x,retval,rtim])

#######################################
# If reference, save results and exit #
#######################################   
if outpath=='reference':
    # Save timing / render information to file
    pickle.dump(timings, open('reference/reference.timing','w'))

    print('Reference timing information saved.  Reference run complete.')
    sys.exit(0)

pickle.dump(timings,open('trial/trial.timing','w'))

##########################
# trial mode comparisons #
##########################
reference = pickle.load(open('reference/reference.timing'))

# Issue a report 
issue_report(reference,timings)
