package com.elphel.imagej.tileprocessor;
/**
 **
 ** IntersceneLmaParameters - Class for handling multiple configuration parameters
 ** related to the interscene LMA 
 **
 ** Copyright (C) 2020 Elphel, Inc.
 **
 ** -----------------------------------------------------------------------------**
 **
 **  IntersceneLmaParameters.java 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.
 **
 **  This program 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 License
 **  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ** -----------------------------------------------------------------------------**
 **
 */

import java.util.Properties;

import com.elphel.imagej.common.GenericJTabbedDialog;

public class IntersceneLmaParameters {
	public boolean    ilma_thread_invariant =     true; // Do not use DoubleAdder, provide results not dependent on threads
	public boolean [] ilma_lma_select =             new boolean [ErsCorrection.DP_NUM_PARS]; // first three will not be used
	public double  [] ilma_regularization_weights = new double  [ErsCorrection.DP_NUM_PARS]; // first three will not be used
	public boolean    ilma_ignore_ers =        false; // ignore linear and angular velocities, assume tham zeroes
	public boolean    ilma_ers_adj_lin =       false; // adjust linear ERS for scene-to-ref (LWIR does not work, check with high-speed)
	public boolean    ilma_ers_adj_ang =       false; // adjust angular ERS  for scene-to-ref (LWIR does not work, check with high-speed)
	public double     ilma_lambda =            0.1;
	public double     ilma_lambda_scale_good = 0.5;
	public double     ilma_lambda_scale_bad =  8.0;
	public double     ilma_lambda_max =      100;
	public double     ilma_rms_diff =          0.001;
	public int        ilma_num_iter =          20;
	public int        ilma_num_corr =          10; // maximal number of full correlation+LMA cycles
	public int        ilma_debug_level =       1;
	public boolean    ilma_debug_adjust_series = false; // Debug images for series of pose and ers
	public boolean    ilma_debug_invariant =     false; // Monitoring variations when restarting program (should be ilma_thread_invariant=true)

	public IntersceneLmaParameters() {
		ilma_lma_select[ErsCorrection.DP_DVAZ]=  true;
		ilma_lma_select[ErsCorrection.DP_DVTL]=  true;
		ilma_lma_select[ErsCorrection.DP_DVRL]=  true;
		ilma_lma_select[ErsCorrection.DP_DVX]=   true;
		ilma_lma_select[ErsCorrection.DP_DVY]=   true;
		ilma_lma_select[ErsCorrection.DP_DVZ]=   true;
		ilma_lma_select[ErsCorrection.DP_DAZ]=   false;
		ilma_lma_select[ErsCorrection.DP_DTL]=   false;
		ilma_lma_select[ErsCorrection.DP_DRL]=   false;
		ilma_lma_select[ErsCorrection.DP_DX]=    false;
		ilma_lma_select[ErsCorrection.DP_DY]=    false;
		ilma_lma_select[ErsCorrection.DP_DZ]=    false;
		ilma_lma_select[ErsCorrection.DP_DSVAZ]= true;
		ilma_lma_select[ErsCorrection.DP_DSVTL]= true;
		ilma_lma_select[ErsCorrection.DP_DSVRL]= true;
		ilma_lma_select[ErsCorrection.DP_DSVX]=  true;
		ilma_lma_select[ErsCorrection.DP_DSVY]=  true;
		ilma_lma_select[ErsCorrection.DP_DSVZ]=  true;
		ilma_lma_select[ErsCorrection.DP_DSAZ]=  true;
		ilma_lma_select[ErsCorrection.DP_DSTL]=  true;
		ilma_lma_select[ErsCorrection.DP_DSRL]=  true;
		ilma_lma_select[ErsCorrection.DP_DSX]=   true;
		ilma_lma_select[ErsCorrection.DP_DSY]=   true;
		ilma_lma_select[ErsCorrection.DP_DSZ]=   true;
		
		ilma_regularization_weights[ErsCorrection.DP_DVAZ]=  0.001;
		ilma_regularization_weights[ErsCorrection.DP_DVTL]=  0.001;
		ilma_regularization_weights[ErsCorrection.DP_DVRL]=  0.001;
		ilma_regularization_weights[ErsCorrection.DP_DVX]=   0.001;
		ilma_regularization_weights[ErsCorrection.DP_DVY]=   0.001;
		ilma_regularization_weights[ErsCorrection.DP_DVZ]=   0.001;
		ilma_regularization_weights[ErsCorrection.DP_DAZ]=   0.0;
		ilma_regularization_weights[ErsCorrection.DP_DTL]=   0.0;
		ilma_regularization_weights[ErsCorrection.DP_DRL]=   0.0;
		ilma_regularization_weights[ErsCorrection.DP_DX]=    0.0;
		ilma_regularization_weights[ErsCorrection.DP_DY]=    0.0;
		ilma_regularization_weights[ErsCorrection.DP_DZ]=    0.0;
		ilma_regularization_weights[ErsCorrection.DP_DSVAZ]= 0.001;
		ilma_regularization_weights[ErsCorrection.DP_DSVTL]= 0.001;
		ilma_regularization_weights[ErsCorrection.DP_DSVRL]= 0.001;
		ilma_regularization_weights[ErsCorrection.DP_DSVX]=  0.001;
		ilma_regularization_weights[ErsCorrection.DP_DSVY]=  0.001;
		ilma_regularization_weights[ErsCorrection.DP_DSVZ]=  0.001;
		ilma_regularization_weights[ErsCorrection.DP_DSAZ]=  0.0;
		ilma_regularization_weights[ErsCorrection.DP_DSTL]=  0.0;
		ilma_regularization_weights[ErsCorrection.DP_DSRL]=  0.0;
		ilma_regularization_weights[ErsCorrection.DP_DSX]=   0.0;
		ilma_regularization_weights[ErsCorrection.DP_DSY]=   0.0;
		ilma_regularization_weights[ErsCorrection.DP_DSZ]=   0.0;
	}
	
