package com.rjconsultores.ventaboletos.utilerias;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.AuthenticationFailedException;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.SendFailedException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.ByteArrayDataSource;

import org.apache.commons.lang.StringUtils;

import com.rjconsultores.ventaboletos.enums.MimeType;
import com.rjconsultores.ventaboletos.enums.TypeContentEmail;


public class SendMail implements java.io.Serializable {
	private static final String ENCODE_UTF_8 = "UTF-8";

	private static final long serialVersionUID = 2067442255080001075L;
	
	public enum AuthType {TLS,SSL,SMTPS,NONE}

	private String smtpHost;
	private String smtpUser;
	private String smtpPassword;
	private String smtpPort;
	private String emailFrom;
	private String emailTo;
	private String emailToCO;
	private List<String> emailCopy = new ArrayList<String>();
	private String localhost;
	private Boolean auth;
	private String subject;
	private String text;
	private Map<String, ByteArrayOutputStream> anexosBytes;
	private Map<String, String> anexosArquivos;
	private Map<String, MimeType> mimeTypes;
	private AuthType authType;

	public SendMail(){
		this.smtpHost = "";
		this.smtpUser = "";
		this.smtpPassword = "";
		this.smtpPort = "";
		this.emailFrom = "";
		this.localhost = "";
		this.auth = true;
		this.authType = AuthType.TLS;
		this.emailTo = "";
		this.emailToCO = "";
	}
	
	public SendMail(String smtpHost, String smtpUser, String smtpPassword, String smtpPort, 
			String emailFrom, String emailTo, List<String> emailCopy, String localhost, 
			Boolean auth, String subject, String text) {
		this.smtpHost = smtpHost;
		this.smtpUser = smtpUser;
		this.smtpPassword = smtpPassword;
		this.smtpPort = smtpPort;
		this.emailFrom = emailFrom;
		this.emailTo = emailTo;
		this.emailCopy = emailCopy;
		this.localhost = localhost;
		this.auth = auth;
		this.subject = subject;
		this.text = text;
	}

	public String[] removeNull(String[] a) {
	   List<String> removed = new ArrayList<String>();
	   for (String str : a)
	      if (!StringUtils.isBlank(str) && !str.equals("null"))
	         removed.add(str);
	   return removed.toArray(new String[0]);
	}
	
	public void send() throws SendFailedException {
		send(null);
	}
	
	public void send(TypeContentEmail type) throws SendFailedException {
		try {
			if(emailTo==null) {
				throw new SendFailedException("Email de envio não foi corretamente configurado");
			}
			String[] to = removeNull(getArrayEnderecoEmail(emailTo));
			String[] bcc = removeNull(getArrayEnderecoEmail(emailToCO));

			Session session = Session.getInstance(getProperties(), null);

			Message message = new MimeMessage(session);
			message.setFrom(new InternetAddress(emailFrom));
			message.setRecipients(Message.RecipientType.TO, getInternetAddress(to));
			message.setRecipients(Message.RecipientType.BCC, getInternetAddress(bcc));
			InternetAddress[] copyAddress = getInternetAddress(new String[emailCopy.size()]);
			if (copyAddress.length > 0) {
				message.setRecipients(Message.RecipientType.CC, copyAddress);
			}

			((MimeMessage) message).setSubject(subject, ENCODE_UTF_8);

			Multipart multipart = new MimeMultipart();
			multipart.addBodyPart(type == null ? getMimeBodyPart() : getMimeBodyPart(type));
			message.setContent(multipart);

			adicionarAnexos(multipart);

			Transport t = session.getTransport(authType.equals(AuthType.SMTPS) ? "smtps" : "smtp");
			if (auth) {
				t.connect(smtpUser, smtpPassword);
			} else {
				t.connect();

			}
			t.sendMessage(message, message.getAllRecipients());
			t.close();
		} catch (Exception e) {
			throw new SendFailedException("Ocorreu um erro no envio do email.", e);
		}
	}
	
