package com.elphel.imagej.tileprocessor;

import java.util.Arrays;
import java.util.Properties;

import com.elphel.imagej.common.GenericJTabbedDialog;

public class IntersceneGlobalLmaParameters {
	public static final double DEFAULT_GLOB_PCG_TOLERANCE = 1.0e-7;
	public boolean    glob_en;
	public boolean    glob_exit_after_test; // exit OpticalFlow.buildSeries() immediately after running, do not increment num_orient
	public int        glob_solver_mode; // 0 - current sparse global refine, 1 - classic LMA-structure implementation
	public boolean [] param_sel;
	public double []  param_regweights;
	public double []  param_lpf;
	public double     glob_lambda;
	public double     glob_lambda_scale_good;
	public double     glob_lambda_scale_bad;
	public double     glob_lambda_max;
	public double     glob_rms_diff;
	public int        glob_num_iter;
	public int        glob_num_corr;
	public int        glob_pcg_iter;
	public double     glob_pcg_tolerance;
	public double     glob_delta_stop;
	public double     glob_smooth_weight_atr;
	public double     glob_smooth_weight_xyz;
	public double     glob_min_offset;
	public double     glob_max_offset;
	public boolean    glob_freeze_rate_chain_in_inner;
	public boolean    glob_test_inner_results_csv;
	public boolean    glob_run_one_more_outer_after_converged;
	public boolean    glob_debug_dump_observation_hyperstack;
	public boolean    glob_debug_show_observation_hyperstack;
	public boolean    glob_save_initial_final_only;
	public int        glob_center_pair_weight_mode;
	
