/*
 * Copyright 1999-2010 Luca Garulli (l.garulli--at--orientechnologies.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.orientechnologies.orient.core.tx;

import java.util.concurrent.atomic.AtomicInteger;

import com.orientechnologies.orient.core.config.OGlobalConfiguration;
import com.orientechnologies.orient.core.db.ODatabaseComplex.OPERATION_MODE;
import com.orientechnologies.orient.core.db.record.ODatabaseRecordTx;
import com.orientechnologies.orient.core.db.record.ORecordOperation;
import com.orientechnologies.orient.core.id.ORID;
import com.orientechnologies.orient.core.id.ORecordId;
import com.orientechnologies.orient.core.record.ORecordInternal;
import com.orientechnologies.orient.core.storage.OStorageEmbedded;

public class OTransactionOptimistic extends OTransactionRealAbstract {
	private boolean								usingLog;
	private static AtomicInteger	txSerial	= new AtomicInteger();

	public OTransactionOptimistic(final ODatabaseRecordTx iDatabase) {
		super(iDatabase, txSerial.incrementAndGet());
		usingLog = OGlobalConfiguration.TX_USE_LOG.getValueAsBoolean();
	}

	public void begin() {
		status = TXSTATUS.BEGUN;
	}

	public void commit() {
		checkTransaction();
		status = TXSTATUS.COMMITTING;
		database.executeCommit();
	}

	public void rollback() {
		checkTransaction();

		status = TXSTATUS.ROLLBACKING;

		// ROLLBACK AT STORAGE LEVEL DONE IN THE COMMIT PHASE ONLY
		// database.getStorage().rollback(this);

		// INVALIDATE THE CACHE
		database.getLevel1Cache().invalidate();

		// REMOVE ALL THE ENTRIES AND INVALIDATE THE DOCUMENTS TO AVOID TO BE RE-USED DIRTY AT USER-LEVEL. IN THIS WAY RE-LOADING MUST
		// EXECUTED
		for (ORecordOperation v : recordEntries.values())
			v.getRecord().unload();

		for (ORecordOperation v : allEntries.values())
			v.getRecord().unload();

		indexEntries.clear();
	}

	public ORecordInternal<?> loadRecord(final ORID iRid, final ORecordInternal<?> iRecord, final String iFetchPlan) {
		checkTransaction();

		final ORecordInternal<?> txRecord = getRecord(iRid);

		if (txRecord != null)
			return txRecord;

		// DELEGATE TO THE STORAGE
		return database.executeReadRecord((ORecordId) iRid, iRecord, iFetchPlan, false);
	}

	public void deleteRecord(final ORecordInternal<?> iRecord, final OPERATION_MODE iMode) {
		addRecord(iRecord, ORecordOperation.DELETED, null);
	}

	public void saveRecord(final ORecordInternal<?> iRecord, final String iClusterName, final OPERATION_MODE iMode) {
		addRecord(iRecord, iRecord.getIdentity().isValid() ? ORecordOperation.UPDATED : ORecordOperation.CREATED, iClusterName);
	}

	private void addRecord(final ORecordInternal<?> iRecord, final byte iStatus, final String iClusterName) {
		checkTransaction();

		if ((status == OTransaction.TXSTATUS.COMMITTING) && database.getStorage() instanceof OStorageEmbedded) {
			// I'M COMMITTING OR IT'S AN INDEX: BYPASS LOCAL BUFFER
			switch (iStatus) {
			case ORecordOperation.CREATED:
			case ORecordOperation.UPDATED:
				database
						.executeSaveRecord(iRecord, iClusterName, iRecord.getVersion(), iRecord.getRecordType(), OPERATION_MODE.SYNCHRONOUS);
				break;
			case ORecordOperation.DELETED:
				database.executeDeleteRecord(iRecord, iRecord.getVersion(), OPERATION_MODE.SYNCHRONOUS);
				break;
			}
		} else {
			final ORecordId rid = (ORecordId) iRecord.getIdentity();

			if (!rid.isValid()) {
				// // TODO: NEED IT FOR REAL?
				// // NEW RECORD: CHECK IF IT'S ALREADY IN
				// for (OTransactionRecordEntry entry : recordEntries.values()) {
				// if (entry.getRecord() == iRecord)
				// return;
				// }

				iRecord.onBeforeIdentityChanged(rid);

				// ASSIGN A UNIQUE SERIAL TEMPORARY ID
				if (rid.clusterId == ORID.CLUSTER_ID_INVALID)
					rid.clusterId = iClusterName != null ? database.getClusterIdByName(iClusterName) : database.getDefaultClusterId();
				rid.clusterPosition = newObjectCounter--;

				iRecord.onAfterIdentityChanged(iRecord);
			} else
				// REMOVE FROM THE DB'S CACHE
				database.getLevel1Cache().freeRecord(rid);

			ORecordOperation txEntry = getRecordEntry(rid);

			if (txEntry == null) {
				// NEW ENTRY: JUST REGISTER IT
				txEntry = new ORecordOperation(iRecord, iStatus, iClusterName);

				recordEntries.put(rid, txEntry);
			} else {
				// UPDATE PREVIOUS STATUS
				txEntry.record = iRecord;

				switch (txEntry.type) {
				case ORecordOperation.LOADED:
					switch (iStatus) {
					case ORecordOperation.UPDATED:
						txEntry.type = ORecordOperation.UPDATED;
						break;
					case ORecordOperation.DELETED:
						txEntry.type = ORecordOperation.DELETED;
						break;
					}
					break;
				case ORecordOperation.UPDATED:
					switch (iStatus) {
					case ORecordOperation.DELETED:
						txEntry.type = ORecordOperation.DELETED;
						break;
					}
					break;
				case ORecordOperation.DELETED:
					break;
				case ORecordOperation.CREATED:
					switch (iStatus) {
					case ORecordOperation.DELETED:
						recordEntries.remove(rid);
						break;
					}
					break;
				}
			}
		}
	}

	@Override
	public String toString() {
		return "OTransactionOptimistic [id=" + id + ", status=" + status + ", recEntries=" + recordEntries.size() + ", idxEntries="
				+ indexEntries.size() + "]";
	}

	public boolean isUsingLog() {
		return usingLog;
	}

	public void setUsingLog(final boolean useLog) {
		this.usingLog = useLog;
	}
}

@@ -1213,16 +1213,76 @@
 ernal;%0D%0A
+import com.orientechnologies.orient.core.storage.OStorage;%0D%0A
 import c
@@ -3435,18 +3435,75 @@
 MMITTING
-)
  
+%7C%7C OStorage.CLUSTER_INDEX_NAME.equals(iClusterName))%0D%0A%09%09%09%09
 && datab

index entries always by-pass transactional buffer