	public void dialogQuestions(GenericJTabbedDialog gd) {
		gd.addMessage("Interframe LMA parameters selection");
	    gd.addCheckbox    ("Thread-invariant execution",           this.ilma_thread_invariant,
	    		"Do not use DoubleAdder and provide results not dependent on threads" );
		for (int i = ErsCorrection.DP_DVAZ; i < ErsCorrection.DP_NUM_PARS; i++) {
		    gd.addCheckbox    (ErsCorrection.DP_DERIV_NAMES[i],           this.ilma_lma_select[i],
		    		"Adjust parameter "+ErsCorrection.DP_DERIV_NAMES[i]+" with interscene LMA" );
		}
		gd.addMessage("Regularization parameters - pull strength to the initial values");
		for (int i = ErsCorrection.DP_DVAZ; i < ErsCorrection.DP_NUM_PARS; i++) {
			gd.addNumericField(ErsCorrection.DP_DERIV_NAMES[i],           this.ilma_regularization_weights[i], 6,8,"",
			"Weight of "+ErsCorrection.DP_DERIV_NAMES[i]+" pull, 1.0 means that the paramter offset from initial corresponding to 1 image pixel\n"+
			" will cause error equal to all reprojection ones");
		}
		gd.addMessage("LMA other parameters");
		gd.addCheckbox    ("Ignore linear and angular velocities",        this.ilma_ignore_ers,
	    		"Ignore calculated linear and angular velocities when correlating scenes to the reference one" );
		
		gd.addCheckbox    ("Adjust linear ERS for scene-to-ref",          this.ilma_ers_adj_lin,
	    		"Adjust linear velocities during LMA for scene-to-reference matching. So far does not work for LWIR (not stable - effect is samll)" );
		gd.addCheckbox    ("Aadjust angular ERS for scene-to-ref",        this.ilma_ers_adj_ang,
	    		"Adjust angular velocities during LMA for scene-to-reference matching. So far does not work for LWIR (not stable - effect is samll)" );
		
		gd.addNumericField("LMA lambda",                                  this.ilma_lambda, 6,8,"",
				"Initial value of the LMA lambda");
		gd.addNumericField("Scale lambda after successful LMA iteration", this.ilma_lambda_scale_good, 3,5,"",
				"Scale lambda (reduce) if the new RMSE is lower than the previous one.");
		gd.addNumericField("Scale lambda after failed LMA iteration",     this.ilma_lambda_scale_bad, 3,5,"",
				"Scale lambda (increase) if the new RMSE is higher than the previous one.");
		gd.addNumericField("Maximal value of lambda to try",              this.ilma_lambda_max, 2,7,"",
				"Fail LMA if the result is still worse than before parameters were updates.");
		gd.addNumericField("Minimal relative RMSE improvement",           this.ilma_rms_diff, 5,7,"",
				"Exit LMA iterations if relative RMSE improvement drops below this value.");
		gd.addNumericField("Maximal number of LMA iterations",            this.ilma_num_iter, 0,3,"",
				"A hard limit on LMA iterations.");
		gd.addNumericField("Maximal number of correlation +LMA iterations",this.ilma_num_corr, 0,3,"",
				"Outer cycle (recalculate correlations + lma). Otherwise exits if LMA exits at first iteration.");
	    gd.addCheckbox    ("Debug adjust_series()",                       this.ilma_debug_adjust_series,
	    		"Generate debug images for series of pose and ERS (derivatives of pose)" );
	    gd.addCheckbox    ("Debug thread-invariant",                      this.ilma_debug_invariant,
	    		"Generate debug images and text output to verify same results regardless of threads" );
		gd.addNumericField("Debug level",                                 this.ilma_debug_level, 0,3,"",
				"Debug level of interscene LMA operation.");
	}
	
