package eu.dnetlib.pid.service;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.socialhistoryservices.pid.CreatePidRequestType;
import org.socialhistoryservices.pid.CreatePidResponseType;
import org.socialhistoryservices.pid.DeletePidRequestType;
import org.socialhistoryservices.pid.DeletePidResponseType;
import org.socialhistoryservices.pid.DeletePidsRequestType;
import org.socialhistoryservices.pid.DeletePidsResponseType;
import org.socialhistoryservices.pid.GetPidByAttributeRequestType;
import org.socialhistoryservices.pid.GetPidByAttributeResponseType;
import org.socialhistoryservices.pid.GetPidRequestType;
import org.socialhistoryservices.pid.GetPidResponseType;
import org.socialhistoryservices.pid.GetQuickPidRequestType;
import org.socialhistoryservices.pid.GetQuickPidResponseType;
import org.socialhistoryservices.pid.LocAttType;
import org.socialhistoryservices.pid.LocationType;
import org.socialhistoryservices.pid.PidResource;
import org.socialhistoryservices.pid.PidType;
import org.socialhistoryservices.pid.UpdatePidRequestType;
import org.socialhistoryservices.pid.UpdatePidResponseType;
import org.socialhistoryservices.pid.UpsertPidRequestType;
import org.socialhistoryservices.pid.UpsertPidResponseType;

/**
 * Class that interact directly with the IISG Pid web service.
 * 
 * @author alessia
 * 
 */

public class PidServiceClient {
	/** Logger. */
	private static final Log log = LogFactory.getLog(PidServiceClient.class); // NOPMD by marko on 11/24/08 5:02 PM
	/** Web resource for the Pid web service. */
	private PidResource port;

	/***
	 * Creates a Pid.
	 * 
	 * @param namingAuthority
	 *            na holding the create pid
	 * @return a pid under the control of the given naming authority
	 */
	public PidType createPid(final String namingAuthority) {
		final CreatePidRequestType requestType = new CreatePidRequestType();
		requestType.setNa(namingAuthority);
		final PidType pt = new PidType();
		requestType.setHandle(pt);
		final CreatePidResponseType response = this.port.createPid(requestType);
		log.debug("createPid.result=" + response);
		return response.getHandle();
	}

	/**
	 * Delets all pids under the control of the given naming authority.
	 * <p>
	 * This method is available only for test NAs, which are those containing a dot, e.g., 1066.1
	 * 
	 * @param namingAuthority
	 *            na whose pids have to be deleted
	 * @return the number of delete pids
	 */
	public int deletePids(final String namingAuthority) {

		final DeletePidsRequestType requestType = new DeletePidsRequestType();
		requestType.setNa(namingAuthority);
		final DeletePidsResponseType response = this.port.deletePids(requestType);
		log.debug("deletePids.result=" + response);
		return (int) response.getCount();
	}

	public boolean deletePid(final String pid) {
		final DeletePidRequestType requestType = new DeletePidRequestType();
		requestType.setPid(pid);
		final DeletePidResponseType response = this.port.deletePid(requestType);
		log.debug("deletePid.result=" + response);
		return response.isDeleted();
	}

	/**
	 * Gets the PidType instance associated to the given pid string.
	 * 
	 * @param pid
	 *            the pid string
	 * @return a PidType
	 */
	public PidType getPid(final String pid) {

		final GetPidRequestType requestType = new GetPidRequestType();
		requestType.setPid(pid);
		final GetPidResponseType response = this.port.getPid(requestType);
		log.debug("getPid.result=" + response);
		return response.getHandle();
	}

	/**
	 * Gets the PidType instances under the control of the given naming authority, having an attribute with the provided
	 * value.
	 * 
	 * @param namingAuthority
	 *            na holding the pids
	 * @param attributeValue
	 *            value to match in any of the available attributes
	 * @return a List of PidType instances matching the criteria
	 */
	public List<PidType> getPidByAttribute(final String namingAuthority, final String attributeValue) {

		final GetPidByAttributeRequestType requestType = new GetPidByAttributeRequestType();
		requestType.setNa(namingAuthority);
		requestType.setAttribute(attributeValue);
		final GetPidByAttributeResponseType response = this.port.getPidByAttribute(requestType);
		log.debug("getPidByAttribute.result=" + response);
		return response.getHandle();
	}

