#!/usr/bin/env python

# Copyright (c) 2013. The EFIDIR team. All right reserved.
#
# This file is part of EFIDIR tools.
#
# EFIDIR tool(s) is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# EFIDIR tool(s) is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public licence
# along with EFIDIR tools.  If not, see <http://www.gnu.org/licenses/>.
#
# author: Matthieu Volat / ISTerre
#

import sys, datetime

from efidir.config import *
from efidir.param import *
from efidir.sws import *
from efidir.utils import tsx, pairing

# Parameters
define_param("input-datefile", STRING, "input file with date list (one per line)", None)
define_optional_param("njobs", INT, "start up to n concurrent jobs", None)
define_optional_param("verbose", INT, "display as much info as possible", None)
init_param(sys.argv)
verbose = get_int_value_param("verbose")

# Read input date file
dates = [l.rstrip() for l in open(get_string_value_param("input-datefile"))]
# From it, create XML and COS tables, as well as asc and des lists
xml = {}
cos = {}
dates_asc = []
dates_des = []
for d in dates:
	infos = tsx.findinfos(d, "input/TSX")
	xml[d] = infos["xml"]
	cos[d] = infos["cos"]
	if infos["orbit_direction"] == "ASCENDING":
		dates_asc.append(d)
	else:
		dates_des.append(d)
# Input dates arranged as master/slave pairs
dates_pairs_asc = pairing.makepairs_leapfrog(dates_asc)
dates_pairs_des = pairing.makepairs_leapfrog(dates_des)
# All pairs, regardless of orientation
dates_pairs = dates_pairs_asc + dates_pairs_des

# Target list to be built
targets = []

# 1.1 Create azimuth and range images from sat input and DEM
for d in dates:
	outputs = [
		"output/ground/gen_azimuth_range_imgs_user_precision_param_"+d+".save",
		"output/ground/azimuth_"+d,
		"output/ground/azimuth_"+d+".hdr",
		"output/ground/range_"+d,
		"output/ground/range_"+d+".hdr" ]
	inputs = [
		xml[d],
		"input/GIS/up_corrected_mnt_rgd_arg_taco_2008_ll",
		"input/GIS/up_corrected_mnt_rgd_arg_taco_2008_ll.hdr" ]
	commands = [
		EFIDIR_BINDIR+"/gen_azimuth_range_imgs_user_precision"
			+"  --save "+outputs[0]
			+"  --file_type 0"
			+"  --dist_between_points 1"
			+"  --pol_deg 7"
			+"  --ignored_val 0"
			+"  --input_metadata_file "+inputs[0]
			+"  --dem_name "+inputs[1]
			+"  --azimuth_img "+outputs[1]
			+"  --range_img "+outputs[3] ]
	targets.append(target_create(commands, inputs, outputs))

# 1.2 Crop the InSAR images to the zone covered by the DEM
for d in dates:
	subdir = "asc" if (d in dates_asc) else "des"
	outputs = [
		"output/"+subdir+"/crop_SAR_image4specific_DEM_param_"+d+".save",
		"output/"+subdir+"/slc_region_"+d,
		"output/"+subdir+"/slc_region_"+d+".hdr" ]
	inputs = [
		cos[d],
		"output/ground/azimuth_"+d,
		"output/ground/azimuth_"+d+".hdr",
		"output/ground/range_"+d,
		"output/ground/range_"+d+".hdr" ]
	commands = [
		EFIDIR_BINDIR+"/crop_SAR_image4specific_DEM"
			+"  --save "+outputs[0]
			+"  --ignored_val 0"
			+"  --input_SAR "+inputs[0]
			+"  --input_azimuth "+inputs[1]
			+"  --input_range "+inputs[3]
			+"  --additional_nof_lines 0"
			+"  --additional_nof_columns 0"
			+"  --output_cropped_SAR "+outputs[1] ]
	targets.append(target_create(commands, inputs, outputs))