	public IntersceneGlobalLmaParameters() {
		glob_en =                                   true;
		glob_exit_after_test =                      true; // TODO: change default to false when debugging is over
		glob_solver_mode =                          0;
		param_sel =                                 new boolean [ErsCorrection.DP_XYZATR.length];
		param_regweights =                          new double [ErsCorrection.DP_XYZATR.length];
		param_lpf =                                 new double [ErsCorrection.DP_XYZATR.length];
		Arrays.fill(param_sel,        true);
		Arrays.fill(param_regweights, 0.0);
		Arrays.fill(param_lpf,        0.1);// what are the reasonable values?
		glob_lambda =                               0.1;
		glob_lambda_scale_good =                    0.5;
		glob_lambda_scale_bad =                     8.0;
		glob_lambda_max =                         100.0;
		glob_rms_diff =                             0.001;
		glob_num_iter =                             8;
		glob_num_corr =                             0; // <=0: use imp.max_cycles
		glob_pcg_iter =                             0; // <=0: use ilp.ilma_num_iter*8
		glob_pcg_tolerance =                        DEFAULT_GLOB_PCG_TOLERANCE;
		glob_delta_stop =                           1.0e-5;
		glob_smooth_weight_atr =                    1.0;
		glob_smooth_weight_xyz =                    0.15;
		glob_min_offset =                           Double.NaN; // NaN: use imp.min_offset
		glob_max_offset =                           Double.NaN; // NaN: disabled
		glob_freeze_rate_chain_in_inner =           true;  // keep current test behavior
		glob_test_inner_results_csv =               true;  // keep current test behavior
		glob_run_one_more_outer_after_converged =   true;  // keep current test behavior
		glob_debug_dump_observation_hyperstack =    false;
		glob_debug_show_observation_hyperstack =    false;
		glob_save_initial_final_only =              false;
		glob_center_pair_weight_mode =              0;
	}
	public void dialogQuestions(GenericJTabbedDialog gd) {
		final double shownPcgTolerance = (this.glob_pcg_tolerance > 0.0) ?
				this.glob_pcg_tolerance :
				DEFAULT_GLOB_PCG_TOLERANCE;
		gd.addMessage("Interframe Global LMA parameters selection");
	    gd.addCheckbox    ("Enable global LMA",                           this.glob_en,
	    		"Use global LMA for adjusting scenes poses." );
		gd.addCheckbox("Exit after Global LMA (debug mode)",              this.glob_exit_after_test,
				"exit OpticalFlow.buildSeries() immediately after running, do not increment num_orient enabling re-running next time");
		gd.addNumericField("Global LMA solver mode (0/1)",               this.glob_solver_mode, 0,3,"",
				"0: current sparse/banded global solver, 1: classic LMA-structure global solver.");
		gd.addStringField ("Select XYZATR parameters to fit", IntersceneMatchParameters.booleansToString(this.param_sel,2), 40,
				"Select 6 parameters: X,Y,Z,A,T,R to adjust. Use comma/space separated list of true/false, 1/0 or +/-." );
		gd.addStringField ("Regularization weigts", IntersceneMatchParameters.doublesToString(this.param_regweights), 120,
				"Specify regularization weights for X,Y,Z,A,T,R");
		gd.addStringField ("Smootheing weigts", IntersceneMatchParameters.doublesToString(this.param_lpf), 120,
				"Smoothing weights (pull to average of before and after scenes) for X,Y,Z,A,T,R");
		gd.addMessage("Global LMA iteration parameters");
		gd.addNumericField("LMA lambda",                                  this.glob_lambda, 6,8,"",
				"Initial value of global LMA lambda.");
		gd.addNumericField("Scale lambda after successful LMA iteration", this.glob_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.glob_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.glob_lambda_max, 2,7,"",
				"Fail global LMA if lambda reaches this value.");
		gd.addNumericField("Minimal relative RMSE improvement",           this.glob_rms_diff, 5,7,"",
				"Exit inner iterations if relative RMSE improvement drops below this value.");
		gd.addNumericField("Maximal number of inner iterations",          this.glob_num_iter, 0,3,"",
				"Hard limit on inner global LMA iterations.");
		gd.addNumericField("Maximal number of outer corr+LMA cycles",     this.glob_num_corr, 0,3,"",
				"0 means use IntersceneMatchParameters.max_cycles.");
		gd.addNumericField("PCG iterations per inner step",               this.glob_pcg_iter, 0,5,"",
				"0 means use 8*ilma_num_iter.");
		gd.addNumericField("PCG tolerance",                               shownPcgTolerance, 8,10,"",
				"Infinity norm threshold for PCG residual.");
		gd.addNumericField("Delta convergence threshold",                 this.glob_delta_stop, 6,8,"",
				"Stop inner iterations when maximal parameter update is below this value.");
		gd.addNumericField("Default ATR smoothing weight",                this.glob_smooth_weight_atr, 6,8,"",
				"Used only when per-parameter LPF weights are not provided.");
		gd.addNumericField("Default XYZ smoothing weight",                this.glob_smooth_weight_xyz, 6,8,"",
				"Used only when per-parameter LPF weights are not provided.");
		gd.addNumericField("Minimal offset for pairing",                  this.glob_min_offset, 6,8,"pix",
				"NaN means use IntersceneMatchParameters.min_offset.");
		gd.addNumericField("Maximal offset for pairing",                  this.glob_max_offset, 6,8,"pix",
				"NaN disables maximal offset check.");
		gd.addCheckbox("Freeze rate-chain during inner loop",             this.glob_freeze_rate_chain_in_inner,
				"Use outer-start rates during inner iterations and disable rate-chain Jacobian mapping.");
		gd.addCheckbox("Dump per-inner CSV",                              this.glob_test_inner_results_csv,
				"Save per-inner debug CSV with scene states, curvatures and pair RMS.");
		gd.addCheckbox("Run one extra outer after convergence",           this.glob_run_one_more_outer_after_converged,
				"After first convergence run one extra full recorrelation pass.");
		gd.addCheckbox("Dump observation hyperstack",                     this.glob_debug_dump_observation_hyperstack,
				"Save debug hyperstack with observed dX/dY/strength for each pair.");
		gd.addCheckbox("Show observation hyperstack",                     this.glob_debug_show_observation_hyperstack,
				"Display debug hyperstack in ImageJ UI.");
		gd.addCheckbox("Save only initial+final CSV",                     this.glob_save_initial_final_only,
				"Do not save intermediate outer-loop TIFF/CSV, save only combined initial+final CSV.");
		gd.addNumericField("Center pair weight mode (0/1/2)",            this.glob_center_pair_weight_mode, 0,3,"",
				"0: all pairs enabled, 1: disable center +/-1 pairs, 2: disable center +/-1,+/-2 pairs.");
		}
	public void dialogAnswers(GenericJTabbedDialog gd) {
		this.glob_en =                                 gd.getNextBoolean();
		this.glob_exit_after_test =                    gd.getNextBoolean();
		this.glob_solver_mode =                  (int) gd.getNextNumber();
		if (this.glob_solver_mode < 0) {
			this.glob_solver_mode = 0;
		}
		if (this.glob_solver_mode > 1) {
			this.glob_solver_mode = 1;
		}
		this.param_sel =         IntersceneMatchParameters.StringToBooleans(gd.getNextString(), this.param_sel);
		this.param_regweights =  IntersceneMatchParameters.StringToDoubles(gd.getNextString(),  this.param_regweights);
		this.param_lpf =         IntersceneMatchParameters.StringToDoubles(gd.getNextString(),  this.param_lpf);
		this.glob_lambda =                             gd.getNextNumber();
		this.glob_lambda_scale_good =                  gd.getNextNumber();
		this.glob_lambda_scale_bad =                   gd.getNextNumber();
		this.glob_lambda_max =                         gd.getNextNumber();
		this.glob_rms_diff =                           gd.getNextNumber();
		this.glob_num_iter =                     (int) gd.getNextNumber();
		this.glob_num_corr =                     (int) gd.getNextNumber();
		this.glob_pcg_iter =                     (int) gd.getNextNumber();
		this.glob_pcg_tolerance =                      gd.getNextNumber();
		if (!(this.glob_pcg_tolerance > 0.0)) {
			this.glob_pcg_tolerance = DEFAULT_GLOB_PCG_TOLERANCE;
		}
		this.glob_delta_stop =                         gd.getNextNumber();
		this.glob_smooth_weight_atr =                  gd.getNextNumber();
		this.glob_smooth_weight_xyz =                  gd.getNextNumber();
		this.glob_min_offset =                         gd.getNextNumber();
		this.glob_max_offset =                         gd.getNextNumber();
		this.glob_freeze_rate_chain_in_inner =         gd.getNextBoolean();
		this.glob_test_inner_results_csv =             gd.getNextBoolean();
		this.glob_run_one_more_outer_after_converged = gd.getNextBoolean();
			this.glob_debug_dump_observation_hyperstack =  gd.getNextBoolean();
			this.glob_debug_show_observation_hyperstack =  gd.getNextBoolean();
			this.glob_save_initial_final_only =            gd.getNextBoolean();
			this.glob_center_pair_weight_mode =      (int) gd.getNextNumber();
			if (this.glob_center_pair_weight_mode < 0) {
				this.glob_center_pair_weight_mode = 0;
			}
			if (this.glob_center_pair_weight_mode > 2) {
				this.glob_center_pair_weight_mode = 2;
			}
		}
	
