#!/usr/bin/env python

###################################README######################################
# This is a wrapper script of VICAR program marsortho that generates ortho and
# DEM mosaics. Note this script doesn't replicate all the functionalities in 
# marsortho. Use marsortho directly if the ORRs and DEMs are highly customized. 
###################################README######################################

import os
import sys
import glob
import errno
import subprocess
import re

def main(args):
    # Get RSL and XML products.
    if args.img_lis == None or args.xyz_lis == None:
        rsl_prods, xml_prods = retrieve_prods(args.sol, args.end_sol, 
            args.instrument, args.site, args.position, args.sequence, 
            args.filter, args.who)
    else:
        with open(args.img_lis, 'r') as f:
            rsl_prods = f.read().splitlines()

        with open(args.xyz_lis, 'r') as f:
            xml_prods = f.read().splitlines()

    # validate RSL and XML lists:
    validate_prods(rsl_prods, xml_prods)

    # write RSL and XML lists to disk
    working_dir, img_lis_path, xyz_lis_path = write_to_disk(rsl_prods, xml_prods, 
        args.out_dir)

    # run marsortho
    orr_out, dem_out = run_marsortho(img_lis_path, xyz_lis_path, working_dir,
                                     args.cs, args.z_cs, args.scale, args.cut)

    # transcode
    transcode(orr_out, dem_out)

    # generate world files
    gen_world_files(orr_out, dem_out, args.scale, args.cut)

    # generate ORR and DEM mosaics txt files
    orr_txt = gen_txt_file(orr_out, args.sol, args.end_sol, args.instrument, 
        args.site, args.position)
    dem_txt = gen_txt_file(dem_out, args.sol, args.end_sol, args.instrument, 
        args.site, args.position)

    # only create symlinks, verify outputs, print email info when generating 
    # products on OSS.
    if args.out_dir == None:
        # create symlinks for .IMG, .LIS, and .TXT
        create_symlinks(orr_out)
        create_symlinks(dem_out)    

        # verify the outputs
        verify_outputs(orr_out, dem_out)

        # print out the paths of the ORR and DEM products need to be visually checked.
        # print out the paths of the ORR and DEM products need to be sent to users.
        print_check_email_info(orr_out, dem_out)

def print_check_email_info(orr_out, dem_out):
    print '==================================================================='
    print '[INFO] Please visually check the following ORR and DEM product:'
    print '[INFO] %s.IMG' % orr_out[:-4] 
    print '[INFO] %s.IMG' % dem_out[:-4]

    print '==================================================================='
    print '[INFO] Please send the following message to users:'
    print 'Hello,'
    print '\nPlease see the ORR and DEM products below:'
    print '\nORR'
    print '%s.PNG' % orr_out[:-4]
    print '%s.PGW' % orr_out[:-4]
    print '\nDEM'
    print '%s.TIF' % dem_out[:-4]
    print '%s.TFW' % dem_out[:-4]
    print '\nThanks,\nMER MIPL'

