package com.tandbergtv.metadatamanager.spec;

import java.io.InputStream;
import java.net.URL;
import java.util.List;
import java.util.Map;

import org.w3c.dom.Document;

import com.tandbergtv.metadatamanager.exception.InvalidRevisionException;
import com.tandbergtv.metadatamanager.exception.MetadataException;
import com.tandbergtv.metadatamanager.exception.SearchException;
import com.tandbergtv.metadatamanager.exception.TranslationException;
import com.tandbergtv.metadatamanager.model.Asset;
import com.tandbergtv.metadatamanager.model.RootAssetRevision;
import com.tandbergtv.metadatamanager.model.Spec;
import com.tandbergtv.metadatamanager.specimpl.IdentifierBase;
import com.tandbergtv.metadatamanager.specimpl.ttv.TTVId;
import com.tandbergtv.metadatamanager.validation.ValidationError;

/**
 * This interface is implemented by every specification handler.
 * 
 */
public interface ISpecHandler {

	/**
	 * Saves the document which is in the format specified by implementation. It
	 * may involve searching if the asset already exists.
	 * 
	 * <br><br><b>Note: This method is same as Save. Only difference is the return type.</b>
	 * 
	 * @param doc
	 * @return Returns list of top level TTV Ids
	 * 
	 */
	List<IIdentifier> put(Document doc) throws MetadataException;
	
	/**
	 * Same as {@link #put(Document)}, but with additional information related to Revision Tracking
	 * @param doc
	 * @param revisionSource
	 * @param revisionComment
	 * @param externalRevision
	 * @return
	 * @throws MetadataException
	 */
	List<IIdentifier> put(Document doc, String revisionSource, String revisionComment, String externalRevision) throws MetadataException;
	
	/**
	 * Searches for the incoming doc in the database and merges the existing
	 * asset with the incoming doc and returns it (Merge target is the new
	 * asset). If the incoming doc was not found in the db, it converts it to
	 * asset model and returns it.
	 * 
	 * @param doc
	 * @return
	 * @throws MetadataException
	 */
	List<Asset> mergeWithoutSave(Document doc) throws MetadataException;

	/**
	 * When the target asset is known, merges the incoming document to it
	 * @param doc
	 * @return 
	 * @throws MetadataException
	 */
	List<Asset> mergeWithoutSave(Long assetId, Document doc) throws MetadataException;

	
	/**
	 * Gets the document which is in the format specified by implementation.
	 * 
	 * @param id
	 * @return Document
	 * 
	 */
	Document get(IIdentifier id) throws MetadataException, SearchException,
			TranslationException;
	
	
	/**
	 * Gets a list of IDentifiers extracted from the doc. 
	 * 
	 * @param doc
	 * @return List<IIDentifier>
	 * @throws MetadataException
	 */
	
	List<IIdentifier> getIdentifiers(Document doc) throws MetadataException;
	
	/**
	 * Every implementation should be able to create the corresponding
	 * identifier by extracting the identifying fields from the given fields
	 * doc.
	 * 
	 * @param fields
	 * @return
	 */
	IIdentifier extractId(Asset asset);

	/**
	 * Gets the revision history of a top-level asset, in the order of oldest to latest
	 * 
	 * @param id
	 * @return Document
	 * 
	 */
	List<RootAssetRevision> getRevisions(IIdentifier id) throws MetadataException, SearchException,
			TranslationException;
	
	/**
	 * Same as {@link #get(IIdentifier))}, but return an Asset for a specific revision.
	 * 
	 * @param id
	 * @param revision  a particular revision of a top-level asset
	 * @return Document
	 * 
	 */
	Document get(IIdentifier id, String revision) throws MetadataException, SearchException,
			TranslationException;
	
	/**
	 * converts an asset model to spec specific XML
	 * 
	 * @param asset
	 * @return
	 * @throws TranslationException 
	 */
	Document convertAssetToXMLDocument(Asset asset) throws TranslationException;
	
	/**
	 * Rollback an Asset to one of its previous revisions
	 * !!! This will create a new revision for the Asset
	 * @param ttvId
	 * @param revision
	 * @return the Asset of revision
	 * @throws MetadataException
	 * @throws SearchException
	 * @throws TranslationException
	 * @throws InvalidRevisionException 
	 */
	Document rollBackToRevision(TTVId ttvId, String revision)throws MetadataException, SearchException,
	TranslationException, InvalidRevisionException;