# 1.3 SLC -> amplitude conversion
for d in dates:
	subdir = "asc" if (d in dates_asc) else "des"
	outputs = [
		"output/"+subdir+"/cal_slc_param_"+d+".save",
		"output/"+subdir+"/ampv0_region_"+d,
		"output/"+subdir+"/ampv0_region_"+d+".hdr" ]
	inputs = [
		"output/"+subdir+"/slc_region_"+d,
		"output/"+subdir+"/slc_region_"+d+".hdr" ]
	commands = [
		EFIDIR_BINDIR+"/cal_slc"
			+"  --save "+outputs[0]
			+"  --input "+inputs[0]
			+"  --output "+outputs[1]
			+"  --processing 1"
			+"  --description 0" ]
	targets.append(target_create(commands, inputs, outputs))

# 1.4.a Determine common size between image series, first for asc then des
outputs = [ "output/asc/common_pad_size.txt" ]
inputs = []
for d in dates_asc:
	inputs.append("output/asc/ampv0_region_"+d+".hdr")
commands = [
	"awk 'BEGIN { FS=\" *= *\"; maxlines=0; maxsamples=0; } "
		+" { if ($$1~/lines/ && $$2>maxlines) { maxlines=$$2 }; "
		+"   if ($$1~/samples/ && $$2>maxsamples) { maxsamples=$$2 } } "
		+"END { print \"--nrows \"maxlines\"  --ncols \"maxsamples } "
		+" ' "+" ".join(inputs)+" > "+outputs[0] ]
targets.append(target_create(commands, inputs, outputs))
outputs = [ "output/des/common_pad_size.txt" ]
inputs = []
for d in dates_des:
	inputs.append("output/des/ampv0_region_"+d+".hdr")
commands = [
	"awk 'BEGIN { FS=\" *= *\"; maxlines=0; maxsamples=0; } "
		+" { if ($$1~/lines/ && $$2>maxlines) { maxlines=$$2 }; if ($$1~/samples/ && $$2>maxsamples) { maxsamples=$$2 } } "
		+" END { print \"--nrows \"maxlines\"  --ncols \"maxsamples } "
		+" ' "+" ".join(inputs)+" > "+outputs[0] ]
targets.append(target_create(commands, inputs, outputs))
# 1.4.b Then resize
for d in dates:
	subdir = "asc" if (d in dates_asc) else "des"
	outputs = [
		"output/"+subdir+"/amp_region_param_"+d+".save",
		"output/"+subdir+"/amp_region_"+d,
		"output/"+subdir+"/amp_region_"+d+".hdr" ]
	inputs = [
		"output/"+subdir+"/ampv0_region_"+d,
		"output/"+subdir+"/ampv0_region_"+d+".hdr",
		"output/"+subdir+"/common_pad_size.txt" ]
	commands = [
		EFIDIR_BINDIR+"/zeropad"
			+"  --save "+outputs[0]
			+"  --input "+inputs[0]
			+"  --output "+outputs[1]
			+"  --desc \"0\""
			+"  `cat "+inputs[2]+"`" ]
	targets.append(target_create(commands, inputs, outputs))

# 2.1 Project SAR image in orthogonal geometry
for d in dates:
	subdir = "asc" if (d in dates_asc) else "des"
	outputs = [
		"output/ground/interpol_azim_range_radar2ground_param_"+d+".save",
		"output/ground/region_ortho_amp_"+d,
		"output/ground/region_ortho_amp_"+d+".hdr" ]
	inputs = [
		"output/"+subdir+"/amp_region_"+d,
		"output/"+subdir+"/amp_region_"+d+".hdr",
		"output/ground/azimuth_"+d,
		"output/ground/azimuth_"+d+".hdr",
		"output/ground/range_"+d,
		"output/ground/range_"+d+".hdr" ]
	commands = [
		EFIDIR_BINDIR+"/interpol_azim_range_radar2ground"
			+"  --save "+outputs[0]
			+"  --amplitude_img_name "+inputs[0]
			+"  --azimuth_img_name "+inputs[2]
			+"  --range_img_name "+inputs[4]
			+"  --interpolation_type 1"
			+"  --input_ignored_value 0"
			+"  --output_ignored_value 0"
			+"  --amplitude_2_ground_img_name "+outputs[1] ]
	targets.append(target_create(commands, inputs, outputs))