def verify_outputs(orr_out, dem_out):
    print '==================================================================='
    orr_basename = os.path.basename(orr_out)
    orr_basename = os.path.splitext(orr_basename)[0]
    dem_basename = os.path.basename(dem_out)
    dem_basename = os.path.splitext(dem_basename)[0]
    special_dir = os.path.dirname(orr_out)
  
    if 'special' in special_dir:
        archive_dir = special_dir.replace('special', 'archive')
    else:
        print '[ERROR] Incorrect path when verifying output products.'
        sys.exit(1)

    # check ORR products in special directory
    orr_sp_exts = ['LIS', 'BRT', 'IMG', 'PNG', 'PGW', 'TXT']
    orr_sp_counter = 0
    print '[INFO] Verify ORR products in special directory:'
    for ext in orr_sp_exts:
        filename = '%s/%s.%s' % (special_dir, orr_basename, ext)
        if os.path.isfile(filename):
            print '[INFO] %s' % filename
            orr_sp_counter = orr_sp_counter + 1
        else:
            print '[ERROR] %s missing.' % filename
    if len(orr_sp_exts) == orr_sp_counter:
        print '[INFO] %d/%d. Verification passed.\n' % (orr_sp_counter, len(orr_sp_exts))
    else:
        print '[ERROR] %d/%d. Verification failed.\n' % (orr_sp_counter, len(orr_sp_exts))
        sys.exit(1)

    # check DEM products in special directory
    dem_sp_exts = ['LIS', 'IMG', 'TIF', 'TFW', 'TXT']
    dem_sp_counter = 0
    print '[INFO] Verify DEM products in special directory:'
    for ext in dem_sp_exts:
        filename = '%s/%s.%s' % (special_dir, dem_basename, ext)
        if os.path.isfile(filename):
            print '[INFO] %s' % filename
            dem_sp_counter = dem_sp_counter + 1
        else:
            print '[ERROR] %s missing.' % filename
    if len(dem_sp_exts) == dem_sp_counter:
        print '[INFO] %d/%d. Verification passed.\n' % (dem_sp_counter, len(dem_sp_exts))
    else:
        print '[ERROR] %d/%d. Verification failed.\n' % (dem_sp_counter, len(dem_sp_exts))
        sys.exit(1)

    # check ORR and DEM products in archive directory
    ar_exts = ['LIS', 'IMG', 'TXT']
    ar_counter = 0
    print '[INFO] Verify ORR and DEM products in archive directory:'
    for ext in ar_exts:
        orr_filename = '%s/%s.%s' % (archive_dir, orr_basename, ext)
        dem_filename = '%s/%s.%s' % (archive_dir, dem_basename, ext)

        if os.path.isfile(orr_filename):
            print '[INFO] %s' % orr_filename
            ar_counter = ar_counter + 1
        else:
            print '[ERROR] %s missing.' % orr_filename
        
        if os.path.isfile(dem_filename):
            print '[INFO] %s' % dem_filename
            ar_counter = ar_counter + 1
        else:
            print '[ERROR] %s missing.' % dem_filename
    if len(ar_exts) * 2 == ar_counter:
        print '[INFO] %d/%d. Verification passed.' % (ar_counter, len(ar_exts) * 2)
    else:
        print '[ERROR] %d/%d. Verification failed.' % (ar_counter, len(ar_exts) * 2)
        sys.exit(1)
        
def force_symlink(source, target):
    try:
        os.symlink(source, target)
    except OSError, e:
        if e.errno == errno.EEXIST:
            os.remove(target)
            os.symlink(source, target)

def create_symlinks(filename):
    print '==================================================================='
    source_dir = os.path.dirname(filename)
    basename = os.path.basename(filename)
    basename = os.path.splitext(basename)[0]
    img_source_filename = '%s/%s.IMG' % (source_dir, basename)
    lis_source_filename = '%s/%s.LIS' % (source_dir, basename)
    txt_source_filename = '%s/%s.TXT' % (source_dir, basename)

    if 'special' in source_dir:
        archive_dir = source_dir.replace('special', 'archive')
    else:
        print '[ERROR] Incorrect path when creating symlinks from special to ' \
              'archive directory'
        sys.exit(1)

    if not os.path.isdir(archive_dir):
        create_dir(archive_dir)

    img_target_filename = '%s/%s.IMG' % (archive_dir, basename)
    lis_target_filename = '%s/%s.LIS' % (archive_dir, basename)
    txt_target_filename = '%s/%s.TXT' % (archive_dir, basename)

    print 'Create symlink from %s to %s' % (img_source_filename, 
        img_target_filename)
    force_symlink(img_source_filename, img_target_filename)
    if not os.path.islink(img_target_filename):
        print '[ERROR] failed creating symlink for %s from special to archive' \
              'directory. Exit.' % img_source_filename
        cleanup_archive_dir('%s/%s' % (archive_dir, basename))
        sys.exit(1)

    print 'Create symlink from %s to %s' % (lis_source_filename, 
        lis_target_filename)
    force_symlink(lis_source_filename, lis_target_filename)
    if not os.path.islink(lis_target_filename):
        print '[ERROR] failed creating symlink for %s from special to archive' \
              'directory. Exit.' % lis_source_filename
        cleanup_archive_dir('%s/%s' % (archive_dir, basename))
        sys.exit(1)

    print 'Create symlink from %s to %s' % (txt_source_filename, 
        txt_target_filename)
    force_symlink(txt_source_filename, txt_target_filename)
    if not os.path.islink(txt_target_filename):
        print '[ERROR] failed creating symlink for %s from special to archive' \
              'directory. Exit.' % txt_source_filename
        cleanup_archive_dir('%s/%s' % (archive_dir, basename))
        sys.exit(1)