	public void dialogAnswers(GenericJTabbedDialog gd) {
		this.ilma_thread_invariant =              gd.getNextBoolean();
		for (int i = ErsCorrection.DP_DVAZ; i < ErsCorrection.DP_NUM_PARS; i++) {
		    this.ilma_lma_select[i] =             gd.getNextBoolean();
		}
		for (int i = ErsCorrection.DP_DVAZ; i < ErsCorrection.DP_NUM_PARS; i++) {
			this.ilma_regularization_weights[i] = gd.getNextNumber();
		}
		this.ilma_ignore_ers =                    gd.getNextBoolean();
		this.ilma_ers_adj_lin =                   gd.getNextBoolean();
		this.ilma_ers_adj_ang =                   gd.getNextBoolean();
		this.ilma_lambda =                        gd.getNextNumber();
		this.ilma_lambda_scale_good =             gd.getNextNumber();
		this.ilma_lambda_scale_bad =              gd.getNextNumber();
		this.ilma_lambda_max =                    gd.getNextNumber();
		this.ilma_rms_diff =                      gd.getNextNumber();
		this.ilma_num_iter =                (int) gd.getNextNumber();
		this.ilma_num_corr =                (int) gd.getNextNumber();
		this.ilma_debug_adjust_series =           gd.getNextBoolean();
		this.ilma_debug_invariant =               gd.getNextBoolean();
		this.ilma_debug_level =             (int) gd.getNextNumber();
	}
	public void setProperties(String prefix,Properties properties){
		properties.setProperty(prefix+"ilma_thread_invariant",  this.ilma_thread_invariant+"");	
		for (int i = ErsCorrection.DP_DVAZ; i < ErsCorrection.DP_NUM_PARS; i++) {
			properties.setProperty(prefix+ErsCorrection.DP_DERIV_NAMES[i]+"_sel",           this.ilma_lma_select[i]+"");	
			properties.setProperty(prefix+ErsCorrection.DP_DERIV_NAMES[i]+"_regweight",     this.ilma_regularization_weights[i]+"");	
		}
		properties.setProperty(prefix+"ilma_ignore_ers",         this.ilma_ignore_ers+"");	
		properties.setProperty(prefix+"ilma_ers_adj_lin",        this.ilma_ers_adj_lin+"");	
		properties.setProperty(prefix+"ilma_ers_adj_ang",        this.ilma_ers_adj_ang+"");	
		properties.setProperty(prefix+"ilma_lambda",             this.ilma_lambda+"");	
		properties.setProperty(prefix+"ilma_lambda_scale_good",  this.ilma_lambda_scale_good+"");	
		properties.setProperty(prefix+"ilma_lambda_scale_bad",   this.ilma_lambda_scale_bad+"");	
		properties.setProperty(prefix+"ilma_lambda_max",         this.ilma_lambda_max+"");	
		properties.setProperty(prefix+"ilma_rms_diff",           this.ilma_rms_diff+"");	
		properties.setProperty(prefix+"ilma_num_iter",           this.ilma_num_iter+"");	
		properties.setProperty(prefix+"ilma_num_corr",           this.ilma_num_corr+"");	
		properties.setProperty(prefix+"ilma_debug_adjust_series",this.ilma_debug_adjust_series+"");	
		properties.setProperty(prefix+"ilma_debug_invariant",    this.ilma_debug_invariant+"");	
		properties.setProperty(prefix+"ilma_debug_level",        this.ilma_debug_level+"");	
	}
	public void getProperties(String prefix,Properties properties){
		if (properties.getProperty(prefix+"ilma_thread_invariant")!=null)  this.ilma_thread_invariant=Boolean.parseBoolean(properties.getProperty(prefix+"ilma_thread_invariant"));
		for (int i = ErsCorrection.DP_DVAZ; i < ErsCorrection.DP_NUM_PARS; i++) {
			String pn_sel = prefix+ErsCorrection.DP_DERIV_NAMES[i]+"_sel";
			if (properties.getProperty(pn_sel)!=null) this.ilma_lma_select[i]=Boolean.parseBoolean(properties.getProperty(pn_sel));
			pn_sel = prefix+ErsCorrection.DP_DERIV_NAMES[i]+"_regweight";
			if (properties.getProperty(pn_sel)!=null) this.ilma_regularization_weights[i]=Double.parseDouble(properties.getProperty(pn_sel));
		}
		if (properties.getProperty(prefix+"ilma_ignore_ers")!=null)         this.ilma_ignore_ers=Boolean.parseBoolean(properties.getProperty(prefix+"ilma_ignore_ers"));
		if (properties.getProperty(prefix+"ilma_ers_adj_lin")!=null)        this.ilma_ers_adj_lin=Boolean.parseBoolean(properties.getProperty(prefix+"ilma_ers_adj_lin"));
		if (properties.getProperty(prefix+"ilma_ers_adj_ang")!=null)        this.ilma_ers_adj_ang=Boolean.parseBoolean(properties.getProperty(prefix+"ilma_ers_adj_ang"));
		if (properties.getProperty(prefix+"ilma_lambda")!=null)             this.ilma_lambda=Double.parseDouble(properties.getProperty(prefix+"ilma_lambda"));
		if (properties.getProperty(prefix+"ilma_lambda_scale_good")!=null)  this.ilma_lambda_scale_good=Double.parseDouble(properties.getProperty(prefix+"ilma_lambda_scale_good"));
		if (properties.getProperty(prefix+"ilma_lambda_scale_bad")!=null)   this.ilma_lambda_scale_bad=Double.parseDouble(properties.getProperty(prefix+"ilma_lambda_scale_bad"));
		if (properties.getProperty(prefix+"ilma_lambda_max")!=null)         this.ilma_lambda_max=Double.parseDouble(properties.getProperty(prefix+"ilma_lambda_max"));
		if (properties.getProperty(prefix+"ilma_rms_diff")!=null)           this.ilma_rms_diff=Double.parseDouble(properties.getProperty(prefix+"ilma_rms_diff"));
		if (properties.getProperty(prefix+"ilma_num_iter")!=null)           this.ilma_num_iter=Integer.parseInt(properties.getProperty(prefix+"ilma_num_iter"));
		if (properties.getProperty(prefix+"ilma_num_corr")!=null)           this.ilma_num_corr=Integer.parseInt(properties.getProperty(prefix+"ilma_num_corr"));
		if (properties.getProperty(prefix+"ilma_debug_adjust_series")!=null)this.ilma_debug_adjust_series=Boolean.parseBoolean(properties.getProperty(prefix+"ilma_debug_adjust_series"));
		if (properties.getProperty(prefix+"ilma_debug_invariant")!=null)    this.ilma_debug_invariant=Boolean.parseBoolean(properties.getProperty(prefix+"ilma_debug_invariant"));
		if (properties.getProperty(prefix+"ilma_debug_level")!=null)        this.ilma_debug_level=Integer.parseInt(properties.getProperty(prefix+"ilma_debug_level"));
	}
	