# 2.2 Project latitude, longitude and elevation in SAR geometry
for d in dates:
	subdir = "asc" if (d in dates_asc) else "des"
	outputs = [
		"output/"+subdir+"/gen_LUTs_on_master_grid_param_"+d+".save",
		"output/"+subdir+"/lat_region_"+d,
		"output/"+subdir+"/lat_region_"+d+".hdr",
		"output/"+subdir+"/long_region_"+d,
		"output/"+subdir+"/long_region_"+d+".hdr",
		"output/"+subdir+"/region_up_"+d,
		"output/"+subdir+"/region_up_"+d+".hdr",
		"output/"+subdir+"/region_foldovermask_"+d ]
	inputs = [
		"input/GIS/up_corrected_mnt_rgd_arg_taco_2008_ll",
		"input/GIS/up_corrected_mnt_rgd_arg_taco_2008_ll.hdr",
		"output/"+subdir+"/amp_region_"+d,
		"output/"+subdir+"/amp_region_"+d+".hdr",
		"output/ground/azimuth_"+d,
		"output/ground/azimuth_"+d+".hdr",
		"output/ground/range_"+d,
		"output/ground/range_"+d+".hdr" ]
	commands = [
		EFIDIR_BINDIR+"/gen_LUTs_on_master_grid"
			+"  --save "+outputs[0]
			+"  --ignored_val 0"
			+"  --dem_name "+inputs[0]
			+"  --track_type %d" % (0 if (dates.index(d) % 2 == 0) else 1)
			+"  --input_SAR_master "+inputs[2]
			+"  --input_range_master "+inputs[6]
			+"  --input_azimuth_master "+inputs[4]
			+"  --output_lat "+outputs[1]
			+"  --output_long "+outputs[3]
			+"  --output_alt "+outputs[5]
			+"  --output_mask "+outputs[7] ]
	targets.append(target_create(commands, inputs, outputs))

# 2.3 Projection of the geocoded image in SAR geometry
for d in [dates_asc[0]] if len(dates_asc)>0 else [] + [dates_des[0]] if len(dates_des)>0 else []:
	subdir = "asc" if (d in dates_asc) else "des"
	outputs = [
		"output/"+subdir+"/interpol_lut_ground2radar_multiband_param_"+d+".save",
		"output/"+subdir+"/aero_region_"+d,
		"output/"+subdir+"/aero_region_"+d+".hdr" ]
	inputs = [
		"input/GIS/aeroporte_rgd_2008_4m_ll",
		"input/GIS/aeroporte_rgd_2008_4m_ll.hdr",
		"output/"+subdir+"/lat_region_"+d,
		"output/"+subdir+"/lat_region_"+d+".hdr",
		"output/"+subdir+"/long_region_"+d,
		"output/"+subdir+"/long_region_"+d+".hdr" ]
	commands = [
		EFIDIR_BINDIR+"/interpol_lut_ground2radar_multiband"
			+"  --save "+outputs[0]
			+"  --input_image "+inputs[0]
			+"  --nof_bands 1"
			+"  --input_lat "+inputs[2]
			+"  --input_long  "+inputs[4]
			+"  --interpolation_type 1"
			+"  --input_ignored_values 0"
			+"  --output_ignored_values 0"
			+"  --output_image "+outputs[1] ]
	targets.append(target_create(commands, inputs, outputs))