	/**
	 * Merges the asset in the incoming doc with the existing asset in the db.
	 * This merge is a prune and merge. The incoming asset is considered to be
	 * most complete (latest and greatest).
	 * 
	 * Note: If some other spec specific items or fields exist they will still
	 * be maintained.
	 * 
	 * <br><br><b>Note: This method is same as Put. Only difference is the return type.</b>
	 * 
	 * @param doc
	 * @return Asset (unsaved)
	 * @throws MetadataException
	 */
	List<Asset> save(Document doc) throws MetadataException;
	
	/**
	 * Same as {@link #save(Document)))}, but with additional information related to Revision Tracking
	 * @param doc
	 * @param revisionSource
	 * @param revisionComment
	 * @param externalRevision
	 * @return
	 * @throws MetadataException
	 */
	List<Asset> save(Document doc, String revisionSource, String revisionComment, String externalRevision) throws MetadataException;

	/**
	 * merges the incoming asset with the existing asset in the database. Merge
	 * is of kind addOverwrite. Nothing is pruned.
	 * 
	 * @param doc
	 * @return
	 * @throws MetadataException
	 */
	List<Asset> incrementalSave(Document doc) throws MetadataException;

	/**
	 * Deletes assets which match the search criteria in the specification
	 * provided.
	 * 
	 * @param criteria
	 * @return list of deleted asset identifiers
	 */
	List<IIdentifier> deleteAll(List<IIdentifier> ids);

	/**
	 * Deletes the asset matching the criteria.
	 * 
	 * @param criteria
	 */
	void deleteUnique(IIdentifier id) throws SearchException;

	/**
	 * Validates the given doc against the schema known to this specification.
	 * 
	 * @param doc
	 * @return list of error objects
	 */
	List<ValidationError> validateSchema(Document doc);

	/**
	 * Gets all validators registered with this spec handler.
	 * 
	 * @return
	 */
	Map<String, IValidator> getRuleValidators();

	/**
	 * Gets rule manager registered with this spec handler.
	 * 
	 * @return IRuleManager
	 */
	IRuleManager getRuleManager();

	/**
	 * Validates a document using custom defined rules.
	 * 
	 * @param Document
	 *            toBeValidated
	 * @param IValidator
	 *            baseRules
	 * @param String
	 *            nameOfRuleSet
	 * @return list validation errors
	 */
	List<ValidationError> customValidate(Document toBeValidated,
			IValidator baseRules, String nameOfRuleSet);

	/**
	 * Validates a document ignoring errors not for the field defined in the
	 * given xpath.
	 * 
	 * @param Document
	 *            toBeValidated
	 * @param IValidator
	 *            baseRules
	 * @param String
	 *            xpathOfField
	 * @return list validation errors
	 */
	List<ValidationError> validateField(Document toBeValidated,
			IValidator baseRules, String xpathOfField);
	
	/**
	 * Given a document, determines the spec.
	 * 
	 * @param doc
	 * @return Spec
	 */
	Spec getSpecName(Document doc);

	/**
	 * gets the Identifier for this spec 
	 * @return IDentifier
	 */
	IdentifierBase getIdentifier();

	/**
	 * mapping file that has the spec specific xpath and ttv xpath relation for
	 * IDs
	 * 
	 * @param url
	 */
	void setMappingResourceUrl(URL url);

	/**
	 * file that has all ttv xpaths for this spec
	 * @param specSpecificTTVXpathsFileNameStream
	 */
	void setSpecSpecificTTVXpathsStream(
			InputStream specSpecificTTVXpathsFileNameStream);

	/**
	 * translator that converts from spec to TTV
	 * @param toTranslator
	 */
	void setToTTV(ITranslator toTranslator);

	/**
	 * translator that converts from ttv to spec
	 * @param fromTranslator
	 */
	void setFromTTV(ITranslator fromTranslator);

	/**
	 * ruleManager that specifies the custom Rules for the spec.
	 * @param ruleManager
	 */
	void setRuleManager(IRuleManager ruleManager);

	/**
	 * validator for the spec
	 * @param validators
	 */
	void setRuleValidators(Map<String, IValidator> validators);
	
	/**
	 * Checks if the given asset already exists based on FieldRevisons,
	 * !!!Not Fields !!!
	 * @param asset
	 * @return
	 * @throws SearchException
	 */
	public TTVId getTTVId(Asset asset, Asset rootAsset)throws SearchException;
}