def gen_txt_file(path, sol, end_sol, inst, site, pos):
    basepath = path[:-4] # parsed based on MER Camera SIS
    
    with open('%s.TXT' % basepath, 'w') as txt:
        txt.write('PDS:\n\n')
        if end_sol == None or sol == end_sol:
            txt.write('MER-B Opportunity Sol %s\n' % str(sol))
        else:
            txt.write('MER-B Opportunity Sol %s-%s\n' % (str(sol), str(end_sol)))

        if pos == None:
            if 'N' in inst and not 'P' in inst:
                txt.write('Navcam orthorectified mosaic for site %s\n\n' % site)
            elif not 'N' in inst and 'P' in inst:
                txt.write('Pancam orthorectified mosaic for site %s\n\n' % site)
            elif 'N' in inst and 'P' in inst:
                txt.write('Navcam and Pancam orthorectified mosaic for site %s ' \
                          '\n\n' % site)
            else:
                print '[ERROR] Cannot extract instrument information when ' \
                      'creating text files.'
                sys.exit(1)
        else:
            if 'N' in inst and not 'P' in inst:
                txt.write('Navcam orthorectified mosaic for site %s and ' \
                          'position %s\n\n' % (site, pos))
            elif not 'N' in inst and 'P' in inst:
                txt.write('Pancam orthorectified mosaic for site %s and ' \
                          'position %s\n\n' % (site, pos))
            elif 'N' in inst and 'P' in inst:
                txt.write('Navcam and Pancam orthorectified mosaic for site %s ' \
                          'and position %s\n\n' % (site, pos))
            else:
                print '[ERROR] Cannot extract instrument information when ' \
                      'creating text files.'
                sys.exit(1)

        txt.write('MIPL/OPGS')
    
    return '%s.TXT' % basepath
    

def gen_world_files(orr_out, dem_out, scale, cut):
    orr_basepath = orr_out[:-4] # parsed based on MER Camera SIS
    dem_basepath = dem_out[:-4] # parsed based on MER Camera SIS
    orr_world_path = orr_basepath + '.PGW' # convention for world file
    dem_world_path = dem_basepath + '.TFW' # convention for world file

    # Calculate the six parameters world files require.
    # Reference: https://en.wikipedia.org/wiki/World_file
    # x_scale: pixel size in the x-direction in meters/pixel.
    # y_rotation: rotation about y-axis.
    # x_rotation: rotation about x-axis.
    # y_scale: pixel size in the y-direction in meters/pixel, almost always 
    #          negative.
    # x_upper_left: x-coordinate of the center of the upper left pixel.
    # y_upper_left: y-coordinate of the center of the upper left pixel.
    x_scale = scale 
    y_rotation = 0.0
    x_rotation = 0.0
    y_scale = -scale
    x_upper_left = 0 - (cut - x_scale / 2)
    y_upper_left = 0 + (cut + y_scale / 2)

    # write to world files
    with open(orr_world_path, 'w') as f:
        f.write('%f\n%f\n%f\n%f\n%f\n%f' % (x_scale, y_rotation, x_rotation, 
                y_scale, x_upper_left, y_upper_left))
    with open(dem_world_path, 'w') as f:
        f.write('%f\n%f\n%f\n%f\n%f\n%f' % (x_scale, y_rotation, x_rotation,
                y_scale, x_upper_left, y_upper_left))

# remove files based on a given basepath.
# basepath example:
# $OSS_SOL/4601/opgs/rdr/ncam/mosaic/vert/special/1NNDW1ZZLCUORRBVP1969L000T3
def remove_files(basepath):
    exts = ['LIS', 'BRT', 'VIC', 'TIF', 'PNG', 'IMG', 'PGW', 'TFW', 'TXT']
    for ext in exts:
        filename = '%s.%s' % (basepath, ext)
        if os.path.isfile(filename):
            os.remove(filename)
            print '[INFO] removed %s' % filename

# remove a single file.
def remove_file(filename):
    if os.path.isfile(filename):
        os.remove(filename)
        print '[INFO] removed %s' % filename

