class PhaseScan_Runner(Runnable):
	def __init__(self,scl_long_tuneup_controller, run_to_end = true):
		self.scl_long_tuneup_controller = scl_long_tuneup_controller
		self.run_to_end = run_to_end
		self.harmonicsAnalyzer = HarmonicsAnalyzer(2)
		#--- fake scan boolean variable
		scl_long_tuneup_phase_scan_controller = self.scl_long_tuneup_controller.scl_long_tuneup_phase_scan_controller
		set_phase_shift_panel = scl_long_tuneup_phase_scan_controller.set_phase_shift_panel
		self.fake_scan = set_phase_shift_panel.scanSim_RadioButton.isSelected()
		self.useTrigger = set_phase_shift_panel.beamTrigger_RadioButton.isSelected()
		self.keepCavPhases = set_phase_shift_panel.keepLiveCavPhases_RadioButton.isSelected()
		self.wrapPhases = set_phase_shift_panel.wrapPhases_RadioButton.isSelected()
	
	def run(self):
		messageTextField = self.scl_long_tuneup_controller.getMessageTextField()
		if(messageTextField != null):
			messageTextField.setText("")	
		scl_long_tuneup_phase_scan_controller = self.scl_long_tuneup_controller.scl_long_tuneup_phase_scan_controller
		scan_state_controller = scl_long_tuneup_phase_scan_controller.scan_state_controller
		cavs_table = scl_long_tuneup_phase_scan_controller.cavs_table
		cav_wrappers = self.scl_long_tuneup_controller.cav_wrappers
		n_cavs = len(cav_wrappers)
		phase_step = scl_long_tuneup_phase_scan_controller.start_stop_scan_panel.phase_step_text.getValue()
		time_wait = scl_long_tuneup_phase_scan_controller.set_phase_shift_panel.time_wait_text.getValue()
		scan_status_text = scl_long_tuneup_phase_scan_controller.start_stop_scan_panel.scan_status_text
		scl_long_tuneup_init_controller = self.scl_long_tuneup_controller.scl_long_tuneup_init_controller
		if(not scl_long_tuneup_init_controller.allPairsSet()):
			if(messageTextField != null):
				messageTextField.setText("You should Initialize the scan first! Go to the Init tab!")		
			return
		scl_long_tuneup_init_controller.connectAllBPMs()
		beamTrigger = self.scl_long_tuneup_controller.beamTrigger
		bpmBatchReader = self.scl_long_tuneup_controller.bpmBatchReader
		bpm_amp_min_limit = scl_long_tuneup_phase_scan_controller.post_scan_panel.amp_limit_text.getValue()
		#------------------------------- CA START
		# ?????? For live CA
		if(beamTrigger == null):
			if(messageTextField != null):
				messageTextField.setText("You should Initialize the scan first! Go to the Init tab!")		
			return
		else:
			beamTrigger.setFakeScan(self.fake_scan)
			beamTrigger.setUseTrigger(self.useTrigger)
		beamTrigger.setSleepTime(time_wait)
		beamTrigger.scan_state_controller = scan_state_controller
		bpmBatchReader.setBeamTrigger(beamTrigger)
		#------------------------------- CA END
		#---------start deal with selected cavities
		cav_selected_inds = cavs_table.getSelectedRows()
		if(len(cav_selected_inds) < 1 or cav_selected_inds[0] < 0): 
			if(messageTextField != null):
				messageTextField.setText("Select one or more cavities to start Phase Scan!")	
				beamTrigger.setFakeScan(false)
			return
		# these are the table model indexes
		cav_wrapper = null
		start_ind = cav_selected_inds[0]
		last_ind = cav_selected_inds[len(cav_selected_inds)-1]
		if(self.run_to_end): last_ind = n_cavs
		#----- blank and clean scan data for all downstream cavities
		cav_wrapper = self.scl_long_tuneup_controller.cav0_wrapper
		if(start_ind != 0):
			cav_wrapper = cav_wrappers[start_ind-1]
		cav_wrapper.clean()
		for cav_table_ind in range(start_ind+1,n_cavs+1):
			cav_wrapper = cav_wrappers[cav_table_ind-1]
			if(cav_wrapper.isGood):
				# ?????? should be tested with CA
				if(not self.fake_scan): cav_wrapper.setBlankBeam(true)
				cav_wrapper.clean()
		cavs_table.getModel().fireTableDataChanged()
		#-------start loop over cavities in the table
		result = true
		time_start = time.time()
		n_total = last_ind - start_ind + 1
		n_count = 0
		for cav_table_ind in range(start_ind,last_ind+1):
			cavs_table.setRowSelectionInterval(cav_table_ind,cav_table_ind)
			cav_wrapper = self.scl_long_tuneup_controller.cav0_wrapper
			if(cav_table_ind != 0):
				cav_wrapper = cav_wrappers[cav_table_ind-1]
			if(not cav_wrapper.isGood): continue
			if(cav_table_ind != 0):
				# ?????? should be tested with CA
				if(not self.fake_scan): cav_wrapper.setBlankBeam(false)
			txt = "Scan is running! Cavity="+cav_wrapper.alias
			if(start_ind != last_ind): txt = txt + " to Cavity="+cav_wrappers[last_ind-1].alias
			if(n_count > 1):
				run_time = time.time() - time_start
				run_time_sec = int(run_time % 60)
				run_time_min = int(run_time/60.)
				eat_time = ((run_time/n_count)*(n_total - n_count))
				eat_time_sec = int(eat_time % 60)
				eat_time_min = int(eat_time/60.)
				txt = txt + "  ETA= %3d min  %2d sec "%(eat_time_min,eat_time_sec)
				txt = txt + "  Elapse= %3d min  %2d sec "%(run_time_min,run_time_sec)
			scan_status_text.setText(txt)
			if(scan_state_controller.getShouldStop()): break
			cav_phase = -180.
			while(cav_phase <= 180.):
				#debug
				#print "debug cav=",cav_wrapper.alias," phase=",cav_phase
				#--------set cavity phase
				if(cav_table_ind != 0):
					# ?????? should be tested with CA
					if(not self.fake_scan): cav_wrapper.cav.setCavPhase(cav_phase)
				if(cav_wrapper == self.scl_long_tuneup_controller.cav0_wrapper):
					scan_status_text.setText("Scan running  cavity= "+cav_wrapper.alias+" phase="+str(cav_phase))
				if(scan_state_controller.getShouldStop()): break
				time.sleep(time_wait)
				if(scan_state_controller.getShouldStop()): break
				# ?????? For live CA
				result = false
				if(not self.fake_scan):
					result = bpmBatchReader.makePhaseScanStep(cav_phase,cav_wrapper,bpm_amp_min_limit)
				else:
					result = self.fakeMakePhaseScanStep(cav_phase,cav_wrapper)
				if(scan_state_controller.getShouldStop()): break
				if(result == false):
					scan_status_text.setText("Scan stopped. Could not get BPM data. Please check!")
					if(messageTextField != null):
						messageTextField.setText("")
						beamTrigger.setFakeScan(false)
					return 
				if(cav_table_ind != 0):
					cav_wrapper.addScanPointToPhaseDiffData()			
				cav_phase += phase_step
			#----end of phase loop
			if(scan_state_controller.getShouldStop()): break
			#set up the Cavity phase according to scan
			if(cav_table_ind != 0):
				res_phase_set = self.setUpCavityPhase(cav_wrapper)
				time.sleep(time_wait)
				if(not res_phase_set):
					scan_status_text.setText("Scan stopped! Bad scan! Cannot set up phase for cavity="+cav_wrapper.alias)
					beamTrigger.setFakeScan(false)
					return
			cav_wrapper.isMeasured = true
			cavs_table.getModel().fireTableDataChanged()
			n_count += 1
		#------end of cavities loop
		txt = ""
		if(cav_wrapper != null):
			txt = " The last cavity was "+cav_wrapper.alias+" ."
		run_time = time.time() - time_start
		run_time_sec = int(run_time % 60)
		run_time_min = int(run_time/60.)
		txt = txt + "  Total time= %3d min  %2d sec "%(run_time_min,run_time_sec)
		if(scan_state_controller.getShouldStop()):
			scan_status_text.setText("Scan was interrupted!"+txt)
		else:
			scan_status_text.setText("Scan finished!"+txt)
		#restore the beam trigger fake scan state
		beamTrigger.setFakeScan(false)
	
	def setUpCavityPhase(self,cav_wrapper):
		#--- wrap all BPMs phase scans at once
		if(self.wrapPhases): 
			res_phase_wrap = self.allBPMs_PhaseWrapper(cav_wrapper)	
			if(not res_phase_wrap): return false
		max_bad_points_count = 2
		gd = cav_wrapper.phaseDiffPlot
		gdTh = cav_wrapper.phaseDiffPlotTh
		gdTh.removeAllPoints()
		if(gd.getNumbOfPoints() < 8): return false
		err = self.harmonicsAnalyzer.analyzeData(gd)	
		harm_function = self.harmonicsAnalyzer.getHrmonicsFunction()
		#-----remove bad points
		bad_points_count = 0
		bad_index = 1
		while(bad_index >= 0):
			bad_index = -1
			for i in range(gd.getNumbOfPoints()):
				phase = gd.getX(i)
				y_appr = harm_function.getValue(phase)
				y = gd.getY(i)
				if(math.fabs(y-y_appr) > 3.0*err):
					bad_index = i
					bad_points_count += 1
					break
			if(bad_index >= 0):
				gd.removePoint(bad_index)
				bpm_wrappers = cav_wrapper.bpm_wrappers
				for bpm_wrapper in bpm_wrappers:				
					if(cav_wrapper.bpm_amp_phase_dict.has_key(bpm_wrapper)):
						(graphDataAmp,graphDataPhase) = cav_wrapper.bpm_amp_phase_dict[bpm_wrapper]
						graphDataAmp.removePoint(bad_index)
						graphDataPhase.removePoint(bad_index)
			# we should stop if we have too many bad points
			if(bad_points_count > max_bad_points_count):
				return false
		if(bad_points_count > 0):
			err = self.harmonicsAnalyzer.analyzeData(gd)
			harm_function = self.harmonicsAnalyzer.getHrmonicsFunction()
		#----find a new cavity phase
		min_phase = self.harmonicsAnalyzer.getPositionOfMin()
		new_phase = cav_wrapper.initLivePhase
		if(self.keepCavPhases):
			cav_wrapper.scanPhaseShift = makePhaseNear(new_phase - min_phase,0.)
		else:
			new_phase = makePhaseNear(min_phase + cav_wrapper.scanPhaseShift,0.)
		# ?????? should be tested with CA
		cav_wrapper.livePhase = new_phase
		if(not self.fake_scan):
			cav_wrapper.cav.setCavPhase(cav_wrapper.livePhase)
		cav_wrapper.livePhase = new_phase
		cav_wrapper.phase_scan_harm_amp = harm_function.getParamArr()[1]
		cav_wrapper.phase_scan_harm_err = err
		cav_wrapper.phase_scan_harm_funcion.setParamArr(self.harmonicsAnalyzer.getHrmonicsFunction().getParamArr())
		#----make theory graph plot
		x_arr = []
		y_arr = []
		for i in range(73):
		 phase = -180.0 + 5.0*i
		 y = harm_function.getValue(phase)
		 x_arr.append(phase)
		 y_arr.append(y)
		gdTh.addPoint(x_arr,y_arr)
		return true
	
	def fakeMakePhaseScanStep(self,cav_phase,cav_wrapper):
		cav_wrappers = self.scl_long_tuneup_controller.cav_wrappers
		mass = self.scl_long_tuneup_controller.mass/1.0e+6
		bpm_freq = self.scl_long_tuneup_controller.bpm_freq
		c = self.scl_long_tuneup_controller.c_light	
		cav_pos0 = cav_wrappers[0].pos
		max_pos = cav_wrappers[len(cav_wrappers)-1].pos - cav_pos0
		cav_pos = cav_wrapper.pos
		eKin_in = 185.6+650.0*(cav_pos-cav_pos0)/max_pos
		cav_phase_offset = 30.
		phase = makePhaseNear(cav_phase + cav_phase_offset,0.)
		eKin = eKin_in
		if(self.scl_long_tuneup_controller.cav0_wrapper != cav_wrapper):
			eKin +=  8.0*math.cos(2.0*math.pi*phase/360.)
		else:
			eKin = 185.6
		gamma = (eKin+mass)/mass
		beta = math.sqrt(1.0 - 1.0/gamma**2)
		c = 2.99792458e+8
		freq = 402.5e+6
		bpm_phase_coef = 360.0*freq/(c*beta)
		#print "debug cav_phase=",cav_phase," beta=",beta," eKin=",eKin," cav_pos=",cav_pos," coeff=",bpm_phase_coef
		bpm_wrappers = cav_wrapper.bpm_wrappers
		for bpm_wrapper in bpm_wrappers:				
			if(cav_wrapper.bpm_amp_phase_dict.has_key(bpm_wrapper)):
				(graphDataAmp,graphDataPhase) = cav_wrapper.bpm_amp_phase_dict[bpm_wrapper]
				if(bpm_wrapper.pos < cav_pos):
					graphDataAmp.addPoint(cav_phase,10.0)
					graphDataPhase.addPoint(cav_phase,20.0)
					continue
				bpm_phase = makePhaseNear(bpm_phase_coef*(bpm_wrapper.pos - cav_pos),0.)
				bpm_phase += 1.0*(random.random()-0.5)
				bpm_amp = 25.0*1./(1.+(bpm_wrapper.pos-cav_pos)/20.)
				graphDataAmp.addPoint(cav_phase,bpm_amp)
				old_bpm_phase = 0.
				if(graphDataPhase.getNumbOfPoints() > 0):
					old_bpm_phase = graphDataPhase.getY(graphDataPhase.getNumbOfPoints() - 1)
				graphDataPhase.addPoint(cav_phase,makePhaseNear(bpm_phase,old_bpm_phase))
		return true
		
	def allBPMs_PhaseWrapper(self,cav_wrapper):
		# it will wrap all BPM phases for the cvity by iteration from the BPM closest to cavity
		if(not cav_wrapper.isGood): return
		cav_pos = cav_wrapper.pos
		bpm_wrappers = []
		for bpm_ind in range(len(cav_wrapper.bpm_wrappers)):
			bpm_wrapper = cav_wrapper.bpm_wrappers[bpm_ind]
			res_bool = cav_wrapper.bpm_amp_phase_dict.has_key(bpm_wrapper)
			res_bool = res_bool and bpm_wrapper.isGood
			res_bool = res_bool and (bpm_wrapper.pos > cav_pos)
			if(res_bool):
				bpm_wrappers.append(bpm_wrapper)
		for bpm_ind in range(len(bpm_wrappers)-1):
			(graphDataAmp0,graphDataPhase0) = cav_wrapper.bpm_amp_phase_dict[bpm_wrappers[bpm_ind]]
			(graphDataAmp1,graphDataPhase1) = cav_wrapper.bpm_amp_phase_dict[bpm_wrappers[bpm_ind+1]]
			if(graphDataPhase0.getNumbOfPoints() < 1): break
			if(graphDataPhase0.getNumbOfPoints() != graphDataPhase1.getNumbOfPoints()):
				txt = "Phase Wrapper BPM=",bpm_wrappers[bpm_ind].alias
				txt += " and BPM=",bpm_wrappers[bpm_ind+1].alias
				txt += " have different number of RF phase points n1=",graphDataPhase0.getNumbOfPoints()
				txt += " n2=",graphDataPhase1.getNumbOfPoints()
				self.scl_long_tuneup_controller.getMessageTextField().setText(txt)
				print "debug ==================="
				print txt
				time.sleep(10.)
				return false
			y_arr = []
			for ip in range(graphDataPhase1.getNumbOfPoints()):
				y_arr.append(graphDataPhase1.getY(ip))
			base_phase_diff = y_arr[0] - graphDataPhase0.getY(0)
			for ip in range(1,graphDataPhase0.getNumbOfPoints()):
				y0 = graphDataPhase0.getY(ip)
				y_arr[ip] = makePhaseNear(y_arr[ip],y0+base_phase_diff)
				base_phase_diff = y_arr[ip] - y0
			#move all data by 360. to make the avg close to 0.
			y_avg = 0.
			for y in y_arr:
				y_avg += y
			if(len(y_arr) > 1): y_avg /= len(y_arr)
			y_shift = int(y_avg/360.)*360.
			for ip in range(len(y_arr)):
				y_arr[ip] -= y_shift
			#--- update all bpm phases
			graphDataPhase1.updateValuesY(y_arr)	
		# recreate phase difference
		cav_wrapper.recalculatePhaseDiffData()
		return true