""" The model deploy file is used to leverage a trained model to perform inference on unknown set of node deviations.
"""

import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'

import sys
current_path=os.path.dirname(__file__)
parentdir = os.path.dirname(current_path)

#Adding Path to various Modules
sys.path.append("../core")
sys.path.append("../visualization")
sys.path.append("../utilities")
sys.path.append("../datasets")
sys.path.append("../trained_models")
sys.path.append("../config")

import numpy as np
import pandas as pd
import tensorflow as tf
import csv
import logging
tf.get_logger().setLevel(logging.ERROR)

from tensorflow.keras.models import load_model


#Importing Config files
import assembly_config as config
import model_config as cftrain
import measurement_config as mscofig

#Importing required modules from the package
from measurement_system import HexagonWlsScanner
from assembly_system import VRMSimulationModel
from assembly_system import PartType
from wls400a_system import GetInferenceData
from metrics_eval import MetricsEval
from data_import import GetTrainData
#from cam_viz import CamViz
#from cop_viz import CopViz

class DeployModel:
	"""The Deploy Model class is used to import a trained model and use it to infer on unknown data

	"""
	def get_model(self,model_path):
		"""get_model method is is used to retrieve the trained model from a given path
				
				:param model_path: Path to the trained model, ideally it should be same as the train model path output
				:type model_path: str (required)
		"""
		from tensorflow.keras.models import load_model
		try:
			inference_model=load_model(model_path)
			print('Deep Learning Model found and loaded')
		except AssertionError as error:
			print(error)
			print('Model not found at this path ',model_path, ' Update path in config file if required')

		return inference_model

	def model_inference(self,inference_data,inference_model,deploy_path,print_result=0,plot_result=0,get_cam_data=0,append_result=0):
		"""model_inference method is used to infer from unknown sample(s) using the trained model 
				
				:param inference_data: Unknown dataset having same structure as the train dataset
				:type inference_data: numpy.array [samples*voxel_dim*voxel_dim*voxel_dim*deviation_channels] (required) (required)

				:param inference_model: Trained model
				:type inference_model: keras.model (required)
				
				:param print_result: Flag to indicate if the result needs to be printed, 0 by default, change to 1 in case the results need to be printed on the console
				:type print_result: int

		"""		
		result=inference_model.predict(inference_data)
		description="The Process Parameters variations are inferred from the obtained measurement data and the trained CNN based model"
		print('The model estimates are: ')
		rounded_result=np.round(result,2)
		
		if(print_result==1):
			print(rounded_result)
		
		if(append_result==1):
			with open ("user_preds.csv",'a',newline='') as filedata:
				#fieldnames = ['kcc1','kcc2','kcc3','kcc4','kcc5','kcc6']                            
				writer = csv.writer(filedata, delimiter=',')
				writer.writerow(rounded_result[0,:].tolist())
				#writer.writerow(dict(zip(fieldnames, rounded_result[0,:].tolist()))) 
				#filedata.write(rounded_result[0,:].tolist())
		
		if(plot_result==1):
			print("Plotting Results in HTML...")
			import plotly.graph_objects as go
			import plotly as py
			result_str = ["%.2f" % number for number in rounded_result[0,:]]

			kcc_str=[]
			for i in range(rounded_result.shape[1]):
				kcc_str.append("X("+str(i)+"): ")
			#kcc_str=["X(1): ","X(2): ", "X(3): ", "X(4): ", "X(5): ", "X(6): "]	
			
			display_str=np.core.defchararray.add(kcc_str, result_str)	
			print(display_str)
			fig = go.Figure(data=go.Scatter(y=rounded_result[0,:], marker=dict(
			size=30,color=100), mode='markers+text',text=display_str,x=kcc_str))
			fig.update_traces( textfont_size=20,textposition='top center')
			fig.update_layout(title_text='Deep Learning for Manufacturing - Model Estimates')
			py.offline.plot(fig, filename=deploy_path+"results.html")

		if(get_cam_data==1):
			#print(inference_model.summary())
			from cam_viz import CamViz
			from cop_viz import CopViz
			input_conv_data=inference_data
			base_cop=input_conv_data[0,:,:,:,0]+input_conv_data[0,:,:,:,1]+input_conv_data[0,:,:,:,2]
			base_cop[base_cop!=0]=0.6

			process_parameter_id=np.argmax(abs(result[0,:]))
			print("Plotting Gradient based Class Activation Map for Process Parameter: ",process_parameter_id)
			camviz=CamViz(inference_model,'conv_block_9')
			#For explicit plotting change ID here
			#process_parameter_id=0
			cop_input=input_conv_data[0:1,:,:,:,:]
			fmap_eval, grad_wrt_fmap_eval=camviz.grad_cam_3d(cop_input,process_parameter_id)
			alpha_k_c= grad_wrt_fmap_eval.mean(axis=(0,1,2,3)).reshape((1,1,1,-1))
			Lc_Grad_CAM = np.maximum(np.sum(fmap_eval*alpha_k_c,axis=-1),0).squeeze()
			scale_factor = np.array(cop_input.shape[1:4])/np.array(Lc_Grad_CAM.shape)

			from scipy.ndimage.interpolation import zoom
			import tensorflow.keras.backend as K
			
			_grad_CAM = zoom(Lc_Grad_CAM,scale_factor)
			arr_min, arr_max = np.min(_grad_CAM), np.max(_grad_CAM)
			grad_CAM = (_grad_CAM - arr_min) / (arr_max - arr_min + K.epsilon())

			#Code for Grad CAM Plotting
			import plotly.graph_objects as go
			import plotly as py
			import plotly.express as px
			X, Y, Z = np.mgrid[0:len(base_cop), 0:len(base_cop), 0:len(base_cop)]
			#input_conv_data[0,:,:,:,0]=0.2
			values_cop = base_cop
			values_grad_cam=grad_CAM

			trace1=go.Volume(
				x=X.flatten(),
				y=Y.flatten(),
				z=Z.flatten(),
				value=values_cop.flatten(),
				isomin=0,
				isomax=1,
				opacity=0.1, # needs to be small to see through all surfaces
				surface_count=17, # needs to be a large number for good volume rendering
				colorscale='Greens'
				)

			trace2=go.Volume(
				x=X.flatten(),
				y=Y.flatten(),
				z=Z.flatten(),
				value=values_grad_cam.flatten(),
				isomin=0,
				isomax=1,
				opacity=0.1, # needs to be small to see through all surfaces
				surface_count=17,
				colorscale='orrd' # needs to be a large number for good volume rendering
				)
			data = [trace1,trace2]
			
			layout = go.Layout(
				margin=dict(
					l=0,
					r=0,
					b=0,
					t=0
				)
			)
			
			fig = go.Figure(data=data,layout=layout)
			plot_file_name=deploy_path+'voxel_grad_cam.html'
			py.offline.plot(fig, filename=plot_file_name)


		return result