# 3.1.a Compute range/azimuth offset between master/slaves
for md, d in dates_pairs:
	subdir = "asc" if (d in dates_asc) else "des"
	track_arg = "0" if (d in dates_asc) else "1"
	outputs = [
		"output/"+subdir+"/gen_delta_azimuth_range_master_slave_"+md+"-"+d+".save",
		"output/"+subdir+"/delta_azimuth0_region_"+md+"-"+d,
		"output/"+subdir+"/delta_azimuth0_region_"+md+"-"+d+".hdr",
		"output/"+subdir+"/delta_range0_region_"+md+"-"+d,
		"output/"+subdir+"/delta_range0_region_"+md+"-"+d+".hdr" ]
	inputs = [
		"output/"+subdir+"/amp_region_"+md,
		"output/"+subdir+"/amp_region_"+md+".hdr",
		"output/ground/azimuth_"+md,
		"output/ground/azimuth_"+md+".hdr",
		"output/ground/range_"+md,
		"output/ground/range_"+md+".hdr",
		"output/ground/azimuth_"+d,
		"output/ground/azimuth_"+d+".hdr",
		"output/ground/range_"+d,
		"output/ground/range_"+d+".hdr" ]
	commands = [
		EFIDIR_BINDIR+"/gen_delta_azimuth_range_master_slave"
			+"  --save "+outputs[0]
			+"  --ignored_val 0"
			+"  --track_type "+track_arg
			+"  --input_SAR_master "+inputs[0]
			+"  --input_range_master "+inputs[4]
			+"  --input_range_slave "+inputs[8]
			+"  --input_azimuth_master "+inputs[2]
			+"  --input_azimuth_slave "+inputs[6]
			+"  --output_delta_azimuth "+outputs[1]
			+"  --output_delta_range "+outputs[3] ]
	targets.append(target_create(commands, inputs, outputs))
# 3.1.b Some delta images may be cropped, pad them (as amplitude before)
for md, d in dates_pairs:
	for direction in ["azimuth", "range"]:
		subdir = "asc" if (d in dates_asc) else "des"
		outputs = [
			"output/"+subdir+"/delta_"+direction+"_region_param_"+d+".save",
			"output/"+subdir+"/delta_"+direction+"_region_"+md+"-"+d,
			"output/"+subdir+"/delta_"+direction+"_region_"+md+"-"+d+".hdr"]
		inputs = [
			"output/"+subdir+"/delta_"+direction+"0_region_"+md+"-"+d,
			"output/"+subdir+"/delta_"+direction+"0_region_"+md+"-"+d+".hdr",
			"output/"+subdir+"/common_pad_size.txt" ]
		commands = [
			EFIDIR_BINDIR+"/zeropad"
				+"  --save "+outputs[0]
				+"  --input "+inputs[0]
				+"  --output "+outputs[1]
				+"  --desc \"0\""
				+"  `cat "+inputs[2]+"`" ]
		targets.append(target_create(commands, inputs, outputs))

# 3.2.a Normalized crosscorrelation between masters/slaves
for md, d in dates_pairs:
	subdir = "asc" if (d in dates_asc) else "des"
	outputs = [
		"output/"+subdir+"/dist_corr_"+md+"_"+d+".save",
		"output/"+subdir+"/region_vecv0_"+md+"_"+d,
		"output/"+subdir+"/region_vecv0_"+md+"_"+d+".hdr",
		"output/"+subdir+"/region_peakv0_"+md+"_"+d,
		"output/"+subdir+"/region_peakv0_"+md+"_"+d+".hdr",
		"output/"+subdir+"/region_fwhmv0_"+md+"_"+d,
		"output/"+subdir+"/region_fwhmv0_"+md+"_"+d+".hdr" ]
	inputs = [
		"output/"+subdir+"/amp_region_"+md,
		"output/"+subdir+"/amp_region_"+md+".hdr",
		"output/"+subdir+"/amp_region_"+d,
		"output/"+subdir+"/amp_region_"+d+".hdr" ]
	commands = [
		EFIDIR_PREFIX+"/bin/dist_corr"
			+" --save "+outputs[0]
			+" --masterFileName "+inputs[0]
			+" --slaveFileName "+inputs[2]
			+" --first_line 0"
			+" --displacementFileName "+outputs[1]
			+" --correlationPeakFileName "+outputs[3]
			+" --correlationFWHMFileName "+outputs[5]
			+" --window_nb_rows 65"
			+" --window_search_nb_rows 105"
			+" --window_search_nb_columns 95"
			+" --optimization 3"
			+" --sub_pixel 1" ]
	targets.append(target_create(commands, inputs, outputs))