# cleanup the special directory
def cleanup_special_dir(orr_basepath, dem_basepath):
    remove_files(orr_basepath)
    remove_files(dem_basepath)
    remove_file('%s_byte.VIC' % orr_basepath)

# cleanup the archive directory
def cleanup_archive_dir(basepath):
    remove_files(basepath)

def transcode(orr_out, dem_out):
    cform = marsmap = os.environ.get('R2LIB') + '/cform'
    vic2tiff_float = '/home/mermipl/tools/vic2tiff_float'
    vic2png = '/home/mermipl/tools/vic2png_4701'
    transcode_tmp = '/home/mermipl/tools/transcode_tmp'

    orr_basepath = orr_out[:-4] # parsed based on MER Camera SIS
    dem_basepath = dem_out[:-4] # parsed based on MER Camera SIS

    # convert to byte image
    print '==================================================================='
    print '[INFO] Convert ORR to byte format:'
    cform_command = cform + ' ' + orr_out + ' ' + orr_basepath + '_byte.VIC ' \
        '-byte irange=\\(0,4095\\) orange=\\(0,255\\)'
    print '[INFO] %s' % cform_command
    proc = subprocess.Popen(cform_command, stdout=subprocess.PIPE, shell=True)
    proc.communicate()
    if not os.path.isfile('%s_byte.VIC' % orr_basepath):
        print '[ERROR] $R2LIB/cform program failed converting ORR to byte format.'
        cleanup_special_dir(orr_basepath, dem_basepath)
        sys.exit(1)
         
    # transcode DEM from VIC to TIF format
    print '==================================================================='
    print '[INFO] Transcode DEM from VIC to TIF format'
    vic2tiff_command = vic2tiff_float + ' ' + dem_basepath
    print '[INFO] %s' % vic2tiff_command
    proc = subprocess.Popen(vic2tiff_command, stdout=subprocess.PIPE, shell=True)
    proc.communicate()
    if not os.path.isfile('%s.TIF' % dem_basepath):
        print '[ERROR] vic2tiff_float failed transcoding VIC to TIF.'
        cleanup_special_dir(orr_basepath, dem_basepath)
        sys.exit(1)
    
    # transcode ORR from VIC to PNG format
    print '==================================================================='
    print '[INFO] Transcode ORR from VIC to PNG format'
    vic2png_command = vic2png + ' ' + orr_basepath + '_byte'
    print '[INFO] %s' % vic2png_command
    proc = subprocess.Popen(vic2png_command, stdout=subprocess.PIPE, shell=True)
    proc.communicate()
    if not os.path.isfile('%s_byte.PNG' % orr_basepath):
        print '[ERROR] vic2png failed transcoding VIC to PNG'
        cleanup_special_dir(orr_basepath, dem_basepath)
        sys.exit(1)

    # rename PNG ORR
    if not os.path.isfile(orr_basepath + '_byte.PNG'):
        print '[ERROR] %s_byte.PNG does not exist. Exit.'
        sys.exit(1)
    os.rename('%s_byte.PNG' % orr_basepath, '%s.PNG' % orr_basepath)
    if not os.path.isfile('%s.PNG' % orr_basepath):
        print '[ERROR] failed renaming PNG. Exit.'
        cleanup_special_dir(orr_basepath, dem_basepath)
        sys.exit(1)

    # transcode ORR from VIC to IMG
    print '==================================================================='
    print '[INFO] Transcode ORR from VIC to IMG'
    transcode_orr_command = transcode_tmp + ' ' + orr_basepath
    print '[INFO] %s' % transcode_orr_command
    proc = subprocess.Popen(transcode_orr_command, stdout=subprocess.PIPE, 
        shell=True)
    proc.communicate()
    if not os.path.isfile('%s.IMG' % orr_basepath):
        print '[ERROR] transcode_tmp failed transcoding VIC to IMG. Exit.'
        cleanup_special_dir(orr_basepath, dem_basepath)
        sys.exit(1)

    # transcode DEM from VIC to IMG
    print '==================================================================='
    print '[INFO] Transcode DEM from VIC to IMG'
    transcode_dem_command = transcode_tmp + ' ' + dem_basepath
    print '[INFO] %s' % transcode_dem_command
    proc = subprocess.Popen(transcode_dem_command, stdout=subprocess.PIPE, 
        shell=True)
    proc.communicate()
    if not os.path.isfile('%s.IMG' % dem_basepath):
        print '[ERROR] transcode_tmp failed transcoding VIC to IMG. Exit.'
        cleanup_special_dir(orr_basepath, dem_basepath)
        sys.exit(1)

    # clean up tmp files
    remove_file('%s_byte.VIC' % orr_basepath)
    remove_file('%s.VIC' % orr_basepath)
    remove_file('%s.VIC' % dem_basepath)