	public void setProperties(String prefix,Properties properties){
		properties.setProperty(prefix+"glob_en",                                   this.glob_en+"");
		properties.setProperty(prefix+"glob_exit_after_test",                      this.glob_exit_after_test+"");
		properties.setProperty(prefix+"glob_solver_mode",                          this.glob_solver_mode+"");
		properties.setProperty(prefix+"param_sel",       IntersceneMatchParameters.booleansToString(this.param_sel,2));    		
		properties.setProperty(prefix+"param_regweights",IntersceneMatchParameters.doublesToString(this.param_regweights));    		
		properties.setProperty(prefix+"param_lpf",       IntersceneMatchParameters.doublesToString(this.param_lpf));    		
		properties.setProperty(prefix+"glob_lambda",                               this.glob_lambda+"");
		properties.setProperty(prefix+"glob_lambda_scale_good",                    this.glob_lambda_scale_good+"");
		properties.setProperty(prefix+"glob_lambda_scale_bad",                     this.glob_lambda_scale_bad+"");
		properties.setProperty(prefix+"glob_lambda_max",                           this.glob_lambda_max+"");
		properties.setProperty(prefix+"glob_rms_diff",                             this.glob_rms_diff+"");
		properties.setProperty(prefix+"glob_num_iter",                             this.glob_num_iter+"");
		properties.setProperty(prefix+"glob_num_corr",                             this.glob_num_corr+"");
		properties.setProperty(prefix+"glob_pcg_iter",                             this.glob_pcg_iter+"");
		properties.setProperty(prefix+"glob_pcg_tolerance",                        this.glob_pcg_tolerance+"");
		properties.setProperty(prefix+"glob_delta_stop",                           this.glob_delta_stop+"");
		properties.setProperty(prefix+"glob_smooth_weight_atr",                    this.glob_smooth_weight_atr+"");
		properties.setProperty(prefix+"glob_smooth_weight_xyz",                    this.glob_smooth_weight_xyz+"");
		properties.setProperty(prefix+"glob_min_offset",                           this.glob_min_offset+"");
		properties.setProperty(prefix+"glob_max_offset",                           this.glob_max_offset+"");
		properties.setProperty(prefix+"glob_freeze_rate_chain_in_inner",           this.glob_freeze_rate_chain_in_inner+"");
		properties.setProperty(prefix+"glob_test_inner_results_csv",               this.glob_test_inner_results_csv+"");
		properties.setProperty(prefix+"glob_run_one_more_outer_after_converged",   this.glob_run_one_more_outer_after_converged+"");
			properties.setProperty(prefix+"glob_debug_dump_observation_hyperstack",    this.glob_debug_dump_observation_hyperstack+"");
			properties.setProperty(prefix+"glob_debug_show_observation_hyperstack",    this.glob_debug_show_observation_hyperstack+"");
			properties.setProperty(prefix+"glob_save_initial_final_only",              this.glob_save_initial_final_only+"");
			properties.setProperty(prefix+"glob_center_pair_weight_mode",              this.glob_center_pair_weight_mode+"");

	}
	