# 3.2.b Zeropad the results to compensate for border discarded by distcorr
for md, d in dates_pairs:
	subdir = "asc" if (d in dates_asc) else "des"
	fwhm_outputs = [
		"output/"+subdir+"/region_fwhm_"+md+"_"+d+".save",
		"output/"+subdir+"/region_fwhm_"+md+"_"+d,
		"output/"+subdir+"/region_fwhm_"+md+"_"+d+".hdr" ]
	fwhm_inputs = [
		"output/"+subdir+"/region_fwhmv0_"+md+"_"+d,
		"output/"+subdir+"/region_fwhmv0_"+md+"_"+d+".hdr",
		"output/"+subdir+"/common_pad_size.txt" ]
	fwhm_commands = [
		EFIDIR_BINDIR+"/zeropad"
			+"  --save "+fwhm_outputs[0]
			+"  --input "+fwhm_inputs[0]
			+"  --output "+fwhm_outputs[1]
			+"  `cat "+fwhm_inputs[2]+"`"
            +"  --yoff 52" ]
	targets.append(target_create(fwhm_commands, fwhm_inputs, fwhm_outputs))
	peak_outputs = [
		"output/"+subdir+"/region_"+md+"_"+d+"_peak.save",
		"output/"+subdir+"/region_"+md+"_"+d+"_peak",
		"output/"+subdir+"/region_"+md+"_"+d+"_peak.hdr" ]
	peak_inputs = [
		"output/"+subdir+"/region_peakv0_"+md+"_"+d,
		"output/"+subdir+"/region_peakv0_"+md+"_"+d+".hdr",
		"output/"+subdir+"/common_pad_size.txt" ]
	peak_commands = [
		EFIDIR_BINDIR+"/zeropad"
			+"  --save "+peak_outputs[0]
			+"  --input "+peak_inputs[0]
			+"  --output "+peak_outputs[1]
			+"  `cat "+peak_inputs[2]+"`"
            +"  --yoff 52" ]
	targets.append(target_create(peak_commands, peak_inputs, peak_outputs))
	vec_outputs = [
		"output/"+subdir+"/region_vec_"+md+"_"+d+".save",
		"output/"+subdir+"/region_vec_"+md+"_"+d,
		"output/"+subdir+"/region_vec_"+md+"_"+d+".hdr" ]
	vec_inputs = [
		"output/"+subdir+"/region_vecv0_"+md+"_"+d,
		"output/"+subdir+"/region_vecv0_"+md+"_"+d+".hdr",
		"output/"+subdir+"/common_pad_size.txt" ]
	vec_commands = [
		EFIDIR_BINDIR+"/zeropad"
			+"  --save "+vec_outputs[0]
			+"  --input "+vec_inputs[0]
			+"  --output "+vec_outputs[1]
			+"  `cat "+vec_inputs[2]+"`"
            +"  --yoff 52" ]
	targets.append(target_create(vec_commands, vec_inputs, vec_outputs))

