package com.rjconsultores.ventaboletos.auditoria;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.ManyToOne;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.rjconsultores.ventaboletos.entidad.AuditService;

public class AuditControl {
	private String currentActionService;
	private Boolean checkModuleAudit;
	private AuditService currentService;
	private Boolean auditar;
	
	private static Gson gson;
	private static SimpleDateFormat sdf;

	AuditControl(String currentActionService) {
		this.currentActionService = currentActionService;
	}

	public static String toJson(Object object) throws IllegalArgumentException, IllegalAccessException, ParseException {
		SimpleDateFormat sdf = new SimpleDateFormat();
		sdf.applyPattern("dd/MM/yyyy hh:mm:ss");

		StringBuilder json = new StringBuilder();
		json.append("{");

		List<Object> lsClazz = new ArrayList<Object>();
		List<String> lsFieldName = new ArrayList<String>();

		if (object != null){
			for (Field field : object.getClass().getDeclaredFields()) {
				if ((!field.isAnnotationPresent(Column.class) && !field.isAnnotationPresent(Id.class) &&
						!field.isAnnotationPresent(ManyToOne.class)) || field.getType().getSimpleName().equalsIgnoreCase("byte[]")) {
					continue;
				}
	
				if (!field.isAnnotationPresent(ManyToOne.class)) {
					if (json.length() > 1) {
						json.append(",");
					}
	
					json.append("\"".concat(field.getName().concat("\":")));
				} else {
					lsFieldName.add("\"".concat(field.getName().concat("\":")));
				}
	
				field.setAccessible(true);
	
				if (field.getType().getName().contains("String") || field.getType().getName().contains("Date")) {
					json.append("\"");
	
					Date date = null;
	
					if (field.getType().getName().equals("java.util.Date")) {
						date = (java.util.Date) field.get(object);
						json.append(date == null ? "null" : sdf.format(date));
					} else if (field.getType().getName().equals("java.sql.Date")) {
						date = (Date) field.get(object);
						json.append(date == null ? "null" : sdf.format(date));
					} else if (field.getType().getName().contains("String")) {
						json.append((String) field.get(object));
					}
	
					json.append("\"");
	
					continue;
				}
	
				if (field.isAnnotationPresent(ManyToOne.class)) {
					lsClazz.add(field.get(object));
					continue;
				}
	
				json.append(field.get(object) == null ? "null" : field.get(object).toString());
			}
		}
		
		for (int i = 0; i < lsClazz.size(); i++) {
			json.append(",");

			json.append(lsFieldName.get(i));
			json.append(AuditControl.toJson(lsClazz.get(i)));
		}

		json.append("}");

		return json.toString();
	}

	public static Gson getGson() {
		if (AuditControl.gson == null) {
			GsonBuilder gsonBuilder = new GsonBuilder();
			gsonBuilder.registerTypeAdapter(Date.class, new JsonDeserializer<Date>() {
				DateFormat df = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss");

				public Date deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
						throws JsonParseException {
					try {
						return df.parse(json.getAsString());
					} catch (ParseException e) {
						return null;
					}
				}
			});
			gson = gsonBuilder.create();
		}

		return gson;
	}

	public static String formatJson(Object clazz, boolean mainClass) throws IllegalArgumentException, IllegalAccessException {
		String init = "{";
		String fim = "}";
		String tab = "\t";
		String ql = "\r";

		String ret = init.concat(ql).concat(tab);

		List<Object> lsClazz = new ArrayList<Object>();
		List<String> lsNameFd = new ArrayList<String>();

		if (clazz != null){
			for (Field field : clazz.getClass().getDeclaredFields()) {
				if (!field.isAnnotationPresent(Column.class) && !field.isAnnotationPresent(Id.class) &&
						!field.isAnnotationPresent(ManyToOne.class)) {
					continue;
				}
				
				field.setAccessible(true);
				
				if (field.isAnnotationPresent(ManyToOne.class) && field.get(clazz) != null) {
					lsNameFd.add(field.getName());
					lsClazz.add(field.get(clazz));
					continue;
				}
				
	
				if (!mainClass) {
					ret += tab;
				}
	
				ret += field.getName().concat(":");
				
				String fieldValue = null;
				
				if (field.getType().getSimpleName().equalsIgnoreCase("date") && field.get(clazz) != null) {
					fieldValue = AuditControl.convertDateObjectToString(field.get(clazz));
				} else {
					fieldValue = (field.get(clazz) == null ? "null" : field.get(clazz)).toString();
				}
				
				ret += fieldValue;
				ret += ql.concat(tab);
			}
	
			for (int i = 0; i < lsClazz.size(); i++) {
				ret += lsNameFd.get(i).concat(tab.concat(":").concat(tab));
				ret += formatJson(lsClazz.get(i), false);
			}
	
			if (mainClass) {
				ret += ql.concat(fim);
			} else {
				ret += ql.concat(tab.concat(fim).concat(",").concat(ql).concat(tab));
			}
		}
		return ret;
	}
	
	public static String convertDateObjectToString(Object dateObject) throws RuntimeException {
		if (sdf == null) {
			sdf = new SimpleDateFormat();
			sdf.applyPattern("dd/MM/yyyy hh:mm:ss");
		}
		
		if (dateObject.getClass().getName().contains("util")) {
			Date date = (Date) dateObject;
			return sdf.format(date);
		}
		
		if (dateObject.getClass().getName().contains("sql")) {
			java.sql.Date date = (java.sql.Date) dateObject;
			return sdf.format(new Date(date.getTime()));
		}
		
		throw new RuntimeException("Não foi possível realizar a conversão de data.\rClass: " + AuditControl.class.getName());
	}

	public String getCurrentActionService() {
		return currentActionService;
	}

	public AuditService getCurrentService() {
		return currentService;
	}

	public void setCurrentService(AuditService currentService) {
		this.currentService = currentService;
	}

	public Boolean getCheckModuleAudit() {
		return checkModuleAudit;
	}

	public void setCheckModuleAudit(Boolean checkModuleAudit) {
		this.checkModuleAudit = checkModuleAudit;
	}

	public Boolean getAuditar() {
		return auditar;
	}

	public void setAuditar(Boolean auditar) {
		this.auditar = auditar;
	}
}