	public void getProperties(String prefix,Properties properties){
		if (properties.getProperty(prefix+"glob_en")!=null)                                 this.glob_en=Boolean.parseBoolean(properties.getProperty(prefix+"glob_en"));
		if (properties.getProperty(prefix+"glob_exit_after_test")!=null)                    this.glob_exit_after_test=Boolean.parseBoolean(properties.getProperty(prefix+"glob_exit_after_test"));
		if (properties.getProperty(prefix+"glob_solver_mode")!=null)                        this.glob_solver_mode=Integer.parseInt(properties.getProperty(prefix+"glob_solver_mode"));
		if (this.glob_solver_mode < 0)                                                        this.glob_solver_mode = 0;
		if (this.glob_solver_mode > 1)                                                        this.glob_solver_mode = 1;
		if (properties.getProperty(prefix+"param_sel")!=null)                               this.param_sel= IntersceneMatchParameters.StringToBooleans(properties.getProperty(prefix+"param_sel"),this.param_sel);
		if (properties.getProperty(prefix+"param_regweights")!=null)                        this.param_regweights= IntersceneMatchParameters.StringToDoubles(properties.getProperty(prefix+"param_regweights"),this.param_regweights);
		if (properties.getProperty(prefix+"param_lpf")!=null)                               this.param_lpf= IntersceneMatchParameters.StringToDoubles(properties.getProperty(prefix+"param_lpf"),this.param_lpf);
		if (properties.getProperty(prefix+"glob_lambda")!=null)                             this.glob_lambda=Double.parseDouble(properties.getProperty(prefix+"glob_lambda"));
		if (properties.getProperty(prefix+"glob_lambda_scale_good")!=null)                  this.glob_lambda_scale_good=Double.parseDouble(properties.getProperty(prefix+"glob_lambda_scale_good"));
		if (properties.getProperty(prefix+"glob_lambda_scale_bad")!=null)                   this.glob_lambda_scale_bad=Double.parseDouble(properties.getProperty(prefix+"glob_lambda_scale_bad"));
		if (properties.getProperty(prefix+"glob_lambda_max")!=null)                         this.glob_lambda_max=Double.parseDouble(properties.getProperty(prefix+"glob_lambda_max"));
		if (properties.getProperty(prefix+"glob_rms_diff")!=null)                           this.glob_rms_diff=Double.parseDouble(properties.getProperty(prefix+"glob_rms_diff"));
		if (properties.getProperty(prefix+"glob_num_iter")!=null)                           this.glob_num_iter=Integer.parseInt(properties.getProperty(prefix+"glob_num_iter"));
		if (properties.getProperty(prefix+"glob_num_corr")!=null)                           this.glob_num_corr=Integer.parseInt(properties.getProperty(prefix+"glob_num_corr"));
		if (properties.getProperty(prefix+"glob_pcg_iter")!=null)                           this.glob_pcg_iter=Integer.parseInt(properties.getProperty(prefix+"glob_pcg_iter"));
		if (properties.getProperty(prefix+"glob_pcg_tolerance")!=null)                      this.glob_pcg_tolerance=Double.parseDouble(properties.getProperty(prefix+"glob_pcg_tolerance"));
		if (!(this.glob_pcg_tolerance > 0.0))                                                 this.glob_pcg_tolerance = DEFAULT_GLOB_PCG_TOLERANCE;
		if (properties.getProperty(prefix+"glob_delta_stop")!=null)                         this.glob_delta_stop=Double.parseDouble(properties.getProperty(prefix+"glob_delta_stop"));
		if (properties.getProperty(prefix+"glob_smooth_weight_atr")!=null)                  this.glob_smooth_weight_atr=Double.parseDouble(properties.getProperty(prefix+"glob_smooth_weight_atr"));
		if (properties.getProperty(prefix+"glob_smooth_weight_xyz")!=null)                  this.glob_smooth_weight_xyz=Double.parseDouble(properties.getProperty(prefix+"glob_smooth_weight_xyz"));
		if (properties.getProperty(prefix+"glob_min_offset")!=null)                         this.glob_min_offset=Double.parseDouble(properties.getProperty(prefix+"glob_min_offset"));
		if (properties.getProperty(prefix+"glob_max_offset")!=null)                         this.glob_max_offset=Double.parseDouble(properties.getProperty(prefix+"glob_max_offset"));
		if (properties.getProperty(prefix+"glob_freeze_rate_chain_in_inner")!=null)         this.glob_freeze_rate_chain_in_inner=Boolean.parseBoolean(properties.getProperty(prefix+"glob_freeze_rate_chain_in_inner"));
		if (properties.getProperty(prefix+"glob_test_inner_results_csv")!=null)             this.glob_test_inner_results_csv=Boolean.parseBoolean(properties.getProperty(prefix+"glob_test_inner_results_csv"));
		if (properties.getProperty(prefix+"glob_run_one_more_outer_after_converged")!=null) this.glob_run_one_more_outer_after_converged=Boolean.parseBoolean(properties.getProperty(prefix+"glob_run_one_more_outer_after_converged"));
			if (properties.getProperty(prefix+"glob_debug_dump_observation_hyperstack")!=null)  this.glob_debug_dump_observation_hyperstack=Boolean.parseBoolean(properties.getProperty(prefix+"glob_debug_dump_observation_hyperstack"));
			if (properties.getProperty(prefix+"glob_debug_show_observation_hyperstack")!=null)  this.glob_debug_show_observation_hyperstack=Boolean.parseBoolean(properties.getProperty(prefix+"glob_debug_show_observation_hyperstack"));
			if (properties.getProperty(prefix+"glob_save_initial_final_only")!=null)            this.glob_save_initial_final_only=Boolean.parseBoolean(properties.getProperty(prefix+"glob_save_initial_final_only"));
			if (properties.getProperty(prefix+"glob_center_pair_weight_mode")!=null)            this.glob_center_pair_weight_mode=Integer.parseInt(properties.getProperty(prefix+"glob_center_pair_weight_mode"));
			if (this.glob_center_pair_weight_mode < 0)                                           this.glob_center_pair_weight_mode = 0;
			if (this.glob_center_pair_weight_mode > 2)                                           this.glob_center_pair_weight_mode = 2;
		}
	