# 3.3.a Compute azimuth and range translation params for each master/slave pair
for md, d in dates_pairs:
	subdir = "asc" if (d in dates_asc) else "des"
	outputs = [ 
		"output/"+subdir+"/translation_param_"+md+"_"+d+".txt" ]
	inputs = [
		"output/"+subdir+"/amp_region_"+md+".hdr",
		"output/"+subdir+"/amp_region_"+d+".hdr" ]
	commands = [
		"awk 'BEGIN { FS=\" *= *\"; mxstart=0; mystart=0; xcount=0; sxstart=0; systart=0; } "
			+"{ if ($$1~/x start/) { "
	        +"    if (xcount==0) {mxstart=$$2; xcount=1} else {sxstart=$$2} "
	        +"  }; "
	        +"  if ($$1~/y start/) { "
	        +"    if (ycount==0) {mystart=$$2; ycount=1} else {systart=$$2} "
			+"  }; "
	        +"} END { "
	        +"  rtrans=sxstart-mxstart; atrans=systart-mystart;" 
	        +"  print \"--range_translation \"rtrans\"  --azimuth_translation \"atrans } "
			+"' "+" ".join(inputs) +" > "+outputs[0] ]
	targets.append(target_create(commands, inputs, outputs))
# 3.3.b Conversion of pixel offsets into 2D displacement
for md, d in dates_pairs:
	subdir = "asc" if (d in dates_asc) else "des"
	tdelta = (datetime.datetime.strptime(d, "%Y%m%d") - datetime.datetime.strptime(md, "%Y%m%d")).days
	outputs = [ 
		"output/"+subdir+"/cal_dpi_ori_"+md+"_"+d+".save",
	    "output/"+subdir+"/region_"+md+"_"+d+"_dpl",
	    "output/"+subdir+"/region_"+md+"_"+d+"_dpl.hdr",
	    "output/"+subdir+"/region_"+md+"_"+d+"_ori",
	    "output/"+subdir+"/region_"+md+"_"+d+"_ori.hdr",
	    "output/"+subdir+"/region_"+md+"_"+d+"_dplx",
	    "output/"+subdir+"/region_"+md+"_"+d+"_dplx.hdr",
	    "output/"+subdir+"/region_"+md+"_"+d+"_dply",
	    "output/"+subdir+"/region_"+md+"_"+d+"_dply.hdr",
	    "output/"+subdir+"/region_"+md+"_"+d+"_sigmax",
	    "output/"+subdir+"/region_"+md+"_"+d+"_sigmax.hdr",
	    "output/"+subdir+"/region_"+md+"_"+d+"_sigmay",
	    "output/"+subdir+"/region_"+md+"_"+d+"_sigmay.hdr" ]
	inputs = [
		"output/"+subdir+"/region_vec_"+md+"_"+d,
		"output/"+subdir+"/region_vec_"+md+"_"+d+".hdr",
		"output/"+subdir+"/delta_range_region_"+md+"-"+d,
		"output/"+subdir+"/delta_range_region_"+md+"-"+d+".hdr",
		"output/"+subdir+"/delta_azimuth_region_"+md+"-"+d,
		"output/"+subdir+"/delta_azimuth_region_"+md+"-"+d+".hdr",
		"output/"+subdir+"/translation_param_"+md+"_"+d+".txt",
		"output/"+subdir+"/region_fwhm_"+md+"_"+d,
		"output/"+subdir+"/region_fwhm_"+md+"_"+d+".hdr" ]
	commands = [
		EFIDIR_PREFIX+"/bin/cal_dpl_ori"
			+"  --save "+outputs[0]
			+"  --input_1 "+inputs[0]
			+"  --input_2 "+inputs[2]
			+"  --input_5 "+inputs[7]
	        +"  --output_1 "+outputs[1]
	        +"  --output_2 "+outputs[3]
	        +"  --output_3 "+outputs[5]
	        +"  --output_4 "+outputs[7]
	        +"  --output_5 "+outputs[9]
	        +"  --output_6 "+outputs[11]
			+"  `cat "+inputs[6]+"`" # range/azimuth translation
	        +"  --azimuth_offset_res 0"
	        +"  --range_px 1.36410540008545"
	        +"  --azimuth_px 1.98064713660364311"
	        +"  --temporal_distance "+str(tdelta)
	        +"  --ignored_val_choice 2"
			+"  --verbose "+str(verbose) ]
	targets.append(target_create(commands, inputs, outputs))

set_njobs(get_int_value_param("njobs"))
chain = chain_create("Chamonix.mk", targets)
chain_run(chain)