def write_to_disk(rsl_prods, xml_prods, out_dir):
    # tools
    mersclk2sol = '/home/mermipl/tools/matis/mersclk2sol' 
    merint2asc = '/home/mermipl/tools/merint2asc'

    # build mosaic file name based on the first product in list
    basename = os.path.basename(rsl_prods[0])
    basename = os.path.splitext(basename)[0]
  
    # The following fields are parsed and constructed based on the naming 
    # conventions in MER Camera SIS. 
    # scid field. 1 for Opportunity.
    scid = '1'

    # inst field
    is_ncam = False
    is_pcam = False
    for rsl_prod in rsl_prods:
        rsl_basename = os.path.basename(rsl_prod)
        if rsl_basename[1:2] == 'N':
            is_ncam = True
        if rsl_basename[1:2] == 'P':
            is_pcam = True 

    if is_ncam and is_pcam:
        inst = 'NP'
        cam_dir = 'mult'
    elif is_ncam and not is_pcam:
        inst = 'NN'
        cam_dir = 'ncam'
    elif not is_ncam and is_pcam:
        inst = 'PP'
        cam_dir = 'pcam'
    else:
        print '[ERROR] Cannot build instrument name.'
        sys.exit(1)
 
    # sol field
    sclk = basename[2:11]
    mersclk2sol_command = mersclk2sol + ' ' + sclk
    proc = subprocess.Popen(mersclk2sol_command, stdout=subprocess.PIPE, \
                            shell=True)
    sol_int = proc.stdout.read().strip()
    merint2asc_command = merint2asc + ' ' + sol_int + ' -sol'
    proc = subprocess.Popen(merint2asc_command, stdout=subprocess.PIPE, \
                            shell=True)
    sol_str = proc.stdout.read().strip()
    sol_asc = re.findall(r'"([^"]*)"', sol_str)[0] # sol in ascii representation
    rsl_prod_str = 'RSL' # prod filed for ORR
    xml_prod_str = 'ZZL' # prod field for DEM
    site = basename[14:16] # site field
    proj = 'ORR' # projection field
    pos = basename[16:18] # position field
    seq = basename[18:23] # sequence field
    eye = basename[23:24] # eye field
    filt = basename[24:25] + basename[24:25] + basename[24:25] # filt field
    who = basename[25:26] # who field

    # Find out where to save files
    if out_dir == None:
        oss_sol = os.environ.get('OSS_SOL')
        working_dir = '%s/%s/opgs/rdr/%s/mosaic/vert/special/' % (oss_sol, 
            sol_int, cam_dir)
        if not os.path.isdir(working_dir):
            create_dir(working_dir)
    else:
        working_dir = '%s/' % out_dir

    # ver field
    img_files = glob.glob(working_dir + '/*.IMG')
    if len(img_files) == 0:
        ver = str(1)
    else:
        img_files.sort()
        cur_ver = os.path.splitext(img_files[-1])[0][-1:]
        # valid the highest version. Must be in range of 1-9 and A-Z.
        if ord(cur_ver) in range(ord('1'), ord('9') + 1) or \
           ord(cur_ver) in range(ord('A'), ord('Z') + 1):
            # now up version. 
            if ord(cur_ver) == ord('9'):
                ver = 'A'    
            elif ord(cur_ver) >= ord('Z'):
                ver = 'Z'
            else:
                ver = chr(ord(cur_ver) + 1)
        else:
            print '[ERROR] Cannot up version product. Exit.'
            sys.exit(1)

    # build RSL and XML list names
    rsl_lis_name = scid + inst + sol_asc + rsl_prod_str + site + proj + pos + \
                   seq + eye + filt + who + ver + '.LIS'
    xml_lis_name = scid + inst + sol_asc + xml_prod_str + site + proj + pos + \
                   seq + eye + filt + who + ver + '.LIS'

    # build RSL and XML list paths
    rsl_lis_path = working_dir + rsl_lis_name
    xml_lis_path = working_dir + xml_lis_name

    # write to RSL and XML lists
    rsl_lis_file = open(rsl_lis_path, 'w')
    xml_lis_file = open(xml_lis_path, 'w')
    for rsl_prod in rsl_prods:
        rsl_lis_file.write('%s\n' % rsl_prod)
    for xml_prod in xml_prods:
        xml_lis_file.write('%s\n' % xml_prod)

    return working_dir, rsl_lis_path, xml_lis_path