	@Override
	public IntersceneGlobalLmaParameters clone() throws CloneNotSupportedException {
		IntersceneGlobalLmaParameters iglp =     new IntersceneGlobalLmaParameters();
		iglp.glob_en =                                 this.glob_en;
		iglp.glob_exit_after_test =                    this.glob_exit_after_test;
		iglp.glob_solver_mode =                        this.glob_solver_mode;
		iglp.param_sel =                               this.param_sel.clone();
		iglp.param_regweights =                        this.param_regweights.clone();
		iglp.param_lpf =                               this.param_lpf.clone();
		iglp.glob_lambda =                             this.glob_lambda;
		iglp.glob_lambda_scale_good =                  this.glob_lambda_scale_good;
		iglp.glob_lambda_scale_bad =                   this.glob_lambda_scale_bad;
		iglp.glob_lambda_max =                         this.glob_lambda_max;
		iglp.glob_rms_diff =                           this.glob_rms_diff;
		iglp.glob_num_iter =                           this.glob_num_iter;
		iglp.glob_num_corr =                           this.glob_num_corr;
		iglp.glob_pcg_iter =                           this.glob_pcg_iter;
		iglp.glob_pcg_tolerance =                      this.glob_pcg_tolerance;
		iglp.glob_delta_stop =                         this.glob_delta_stop;
		iglp.glob_smooth_weight_atr =                  this.glob_smooth_weight_atr;
		iglp.glob_smooth_weight_xyz =                  this.glob_smooth_weight_xyz;
		iglp.glob_min_offset =                         this.glob_min_offset;
		iglp.glob_max_offset =                         this.glob_max_offset;
		iglp.glob_freeze_rate_chain_in_inner =         this.glob_freeze_rate_chain_in_inner;
		iglp.glob_test_inner_results_csv =             this.glob_test_inner_results_csv;
		iglp.glob_run_one_more_outer_after_converged = this.glob_run_one_more_outer_after_converged;
		iglp.glob_debug_dump_observation_hyperstack =  this.glob_debug_dump_observation_hyperstack;
		iglp.glob_debug_show_observation_hyperstack =  this.glob_debug_show_observation_hyperstack;
		iglp.glob_save_initial_final_only =            this.glob_save_initial_final_only;
		iglp.glob_center_pair_weight_mode =            this.glob_center_pair_weight_mode;
		return iglp;
		}
	
}
