package com.bagri.core.model;

import static com.bagri.core.api.TransactionManagement.TX_NO;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Represents unique index on XDM Document
 * 
 * @author Denis Sukhoroslov
 *
 */
public class UniqueDocument extends IndexedValue {
	
	private List<UniqueValue> docs = new ArrayList<>();
	
	/**
	 * default constructor
	 */
	public UniqueDocument() {
		super();
	}
	
	/**
	 * 
	 * @param docKey the internal document key
	 */
	public UniqueDocument(long docKey) {
		super();
		addDocument(docKey, TX_NO);
	}

	/**
	 * 
	 * @param docKeys the collection of internal document keys
	 */
	public UniqueDocument(Collection<Long> docKeys) {
		super();
		if (docKeys != null) {
			for (Long docKey: docKeys) {
				addDocument(docKey, TX_NO);
			}
		}
	}

	/**
	 * @return the number of indexed documents
	 */
	@Override
	public int getCount() {
		int cnt = 0;
		for (int i=docs.size() - 1; i >=0; i--) {
			UniqueValue doc = docs.get(i);
			if (doc.getTxFinish() == TX_NO) {
				cnt++;
			}
		}
		return cnt;
	}
	
	/**
	 * @return the indexed document key
	 */
	@Override
	public long getDocumentKey() {
		for (int i=docs.size() - 1; i >=0; i--) {
			UniqueValue doc = docs.get(i);
			if (doc.getTxFinish() == TX_NO) {
				return doc.getDocumentKey();
			}
		}
		return 0;
	}
	
	/**
	 * @return the collection of indexed document keys
	 */
	@Override
	public Set<Long> getDocumentKeys() {
		Set<Long> docIds = new HashSet<>(1);
		for (UniqueValue doc: docs) {
			if (doc.getTxFinish() == TX_NO) {
				docIds.add(doc.getDocumentKey());
			}
		}
		// returning size must be 1!
		return docIds;
	}
	
	/**
	 * 
	 * @return the collection of unique indexed values
	 */
	public Collection<UniqueValue> getDocumentValues() {
		return docs;
	}

	/**
	 * adds document to the index
	 * 
	 * @param docKey the internal document key
	 * @param txId the internal transaction id
	 */
	@Override
	public boolean addDocument(long docKey, long txId) { // synchronized?
		UniqueValue doc;
		for (int i=docs.size() - 1; i >=0; i--) {
			doc = docs.get(i);
			if (doc.getTxFinish() == TX_NO) {
				doc = new UniqueValue(doc.getDocumentKey(), doc.getTxStart(), txId);
				docs.set(i, doc);
				break;
			}
		}
		doc = new UniqueValue(docKey, txId, TX_NO);
		docs.add(doc);
		return true;
	}

	/**
	 * removes document from the index 
	 * 
	 * @param docKey the internal document key
	 * @param txId the internal transaction id
	 */
	@Override
	public boolean removeDocument(long docKey, long txId) { // synchronized?
		for (int i=docs.size() - 1; i >=0; i--) {
			UniqueValue doc = docs.get(i);
			if (doc.getDocumentKey() == docKey && doc.getTxFinish() == TX_NO) {
				doc = new UniqueValue(doc.getDocumentKey(), doc.getTxStart(), txId);
				docs.set(i, doc);
				return true;
			}
		}
		return false;
	}

	/**
	 * 
	 * @param values the collection of unique values
	 */
	public void setDocumentValues(Collection<UniqueValue> values) {
		docs.clear();
		if (values != null) {
			docs.addAll(values);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String toString() {
		return "UniqueDocument [docs=" + docs + "]";
	}

	/**
	 * return consumed size in bytes
	 */
	@Override
	public int getSize() {
		// have no idea how much memory ArrayList takes!
		return Long.SIZE / Byte.SIZE // List ref
			+ (4 * Long.SIZE / Byte.SIZE) * docs.size();
	}
	
}