def validate_prods(rsl_prods, xml_prods):
    # the number of input images should not be zero.
    if len(rsl_prods) == 0:
        print '[WARNING] Empty list. Check your search terms. Exit.'
        sys.exit(0)

    # RSL and XYZ lists must contain the same number of inputs.
    if len(rsl_prods) != len(xml_prods):
        print '[ERROR] Image list and XYZ list contain different number of ' \
              'products.'
        sys.exit(1)
  
    # RSL's sclk must match XYZ's sclk.
    for rsl_prod, xml_prod in zip(rsl_prods, xml_prods):
        rsl_sclk = os.path.basename(rsl_prod)[2:11]
        xml_sclk = os.path.basename(xml_prod)[2:11]
        if rsl_sclk != xml_sclk:
            print '[ERROR] Image list and XYZ list do not co-register.'
            sys.exit(1)
                  
def validate_params(args):
    # validate environmental variable MARSLIB is set
    if check_select('MARSLIB') is None:
        print '[ERROR] cannot access MARSLIB.'
        sys.exit(1)

    # validate img_lis and xyz_lis
    if args.img_lis != None and args.xyz_lis == None:
        print '[ERROR] img_lis and xyz_lis must be supplied together.'
        sys.exit(1)
    elif args.img_lis == None and args.xyz_lis != None:
        print '[ERROR] img_lis and xyz_lis must be supplied together.'
        sys.exit(1)
    elif args.img_lis != None and args.xyz_lis != None:
        if not os.path.isfile(args.img_lis):
            print '[ERROR] img_lis %s does not exist.' % args.img_lis
            sys.exit(1)
        if not os.path.isfile(args.xyz_lis):
            print '[ERROR] xyz_lis %s does not exist.' % args.xyz_lis
            sys.exit(1)
    else:
        if args.sol == None or args.site == None:
            print '[ERROR] parameters provided are not sufficient to run ' \
                  'program.'
            sys.exit(1)

    # validate output directory
    if args.out_dir != None and not os.path.isdir(args.out_dir):
        print '[ERROR] output directory %s does not exist.' % args.out_dir
        sys.exit(1)

    # validate sol and end_sol
    if args.sol != None and args.sol <= 0:
        print '[ERROR] invalid Sol: %s.' % args.sol
        sys.exit(1)
    if args.end_sol != None and args.end_sol <= 0:
        print '[ERROR] invalid end_sol: %s' % args.end_sol
        sys.exit(1)
    if args.sol != None and args.end_sol != None and \
       int(args.end_sol) < int(args.sol): 
        print '[ERROR] end_sol is smaller than sol.'
        sys.exit(1)

    # validate instrument
    if len(args.instrument) > 2:
        print '[ERROR] invalid instrument string %s.' % args.instrument
        sys.exit(1)
    for ch in args.instrument:
        if not ch in 'NP':
            print '[ERROR] invalid instrument %s. Valid instruments are NP.' % ch
            sys.exit(1)

    # validate site
    if args.site != None and len(args.site) != 2:
        print '[ERROR] invalid site: %s.' % args.site
        sys.exit(1)

    # validate position
    if args.position != None and len(args.position) != 2:
        print '[ERROR] invalid position: %s.' % args.position
        sys.exit(1)

    # validate sequence
    if args.sequence != None and len(args.sequence) != 5:
        print '[ERROR] invalid sequence: %s.' % args.sequence
        sys.exit(1)

    # validate filter
    if args.filter != None and len(args.filter) != 1:
        print '[ERROR] invalid filter: %s.' % args.filter
        sys.exit(1)
  
    # validate who
    if args.who != None and len(args.who) != 1:
        print '[ERROR] invalid who: %s.' % args.who
        sys.exit(1)