	public void sendEmail(TypeContentEmail type) throws AuthenticationFailedException, MessagingException, Exception {
		String[] to = removeNull(getArrayEnderecoEmail(emailTo));
		String[] bcc = removeNull(getArrayEnderecoEmail(emailToCO));

		Session session = Session.getInstance(getProperties(), null);

		Message message = new MimeMessage(session);
		message.setFrom(new InternetAddress(emailFrom));
		message.setRecipients(Message.RecipientType.TO, getInternetAddress(to));
		message.setRecipients(Message.RecipientType.BCC, getInternetAddress(bcc));
		InternetAddress[] copyAddress = getInternetAddress(new String[emailCopy.size()]);
		if (copyAddress.length > 0) {
			message.setRecipients(Message.RecipientType.CC, copyAddress);
		}

		((MimeMessage) message).setSubject(subject, ENCODE_UTF_8);

		Multipart multipart = new MimeMultipart();
		multipart.addBodyPart(type == null ? getMimeBodyPart() : getMimeBodyPart(type));
		message.setContent(multipart);

		adicionarAnexos(multipart);

		Transport t = session.getTransport(authType.equals(AuthType.SMTPS) ? "smtps" : "smtp");
		if (auth) {
			t.connect(smtpUser, smtpPassword);
		} else {
			t.connect();

		}
		t.sendMessage(message, message.getAllRecipients());
		t.close();
	}

	public boolean testConfiguration() throws AuthenticationFailedException, MessagingException, Exception {
		boolean result = false;

		Session session = Session.getInstance(getProperties(), null);

		Message message = new MimeMessage(session);

		((MimeMessage) message).setSubject(subject, ENCODE_UTF_8);

		Transport t = session.getTransport(authType.equals(AuthType.SMTPS) ? "smtps" : "smtp");
		if (auth) {
			t.connect(smtpUser, smtpPassword);
		} else {
			t.connect();

		}
		t.close();
		result = true;

		return result;
	}

	private String[] getArrayEnderecoEmail(String emailValue) {
		String[] arrayEmail = {};
		if (emailValue.contains(";")){
			arrayEmail = emailValue.split(";");
		} else if (!StringUtils.isBlank(emailValue)){
			arrayEmail =  new String[1];
			arrayEmail[0] = emailValue;
		}
		return arrayEmail;
	}
	
	public void addAnexo(String nome, ByteArrayOutputStream anexo, MimeType mimeType) {
		if(anexosBytes == null) {
			anexosBytes = new HashMap<String, ByteArrayOutputStream>();
		}
		if(mimeTypes == null) {
			mimeTypes = new HashMap<String, MimeType>();
		}
		anexosBytes.put(nome, anexo);
		mimeTypes.put(nome, mimeType);
	}
	
	public void addAnexo(String nome, String anexo) {
		if(anexosArquivos== null) {
			anexosArquivos = new HashMap<String, String>();
		}
		anexosArquivos.put(nome, anexo);
	}

	private BodyPart getMimeBodyPart() throws MessagingException {
		BodyPart messageBodyPart = new MimeBodyPart();
		((MimeBodyPart)messageBodyPart).setText(text, ENCODE_UTF_8);
		return messageBodyPart;
	}

	private BodyPart getMimeBodyPart(TypeContentEmail type) throws MessagingException {
		BodyPart messageBodyPart = new MimeBodyPart();
		messageBodyPart.setContent(text, type.valor);
		return messageBodyPart;
	}

	private InternetAddress[] getInternetAddress(String[] address) throws AddressException {
		InternetAddress[] toAddress = new InternetAddress[address.length];
		for (int i = 0; i < address.length; i++) {
			toAddress[i] = new InternetAddress(address[i]);
		}
		return toAddress;
	}

	private Properties getProperties() {
		Properties props = System.getProperties();
		props.put("mail.smtp.host", smtpHost);
		props.put("mail.smtp.ssl.trust", smtpHost);
		
		if (localhost != null) {
			props.put("mail.smtp.localhost", localhost);
		}

		if (auth) {
			props.setProperty("mail.smtp.user", smtpUser);
		}
		
		if (authType.equals(AuthType.TLS)) {
			props.setProperty("mail.smtp.starttls.enable", "true");
			props.setProperty("mail.smtp.starttls.required", "true");
		} else if (authType.equals(AuthType.SSL)) {
			props.put("mail.smtp.socketFactory.port", smtpPort);
			props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
		} else if (authType.equals(AuthType.SMTPS)) {
			props.setProperty("mail.smtps.ssl.checkserveridentity", "true");
			props.setProperty("mail.smtps.quitwait", "false");
		} else if (authType.equals(AuthType.NONE)) {
			props.setProperty("mail.smtp.starttls.enable", "true");
			props.setProperty("mail.smtp.starttls.required", "false");
			props.setProperty("mail.smtp.ssl.trust", "*");
			props.setProperty("mail.smtp.ssl.checkserveridentity", "false");
			props.setProperty("mail.smtp.ssl.enable", "false");
		}

		props.setProperty("mail.smtp.auth", auth.toString());
		props.setProperty("mail.smtp.port", smtpPort);
		return props;
	}