	@Override
	public IntersceneLmaParameters clone() throws CloneNotSupportedException {
		IntersceneLmaParameters ilp =     new IntersceneLmaParameters();
		ilp.ilma_thread_invariant =  this.ilma_thread_invariant;
		System.arraycopy(this.ilma_lma_select,             0, ilp.ilma_lma_select,             0, ilma_lma_select.length);
		System.arraycopy(this.ilma_regularization_weights, 0, ilp.ilma_regularization_weights, 0, ilma_regularization_weights.length);
		ilp.ilma_ignore_ers =          this.ilma_ignore_ers;
		ilp.ilma_ers_adj_lin =         this.ilma_ers_adj_lin;
		ilp.ilma_ers_adj_ang =         this.ilma_ers_adj_ang;
		ilp.ilma_lambda =              this.ilma_lambda;
		ilp.ilma_lambda_scale_good =   this.ilma_lambda_scale_good;
		ilp.ilma_lambda_scale_bad =    this.ilma_lambda_scale_bad;
		ilp.ilma_lambda_max =          this.ilma_lambda_max;
		ilp.ilma_rms_diff =            this.ilma_rms_diff;
		ilp.ilma_num_iter =            this.ilma_num_iter;
		ilp.ilma_num_corr =            this.ilma_num_corr;
		ilp.ilma_debug_adjust_series = this.ilma_debug_adjust_series;
		ilp.ilma_debug_invariant =     this.ilma_debug_invariant;
		ilp.ilma_debug_level =         this.ilma_debug_level;
		return ilp;
	}	
}