class IdinSender extends SenderWithParametersBase implements HasPhysicalDestination {

	@Override
	public Message sendMessage(Message message, IPipeLineSession session) throws SenderException, TimeOutException {

		Element queryElement = null;
		try {
			if (XmlUtils.isWellFormed(message.asString(), "idin")) {
				queryElement = XmlUtils.buildElement(message.asString());
			} else {
				queryElement = XmlUtils.buildElement("<idin/>");
			}
		} catch (DomBuilderException | IOException e) {
			throw new SenderException(e);
		}

		XmlBuilder result = new XmlBuilder("result");
		ErrorResponse error = null;

		if(getAction().equals("DIRECTORY")) {
			DirectoryResponse response = getCommunicator().getDirectory();

			if(response.getIsError()) {
				error = response.getErrorResponse();
			}
			else {
				XmlBuilder issuers = new XmlBuilder("issuers");

				if(XmlUtils.getChildTagAsBoolean(queryElement, "issuersByCountry")) {
					for (Entry<String, List<Issuer>> entry : response.getIssuersByCountry().entrySet()) {
						XmlBuilder countryXml = new XmlBuilder("country");
						String country = entry.getKey();
						countryXml.addAttribute("name", country);
	
						for(Issuer issuer : entry.getValue()) {
							XmlBuilder issuerXml = new XmlBuilder("issuer");
							issuerXml.setValue(issuer.getIssuerName());
							issuerXml.addAttribute("id", issuer.getIssuerID());
							countryXml.addSubElement(issuerXml);
						}
						issuers.addSubElement(countryXml);
					}
				}
				else {
					for(Issuer issuer : response.getIssuers()) {
						XmlBuilder issuerXml = new XmlBuilder("issuer");
						issuerXml.setValue(issuer.getIssuerName());
						issuerXml.addAttribute("id", issuer.getIssuerID());
						issuerXml.addAttribute("country", issuer.getIssuerCountry());
						issuers.addSubElement(issuerXml);
					}
				}
				result.addSubElement(issuers);

				XmlBuilder timestamp = new XmlBuilder("timestamp");
				Date txDate = response.getDirectoryDateTimestamp().toGregorianCalendar().getTime();
				timestamp.setValue(DateUtils.format(txDate, "yyyy-MM-dd HH:mm:ss.SSS"), false);
				result.addSubElement(timestamp);
			}

			if(StringUtils.isNotEmpty(response.getRawMessage())) {
				log.debug(response.getRawMessage());
			}
		}
		else if(getAction().equals("RESPONSE")) {
			String transactionID = XmlUtils.getChildTagAsString(queryElement, "transactionID");
			if(StringUtils.isEmpty(transactionID))
				throw new SenderException("no transactionID was supplied");

			StatusRequest statusRequest = new StatusRequest();
			statusRequest.setTransactionID(transactionID);
			StatusResponse response = getCommunicator().getResponse(statusRequest);

			if(response.getIsError()) {
				error = response.getErrorResponse();
			}
			else {
				XmlBuilder status = new XmlBuilder("status");
				status.setValue(response.getStatus(), false);
				result.addSubElement(status);

				if(response.getStatus() == StatusResponse.Success) {
					SamlResponse saml = response.getSamlResponse();
					XmlBuilder samlXml = new XmlBuilder("saml");

					XmlBuilder acquirerId = new XmlBuilder("acquirerId");
					acquirerId.setValue(saml.getAcquirerID());
					samlXml.addSubElement(acquirerId);

					XmlBuilder attributes = new XmlBuilder("attributes");
					for (Entry<String, String> entry : saml.getAttributes().entrySet()) {
						XmlBuilder attribute = new XmlBuilder("attribute");
						attribute.addAttribute("name", entry.getKey());
						attribute.setValue(entry.getValue());
						attributes.addSubElement(attribute);
					}
					samlXml.addSubElement(attributes);

					XmlBuilder merchantReference = new XmlBuilder("merchantReference");
					merchantReference.setValue(saml.getMerchantReference());
					samlXml.addSubElement(merchantReference);

					XmlBuilder version = new XmlBuilder("version");
					version.setValue(saml.getAcquirerID());
					samlXml.addSubElement(version);

					result.addSubElement(samlXml);
				}

				XmlBuilder transactionIdXml = new XmlBuilder("transactionID");
				transactionIdXml.setValue(response.getTransactionID(), false);
				result.addSubElement(transactionIdXml);

				XmlBuilder timestamp = new XmlBuilder("timestamp");
				Date txDate = response.getStatusDateTimestamp().toGregorianCalendar().getTime();
				timestamp.setValue(DateUtils.format(txDate, "yyyy-MM-dd HH:mm:ss.SSS"), false);
				result.addSubElement(timestamp);
			}

			if(StringUtils.isNotEmpty(response.getRawMessage())) {
				log.debug(response.getRawMessage());
			}
		}
		else if(getAction().equals("AUTHENTICATE")) {
			AuthenticationRequest authRequest = new AuthenticationRequest();

			String issuerId = XmlUtils.getChildTagAsString(queryElement, "issuerId");
			if(StringUtils.isEmpty(issuerId))
				throw new SenderException("no issuerId was supplied");
			authRequest.setIssuerID(issuerId);

			String language = XmlUtils.getChildTagAsString(queryElement, "language");
			if(StringUtils.isNotEmpty(language))
				authRequest.setLanguage(language);

			String expirationPeriod = XmlUtils.getChildTagAsString(queryElement, "expirationPeriod");
			if(StringUtils.isNotEmpty(expirationPeriod)) {
				try {
					Duration duration = DatatypeFactory.newInstance().newDuration(expirationPeriod);
					authRequest.setExpirationPeriod(duration);
				} catch (DatatypeConfigurationException e) {
					throw new SenderException(e);
				}
			}

			String requestedServiceId = XmlUtils.getChildTagAsString(queryElement, "requestedServiceId");
			if(StringUtils.isNotEmpty(requestedServiceId)) {
				authRequest.setRequestedServiceID(new ServiceId(requestedServiceId));
			}

			String merchantReference = XmlUtils.getChildTagAsString(queryElement, "merchantReference");
			if(StringUtils.isNotEmpty(requestedServiceId))
				authRequest.setMerchantReference(merchantReference);

			AssuranceLevel assuranceLevel = AssuranceLevel.Loa3;
			String assurance = XmlUtils.getChildTagAsString(queryElement, "assuranceLevel");
			if(StringUtils.isNotEmpty(assurance))
				assuranceLevel = AssuranceLevel.valueOf(assurance);
			authRequest.setAssuranceLevel(assuranceLevel);


			String entranceCode = XmlUtils.getChildTagAsString(queryElement, "entranceCode");
			if(StringUtils.isNotEmpty(entranceCode))
				authRequest.setEntranceCode(entranceCode);

			AuthenticationResponse response = getCommunicator().newAuthenticationRequest(authRequest);
			if(response.getIsError()) {
				error = response.getErrorResponse();
			}
			else {
				XmlBuilder authenticationURL = new XmlBuilder("authenticationURL");
				authenticationURL.setValue(response.getIssuerAuthenticationURL(), false);
				result.addSubElement(authenticationURL);

				XmlBuilder transactionIdXml = new XmlBuilder("transactionID");
				transactionIdXml.setValue(response.getTransactionID(), false);
				result.addSubElement(transactionIdXml);

				XmlBuilder creationTime = new XmlBuilder("creationTime");
				Date txDate = response.getTransactionCreateDateTimestamp().toGregorianCalendar().getTime();
				creationTime.setValue(DateUtils.format(txDate, "yyyy-MM-dd HH:mm:ss.SSS"), false);
				result.addSubElement(creationTime);
			}

			if(StringUtils.isNotEmpty(response.getRawMessage())) {
				log.debug(response.getRawMessage());
			}
		}

		if(error != null) {
			XmlBuilder errorXml = new XmlBuilder("error");
			XmlBuilder statusCodeXml = new XmlBuilder("statusCode");
			statusCodeXml.setValue(error.getErrorCode());
			errorXml.addSubElement(statusCodeXml);

			XmlBuilder detailsXml = new XmlBuilder("details");
			detailsXml.setValue(error.getErrorDetails());
			errorXml.addSubElement(detailsXml);

			XmlBuilder messageXml = new XmlBuilder("message");
			messageXml.setValue(error.getErrorMessage());
			errorXml.addSubElement(messageXml);

			result.addSubElement(errorXml);
		}

		return new Message(result.toXML());
	}

}