package jp.co.ase.izpack.util;

import java.util.Hashtable;

import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;

import com.izforge.izpack.installer.AutomatedInstallData;
import com.izforge.izpack.installer.DataValidator;

public class LDAPSearchValidator extends LDAPConnectValidator implements DataValidator {

	/**
	 * 検索
	 *
	 * @param idata
	 * @return
	 */
	public boolean canSearch(AutomatedInstallData idata) {
		boolean canSearch = false;
		String ldap_base_dn = idata.getVariable("ldap_base_dn");
		String ldap_user_attribute = idata.getVariable("ldap_user_attribute");
		String admin_account = idata.getVariable("admin_account");
		// フィルタを作成
		String filter = "(" + ldap_user_attribute + "=" + admin_account + ")";

		SearchControls searchControls = new SearchControls();
		searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
		try {
			// 検索結果のエントリを取得
			NamingEnumeration<SearchResult> result = ctx.search(ldap_base_dn, filter,
			        searchControls);

			String adminDn = "";
			while (result.hasMore()) {
				SearchResult sr = result.next();
				// dnを取得
				adminDn = sr.getNameInNamespace();
				String host = idata.getVariable("ldap_host");
				String port = idata.getVariable("ldap_port");
				String baseDn = idata.getVariable("ldap_base_dn");
				String adminPassword = idata.getVariable("admin_password");

				// Bind認証させる
				Hashtable<String, String> env = getLdapEnv(host, port, baseDn, adminDn,
				        adminPassword);
				boolean canCertify = canCertify(env);
				if (canCertify == false) {
					return false;
				}

				// bind認証がOKなら、苗字、名前、メール属性をチェック
				Attributes attributes = sr.getAttributes();
				idata.setVariable("displayMailSnGivenNameLdapSetting", "false");

				String ldap_mail_attribute = idata.getVariable("ldap_mail_attribute");
				if (ldap_mail_attribute != null && ldap_mail_attribute.length() != 0) {
					Attribute mailAttr = attributes.get(ldap_mail_attribute);
					if (mailAttr == null) {
						idata.setVariable("displayMailSnGivenNameLdapSetting", "true");
						idata.setVariable("notExistsLdapMail", "true");
					} else {
						String mailValue = (String) mailAttr.get();
						boolean isOK = isOKMailAddress(mailValue);
						if(isOK == true) {
							idata.setVariable("notExistsLdapMail", "false");
						} else {
							idata.setVariable("displayMailSnGivenNameLdapSetting", "true");
							idata.setVariable("notExistsLdapMail", "true");
						}
					}
				} else {
					// 属性が入力されなかった場合は、後の画面で値を入力させるために使うフラグをtrueにする
					idata.setVariable("displayMailSnGivenNameLdapSetting", "true");
					idata.setVariable("notExistsLdapMail", "true");
				}

				// LDAP上に属性がなければ後の画面で値を入力させるために使うフラグを設定
				String ldap_last_name_attribute = idata.getVariable("ldap_last_name_attribute");
				if (ldap_last_name_attribute != null && ldap_last_name_attribute.length() != 0) {
					Attribute snAttr = attributes.get(ldap_last_name_attribute);
					if (snAttr == null) {
						idata.setVariable("displayMailSnGivenNameLdapSetting", "true");
						idata.setVariable("notExistsLdapSn", "true");
					} else {
						String snValue = (String) snAttr.get();
						boolean isOK = isOKLastNameAndFirstName(snValue);
						if(isOK == true) {
							idata.setVariable("notExistsLdapSn", "false");
						} else {
							idata.setVariable("displayMailSnGivenNameLdapSetting", "true");
							idata.setVariable("notExistsLdapSn", "true");
						}
					}
				}else {
					// 属性が入力されなかった場合は、後の画面で値を入力させるために使うフラグをtrueにする
					idata.setVariable("displayMailSnGivenNameLdapSetting", "true");
					idata.setVariable("notExistsLdapSn", "true");
				}

				String ldap_first_name_attribute = idata.getVariable("ldap_first_name_attribute");
				if (ldap_first_name_attribute != null && ldap_first_name_attribute.length() != 0) {
					Attribute givenNameAttr = attributes.get(ldap_first_name_attribute);
					if (givenNameAttr == null) {
						idata.setVariable("displayMailSnGivenNameLdapSetting", "true");
						idata.setVariable("notExistsLdapGivenName", "true");
					} else {
						String givenNameValue = (String) givenNameAttr.get();
						boolean isOK = isOKLastNameAndFirstName(givenNameValue);
						if(isOK == true) {
							idata.setVariable("notExistsLdapGivenName", "false");
						} else {
							idata.setVariable("displayMailSnGivenNameLdapSetting", "true");
							idata.setVariable("notExistsLdapGivenName", "true");
						}
					}
				} else {
					// 属性が入力されなかった場合は、後の画面で値を入力させるために使うフラグをtrueにする
					idata.setVariable("displayMailSnGivenNameLdapSetting", "true");
					idata.setVariable("notExistsLdapGivenName", "true");
				}

				canSearch = true;
			}

			if (adminDn.length() == 0) {
			}

		} catch (NamingException e) {
			canSearch = false;
		}
		return canSearch;
	}

	private boolean isOKLastNameAndFirstName(String value) {
		return RegularExpressionUtils.isOKPatten(value, "^[\\w\\s\\'\\-\\.\\p{InHiragana}\\p{InKatakana}\\p{InHalfwidthAndFullwidthForms}\\p{InCJKUnifiedIdeographs}]{1,30}$");
	}

	private boolean isOKMailAddress(String value) {
		return RegularExpressionUtils.isOKPatten(value, "^([^@\\s]+)@((?:[-a-z0-9]+\\.)+[a-z]{2,})$");
	}

	/**
	 * エラーメッセージを返却する。
	 */
	public String getErrorMessageId() {
		return "アカウントが見つかりませんでした。以下の可能性があります。\r\n・LDAP検索ベースが誤っている\r\n・管理者アカウントまたはパスワードが誤っている\r\n再度入力内容をご確認ください。";
	}

	/**
	 * validate処理を行う。
	 */
	public Status validateData(AutomatedInstallData idata) {
		Status status = Status.ERROR;
		String ldap_setting = idata.getVariable("ldap_setting");
		if ("1".equals(ldap_setting) || "true".equals(ldap_setting)) {
			// 外部LDAPの設定を行うとき
			boolean canCconnect = canCconnect(idata);
			if (canCconnect == true) {
				boolean canSearch = canSearch(idata);
				if (canSearch == true) {
					status = Status.OK;
				}
			}
			if (ctx != null) {
				try {
					ctx.close();
				} catch (Exception e) {
				}
			}
		} else {
			// 外部LDAPを使わないとき
			status = Status.OK;
		}
		return status;
	}
}