	/**
	 * Creates/updates/binds/unbinds a Pid.
	 * <p>
	 * Pid creation: when the localIdentifier is not bound to a known pid, the webservice creates a pid and then binds
	 * it to the resolveUrl and localIdentifier.
	 * </p>
	 * <p>
	 * Pid lookup: when the localIdentifier is bound to an existing Pid, the method will echo back all data bound to the
	 * pid.
	 * </p>
	 * <p>
	 * Pid update: when the localIdentifier is bound to an existing Pid and the supplied resolveUrl is different to the
	 * bound resolveUrl, a rebind will be made.
	 * </p>
	 * 
	 * 
	 * @param namingAuthority
	 *            na holding the pid
	 * @param localID
	 *            localID attribute to associate to the Pid
	 * @param resolveURL
	 *            resolveURL of the Pid
	 * @return a PidType instance
	 */
	public PidType getQuickPid(final String namingAuthority, final String localID, final String resolveURL) {

		final GetQuickPidRequestType requestType = new GetQuickPidRequestType();
		requestType.setNa(namingAuthority);
		requestType.setLocalIdentifier(localID);
		requestType.setResolveUrl(resolveURL);
		final GetQuickPidResponseType response = this.port.getQuickPid(requestType);
		log.debug("getQuickPid.result=" + response);
		return response.getHandle();
	}

	/**
	 * Updates an existing Pid with the provided information.
	 * 
	 * @param pid
	 *            string identifier
	 * @param resolveURL
	 *            resolveURL of the Pid
	 * @param localID
	 *            localID attribute to associate to the Pid
	 * @param locations
	 *            locations attributes to associate to the pid
	 * @return
	 */
	public PidType update(final String pid, final String resolveURL, final String localID, final List<LocationType> locations) {

		final PidType pidType = new PidType();
		pidType.setPid(pid);
		pidType.setLocalIdentifier(localID);
		pidType.setResolveUrl(resolveURL);
		final LocAttType locs = new LocAttType();
		if (locations != null)
			locs.getLocation().addAll(locations);
		pidType.setLocAtt(locs);
		return this.updatePid(pidType);

	}

	/**
	 * Updates an existing Pid with information contained in the given PidType instance.
	 * 
	 * @param pid
	 *            PidType instance
	 * @return the updated PidType
	 */
	public PidType updatePid(final PidType pid) {
		final UpdatePidRequestType requestType = new UpdatePidRequestType();
		requestType.setHandle(pid);
		final UpdatePidResponseType response = this.port.updatePid(requestType);
		log.debug("updatePid.result=" + response);
		return response.getHandle();
	}

	public PidType upsert(String namingAuthority, PidType pid) {
		UpsertPidRequestType requestType = new UpsertPidRequestType();
		requestType.setHandle(pid);
		requestType.setNa(namingAuthority);
		UpsertPidResponseType response = this.port.upsertPid(requestType);
		log.debug("upsertPid.result=" + response);
		return response.getHandle();
	}

	/**
	 * Creates/Updates an existing Pid with the provided information.
	 * 
	 * @param pid
	 *            string identifier
	 * @param resolveURL
	 *            resolveURL of the Pid
	 * @param localID
	 *            localID attribute to associate to the Pid
	 * @param locations
	 *            locations attributes to associate to the pid
	 * @return
	 */
	public PidType upsert(String namingAuthority, final String pid, final String resolveURL, final String localID, final List<LocationType> locations) {

		final PidType pidType = new PidType();
		pidType.setPid(pid);
		pidType.setLocalIdentifier(localID);
		pidType.setResolveUrl(resolveURL);
		final LocAttType locs = new LocAttType();
		if (locations != null)
			locs.getLocation().addAll(locations);
		pidType.setLocAtt(locs);
		return this.upsert(namingAuthority, pidType);
	}

	public PidResource getPort() {
		return this.port;
	}

	public void setPort(final PidResource port) {
		this.port = port;
	}
}