if __name__ == '__main__':
	
	print("Welcome to Deep Learning for Manufacturing (dlmfg)...")
	print('Parsing from Assembly Config File....')

	data_type=config.assembly_system['data_type']
	application=config.assembly_system['application']
	part_type=config.assembly_system['part_type']
	part_name=config.assembly_system['part_name']
	data_format=config.assembly_system['data_format']
	assembly_type=config.assembly_system['assembly_type']
	assembly_kccs=config.assembly_system['assembly_kccs']	
	assembly_kpis=config.assembly_system['assembly_kpis']
	voxel_dim=config.assembly_system['voxel_dim']
	point_dim=config.assembly_system['point_dim']
	voxel_channels=config.assembly_system['voxel_channels']
	noise_type=config.assembly_system['noise_type']
	mapping_index=config.assembly_system['mapping_index']
	file_names_x=config.assembly_system['test_data_files_x']
	file_names_y=config.assembly_system['test_data_files_y']
	file_names_z=config.assembly_system['test_data_files_z']
	system_noise=config.assembly_system['system_noise']
	aritifical_noise=config.assembly_system['aritifical_noise']
	data_folder=config.assembly_system['data_folder']
	kcc_folder=config.assembly_system['kcc_folder']
	kcc_files=config.assembly_system['test_kcc_files']
	

	print('Initializing the Assembly System and Measurement System....')
	measurement_system=HexagonWlsScanner(data_type,application,system_noise,part_type,data_format)
	vrm_system=VRMSimulationModel(assembly_type,assembly_kccs,assembly_kpis,part_name,part_type,voxel_dim,voxel_channels,point_dim,aritifical_noise)
	deploy_model=DeployModel()
	
	#Generate Paths
	train_path='../trained_models/'+part_type
	model_path=train_path+'/model'+'/trained_model_0.h5'
	logs_path=train_path+'/logs'
	deploy_path=train_path+'/deploy/'

	#Voxel Mapping File

	get_data=GetTrainData();
	
	print('Importing and Preprocessing Cloud-of-Point Data')
	dataset=[]
	dataset.append(get_data.data_import(file_names_x,data_folder))
	dataset.append(get_data.data_import(file_names_y,data_folder))
	dataset.append(get_data.data_import(file_names_z,data_folder))
	point_index=get_data.load_mapping_index(mapping_index)


	#Make an Object of the Measurement System Class
	measurement_system=HexagonWlsScanner(data_type,application, system_noise,part_type,data_format)
	#Make an Object of the Assembly System Class
	assembly_system=PartType(assembly_type,assembly_kccs,assembly_kpis,part_name,part_type,voxel_dim,voxel_channels,point_dim)

	
	#Inference from simulated data
	inference_model=deploy_model.get_model(model_path)
	print(inference_model.summary())
	
	input_conv_data, kcc_subset_dump,kpi_subset_dump=get_data.data_convert_voxel_mc(vrm_system,dataset,point_index)

	y_pred=deploy_model.model_inference(input_conv_data,inference_model,deploy_path,print_result=1,plot_result=1);

	evalerror=1

	if(evalerror==1):
		kcc_dataset=get_data.data_import(kcc_files,kcc_folder)
		metrics_eval=MetricsEval();
		eval_metrics,accuracy_metrics_df=metrics_eval.metrics_eval_base(y_pred,kcc_dataset,logs_path)
		
		print('Evaluation Metrics: ',eval_metrics)
		accuracy_metrics_df.to_csv(logs_path+'/metrics_test.csv')
		
		np.savetxt((deploy_path+"predicted.csv"), y_pred, delimiter=",")
		print('Predicted Values saved to disk...')

	#Inference from Measurement Data

	#measurement_files=mscofig.ms_parameters['measurement_files']
	
	#Make an object of Get Data Class
	#get_data=GetInferenceData();
	
	#Call functions of the get Data Class
	#for measurement_file in measurement_files:	
		#measurement_path=deploy_path+measurement_file
		#measurement_data=get_data.load_measurement_file(measurement_path)
		#voxel_point_index=get_data.load_mapping_index(voxel_path)
		#y_dev_data_filtered=get_data.data_pre_processing(measurement_data,voxel_channels)
		#input_conv_data=get_data.voxel_mapping(y_dev_data_filtered,voxel_point_index,point_dim,voxel_dim,voxel_channels)
		#y_pred=deploy_model.model_inference(input_conv_data,inference_model);
		#print('KCCs for: ',measurement_file)
		#print(y_pred)

	#Code for Voxel Vizvalization

	#Code for CAM Visualization
	viz=0
	if(viz==1):
		print(inference_model.summary())
		camviz=CamViz(inference_model,'conv3d_3')

		grads=camviz.grad_cam_3d(input_conv_data[1:2,:,:,:,:],1)