def retrieve_prods(sol, e_sol, inst, site, pos, seq, filt, who):
    if pos == None:
        pos = '*'

    if seq == None:
        seq = '*'

    if filt == None:
        filt = '*'

    if who == None:
        who = '*'

    # environmental variable OSS_SOL
    oss_sol = os.environ.get('OSS_SOL')

    # RSL product partial directory path after $OSS_SOL/sol/
    rsl_subdir = '/opgs/rdr/[pn]cam/rad/linear/sap/'

    # XML producty partial directory path after $OSS_SOL/sol/
    xml_subdir = '/opgs/rdr/[pn]cam/xyz/linear/mask/'

    # construct RSL and XML products directory to search
    sols = []
    if e_sol != None:
        sol_int = int(sol)
        e_sol_int = int(e_sol)
        while sol_int <= e_sol_int:
            sols.append(str(sol_int))
            sol_int = sol_int + 1
    else:
        sols.append(sol)    

    rsl_fname = '1[' + inst + ']*RSL' + site + pos + seq + 'L' + filt \
                + who + '*.IMG'
    xml_fname = '1[' + inst + ']*XYL' + site + pos + seq + 'L' + filt \
                + who + '*.MSK'
    rsl_tmp = []
    xml_tmp = []
    for s in sols:
        # RSL and XML directories
        rsl_dir = oss_sol + '/' + s + rsl_subdir
        xml_dir = oss_sol + '/' + s + xml_subdir

        # Get preliminary RSL and XML products. 
        rsl_tmp.extend(glob.glob(rsl_dir + rsl_fname))
        xml_tmp.extend(glob.glob(xml_dir + xml_fname))

    # Filter RSL and XML lists
    rsl_prods = [p for p in rsl_tmp if has_right_eye(p) and 
                                       is_highest_ver(p, rsl_tmp) and 
                                       not is_sun_image(p)] 
    xml_prods = [p for p in xml_tmp if is_highest_ver(p, xml_tmp) and 
                                       not is_sun_image(p)]

    # Sort RSL and XML lists so that they have the same order
    rsl_prods.sort()
    xml_prods.sort()

    # Print out the RSL and XML lists
    print '[INFO] RSL list:'
    for idx, rsl_prod in enumerate(rsl_prods):
        print '[INFO] %3d. %s' % (idx + 1, rsl_prod)
    print '[INFO] XML list:'
    for idx, xml_prod in enumerate(xml_prods):
        print '[INFO] %3d. %s' % (idx + 1, xml_prod)

    return rsl_prods, xml_prods

def has_right_eye(prod):
    dir = os.path.dirname(prod)
    basename = os.path.basename(prod)
    basename = os.path.splitext(basename)[0]

    # file basename is parsed based on the naming convention in MER CAMERA SIS.
    r_fname = '%s%s%s' % (basename[:23], 'R', basename[24:])
    r_path = '%s/%s%s' % (dir, r_fname[:-1], '*.IMG')
    r_files = glob.glob(r_path)

    if len(r_files) > 0:
        return True
    else:
        return False

def is_highest_ver(prod, prods):
    basename = os.path.basename(prod)
    basename = os.path.splitext(basename)[0]
    same_prods = [p for p in prods if basename[:-1] in p]

    if len(same_prods) == 1:
        return True
    else:
        same_prods.sort()
        idx = same_prods.index(prod)
        if idx + 1 == len(same_prods):
            return True
        else:
            return False

def is_sun_image(prod):
    sun_identifier = 'P26[0-9][0-9]'
    basename = os.path.basename(prod)
    basename = os.path.splitext(basename)[0]
    seq = basename[18:23]

    match = re.search(sun_identifier, seq)
    if match:
        return True
    else:
        return False