	private void adicionarAnexos(Multipart multipart) throws MessagingException, IOException {
		anexarArquivosBytes(multipart);
		anexarArquivos(multipart);
	}

	private void anexarArquivosBytes(Multipart multipart) throws MessagingException, IOException {
		if(anexosBytes != null) {
			for (Entry<String, ByteArrayOutputStream> anexo : anexosBytes.entrySet()) {
				BodyPart bodyPart = new MimeBodyPart();
				MimeType mimeType = mimeTypes.get(anexo.getKey());
				DataSource bds = new ByteArrayDataSource(new ByteArrayInputStream(anexo.getValue().toByteArray()), mimeType.toString());
				bodyPart.setDataHandler(new DataHandler(bds)); 
				bodyPart.setFileName(anexo.getKey());
				
				multipart.addBodyPart(bodyPart);
			}
		}
	}
	
	private void anexarArquivos(Multipart multipart) throws MessagingException {
		if(anexosArquivos != null) {
			for (Entry<String, String> anexo : anexosArquivos.entrySet()) {
				BodyPart bodyPart = new MimeBodyPart();
				DataSource bds = new FileDataSource(anexo.getValue());
				bodyPart.setDataHandler(new DataHandler(bds)); 
				bodyPart.setFileName(anexo.getKey());
				multipart.addBodyPart(bodyPart);
			}
		}
	}

	public String getSmtpHost() {
		return smtpHost;
	}

	public void setSmtpHost(String smtpHost) {
		this.smtpHost = smtpHost;
	}

	public String getSmtpUser() {
		return smtpUser;
	}

	public void setSmtpUser(String smtpUser) {
		this.smtpUser = smtpUser;
	}

	public String getSmtpPassword() {
		return smtpPassword;
	}

	public void setSmtpPassword(String smtpPassword) {
		this.smtpPassword = smtpPassword;
	}

	public String getSmtpPort() {
		return smtpPort;
	}

	public void setSmtpPort(String smtpPort) {
		this.smtpPort = smtpPort;
	}

	public String getEmailFrom() {
		return emailFrom;
	}

	public void setEmailFrom(String emailFrom) {
		this.emailFrom = emailFrom;
	}

	public String getEmailTo() {
		return emailTo;
	}

	public void setEmailTo(String emailTo) {
		this.emailTo = emailTo;
	}

	public List<String> getEmailCopy() {
		return emailCopy;
	}

	public void setEmailCopy(List<String> emailCopy) {
		this.emailCopy = emailCopy;
	}

	public String getLocalhost() {
		return localhost;
	}

	public void setLocalhost(String localhost) {
		this.localhost = localhost;
	}

	public String getSubject() {
		return subject;
	}

	public void setSubject(String subject) {
		this.subject = subject;
	}

	public String getText() {
		return text;
	}

	public void setText(String text) {
		this.text = text;
	}

	public String getEmailToCO() {
		return emailToCO;
	}

	public void setEmailToCO(String emailToCO) {
		this.emailToCO = emailToCO;
	}
	
	public Boolean getAuth() {
		return auth;
	}

	public void setAuth(Boolean auth) {
		this.auth = auth;
	}

	public Map<String, String> getAnexosArquivos() {
		return anexosArquivos;
	}

	public void setAnexosArquivos(Map<String, String> anexosArquivos) {
		this.anexosArquivos = anexosArquivos;
	}

	public AuthType getAuthType() {
		return authType;
	}

	public void setAuthType(AuthType authType) {
		this.authType = authType;
	}
	
}