tensorflow.keras.models.load_model numpy.core.defchararray.add numpy.min tensorflow.get_logger numpy.round numpy.max tensorflow.keras.backend.epsilon numpy.savetxt numpy.array scipy.ndimage.interpolation.zoom numpy.sum
[(8, 'os.path.dirname', 'os.path.dirname', (['__file__'], {}), False, 'import os\n'), (9, 'os.path.dirname', 'os.path.dirname', (['current_path'], {}), False, 'import os\n'), (12, 'sys.path.append', 'sys.path.append', (['"""../core"""'], {}), False, 'import sys\n'), (13, 'sys.path.append', 'sys.path.append', (['"""../visualization"""'], {}), False, 'import sys\n'), (14, 'sys.path.append', 'sys.path.append', (['"""../utilities"""'], {}), False, 'import sys\n'), (15, 'sys.path.append', 'sys.path.append', (['"""../datasets"""'], {}), False, 'import sys\n'), (16, 'sys.path.append', 'sys.path.append', (['"""../trained_models"""'], {}), False, 'import sys\n'), (17, 'sys.path.append', 'sys.path.append', (['"""../config"""'], {}), False, 'import sys\n'), (217, 'measurement_system.HexagonWlsScanner', 'HexagonWlsScanner', (['data_type', 'application', 'system_noise', 'part_type', 'data_format'], {}), False, 'from measurement_system import HexagonWlsScanner\n'), (218, 'assembly_system.VRMSimulationModel', 'VRMSimulationModel', (['assembly_type', 'assembly_kccs', 'assembly_kpis', 'part_name', 'part_type', 'voxel_dim', 'voxel_channels', 'point_dim', 'aritifical_noise'], {}), False, 'from assembly_system import VRMSimulationModel\n'), (229, 'data_import.GetTrainData', 'GetTrainData', ([], {}), False, 'from data_import import GetTrainData\n'), (240, 'measurement_system.HexagonWlsScanner', 'HexagonWlsScanner', (['data_type', 'application', 'system_noise', 'part_type', 'data_format'], {}), False, 'from measurement_system import HexagonWlsScanner\n'), (242, 'assembly_system.PartType', 'PartType', (['assembly_type', 'assembly_kccs', 'assembly_kpis', 'part_name', 'part_type', 'voxel_dim', 'voxel_channels', 'point_dim'], {}), False, 'from assembly_system import PartType\n'), (24, 'tensorflow.get_logger', 'tf.get_logger', ([], {}), True, 'import tensorflow as tf\n'), (80, 'numpy.round', 'np.round', (['result', '(2)'], {}), True, 'import numpy as np\n'), (257, 'metrics_eval.MetricsEval', 'MetricsEval', ([], {}), False, 'from metrics_eval import MetricsEval\n'), (263, 'numpy.savetxt', 'np.savetxt', (["(deploy_path + 'predicted.csv')", 'y_pred'], {'delimiter': '""","""'}), True, 'import numpy as np\n'), (290, 'cam_viz.CamViz', 'CamViz', (['inference_model', '"""conv3d_3"""'], {}), False, 'from cam_viz import CamViz\n'), (56, 'tensorflow.keras.models.load_model', 'load_model', (['model_path'], {}), False, 'from tensorflow.keras.models import load_model\n'), (104, 'numpy.core.defchararray.add', 'np.core.defchararray.add', (['kcc_str', 'result_str'], {}), True, 'import numpy as np\n'), (110, 'plotly.offline.plot', 'py.offline.plot', (['fig'], {'filename': "(deploy_path + 'results.html')"}), True, 'import plotly as py\n'), (122, 'cam_viz.CamViz', 'CamViz', (['inference_model', '"""conv_block_9"""'], {}), False, 'from cam_viz import CamViz\n'), (134, 'scipy.ndimage.interpolation.zoom', 'zoom', (['Lc_Grad_CAM', 'scale_factor'], {}), False, 'from scipy.ndimage.interpolation import zoom\n'), (181, 'plotly.graph_objects.Figure', 'go.Figure', ([], {'data': 'data', 'layout': 'layout'}), True, 'import plotly.graph_objects as go\n'), (183, 'plotly.offline.plot', 'py.offline.plot', (['fig'], {'filename': 'plot_file_name'}), True, 'import plotly as py\n'), (88, 'csv.writer', 'csv.writer', (['filedata'], {'delimiter': '""","""'}), False, 'import csv\n'), (129, 'numpy.array', 'np.array', (['cop_input.shape[1:4]'], {}), True, 'import numpy as np\n'), (129, 'numpy.array', 'np.array', (['Lc_Grad_CAM.shape'], {}), True, 'import numpy as np\n'), (135, 'numpy.min', 'np.min', (['_grad_CAM'], {}), True, 'import numpy as np\n'), (135, 'numpy.max', 'np.max', (['_grad_CAM'], {}), True, 'import numpy as np\n'), (136, 'tensorflow.keras.backend.epsilon', 'K.epsilon', ([], {}), True, 'import tensorflow.keras.backend as K\n'), (128, 'numpy.sum', 'np.sum', (['(fmap_eval * alpha_k_c)'], {'axis': '(-1)'}), True, 'import numpy as np\n')]