def run_marsortho(img_lis, xyz_lis, working_dir, cs, z_cs, scale, cut):
    img_basename = os.path.basename(img_lis)
    img_basename = os.path.splitext(img_basename)[0]
    xyz_basename = os.path.basename(xyz_lis)
    xyz_basename = os.path.splitext(xyz_basename)[0]

    #marsortho = os.environ.get('MARSLIB') + '/marsortho'
    marsortho = '/home/mermipl/tools/sbin/marsortho' # tmp solution
    marsmap = os.environ.get('MARSLIB') + '/marsmap'
    marsbrt = os.environ.get('MARSLIB') + '/marsbrt'

    # Run marsmap to generate overlay file
    print '==================================================================='
    print '[INFO] Generate overlay statistics:'
    marsmap_command = marsmap + ' inp=%s out=%stmp.vic -norad ovr=%stmp.ovr ' \
        ' point_method=\'\"cm=label,pm=3dof,cahv_fov=min\"\'' % \
        (img_lis, working_dir, working_dir)
    print '[INFO] %s' % marsmap_command
    proc = subprocess.Popen(marsmap_command, stdout=subprocess.PIPE, shell=True)
    proc.communicate()

    # Run marsbrt to generate brightness correction file
    print '==================================================================='
    print '[INFO] Generate brightness correction file:'
    brt_out = working_dir + img_basename + '.BRT'
    marsbrt_command = marsbrt + ' %s %s in_ovr=%stmp.ovr out_sol=mipl_steven ' \
        ' inertia=\\(0.1 0.0001\\)' % (img_lis, brt_out, working_dir)
    print '[INFO] %s' % marsbrt_command
    proc = subprocess.Popen(marsbrt_command, stdout=subprocess.PIPE, shell=True)
    proc.communicate()

    # Run marsoerho to generate ORR and DEM
    print '==================================================================='
    print '[INFO] Generate ORR and DEM'
    orr_out = working_dir + img_basename + '.VIC'
    dem_out = working_dir + xyz_basename + '.VIC'
    marsortho_command = marsortho + ' inp=%s in_xyz=%s out=%s out_dem=%s brt=%s ' \
                                    'scale=%f cut=%f range=%f -%s z_coord=%s -first ' \
                                    '-circle fill_size=10 -erosion -norad -zup' % \
                                    (img_lis, xyz_lis, orr_out, dem_out,
                                     brt_out, scale, cut, cut, cs, z_cs)
    print '[INFO] %s' % marsortho_command
    proc = subprocess.Popen(marsortho_command, stdout=subprocess.PIPE, shell=True)
    proc.communicate()

    # clean up tmp files
    os.remove('%stmp.vic' % working_dir)
    os.remove('%stmp.ovr' % working_dir)

    return orr_out, dem_out

def check_select(variable_name):
    return os.environ.get(variable_name)

def create_dir(path):
    try:
        os.makedirs(path)
    except OSError as exception:
        if exception.errno != errno.EEXIST:
            raise

def atoi(text):
    return int(text) if text.isdigit() else text

def natural_keys(text):
    return [atoi(c) for c in re.split('(\d+)', text)]

if __name__ == '__main__':
    import argparse

    parser = argparse.ArgumentParser(description='''A wrapper script of VICAR
		program marsortho that generates ortho and DEM mosaics.''')
    parser.add_argument('-sol', '--sol', type=str, help='beginning sol')
    parser.add_argument('-e_sol', '--end_sol', type=str, help='ending sol')
    parser.add_argument('-inst', '--instrument', type=str, default='N',
                        help='instrument')
    parser.add_argument('-site', '--site', type=str, help='site')
    parser.add_argument('-pos', '--position', type=str, help='position')
    parser.add_argument('-seq', '--sequence', type=str, help='sequence')
    parser.add_argument('-filt', '--filter', type=str, help='filter')
    parser.add_argument('-who', '--who', type=str, default='T')
    parser.add_argument('-img', '--img_lis', type=str, help='path to image list')
    parser.add_argument('-xyz', '--xyz_lis', type=str, help='path to xyz list')
    parser.add_argument('-o', '--out_dir', type=str, help='output directory') 
    parser.add_argument('-cs', '--cs', type=str, default='local_level',
                        help='the coordinate system used to calculate geometries' \
                        'of ORR and DEM.')
    parser.add_argument('-z_cs', '--z_cs', type=str, default='local_level', help='the ' \
                        'coordinate system used to set Z values of DEM.')
    parser.add_argument('-scale', '--scale', type=float, default=0.01, help='scale')
    parser.add_argument('-cut', '--cut', type=float, default=35, help='cut')

    args = parser.parse_args()

    # validate input parameters
    # Note that marsortho parameters cs, z_cs, scale, and cut will be validated in 
    # marsortho program.
    validate_params(args)
 
    # start processing
    main(args)
