/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.activemq.artemis.core.journal;

import java.util.List;
import java.util.Map;

import org.apache.activemq.artemis.core.io.SequentialFileFactory;
import org.apache.activemq.artemis.core.journal.impl.JournalFile;
import org.apache.activemq.artemis.core.server.ActiveMQComponent;

/**
 * Most methods on the journal provide a blocking version where you select the sync mode and a non
 * blocking mode where you pass a completion callback as a parameter.
 * <p>
 * Notice also that even on the callback methods it's possible to pass the sync mode. That will only
 * make sense on the NIO operations.
 */
public interface Journal extends ActiveMQComponent {

   enum JournalState {
      STOPPED,
      /**
       * The journal has some fields initialized and services running. But it is not fully
       * operational. See {@link JournalState#LOADED}.
       */
      STARTED,
      /**
       * When a replicating server is still not synchronized with its live. So if the live stops,
       * the backup may not fail-over and will stop as well.
       */
      SYNCING,
      /**
       * Journal is being used by a replicating server which is up-to-date with its live. That means
       * that if the live stops, the backup can fail-over.
       */
      SYNCING_UP_TO_DATE,
      /**
       * The journal is fully operational. This is the state the journal should be when its server
       * is live.
       */
      LOADED;
   }

   // Non transactional operations

   void appendAddRecord(long id, byte recordType, byte[] record, boolean sync) throws Exception;

   void appendAddRecord(long id, byte recordType, EncodingSupport record, boolean sync) throws Exception;

   void appendAddRecord(long id,
                        byte recordType,
                        EncodingSupport record,
                        boolean sync,
                        IOCompletion completionCallback) throws Exception;

   void appendUpdateRecord(long id, byte recordType, byte[] record, boolean sync) throws Exception;

   void appendUpdateRecord(long id, byte recordType, EncodingSupport record, boolean sync) throws Exception;

   void appendUpdateRecord(long id,
                           byte recordType,
                           EncodingSupport record,
                           boolean sync,
                           IOCompletion completionCallback) throws Exception;

   void appendDeleteRecord(long id, boolean sync) throws Exception;

   void appendDeleteRecord(long id, boolean sync, IOCompletion completionCallback) throws Exception;

   // Transactional operations

   void appendAddRecordTransactional(long txID, long id, byte recordType, byte[] record) throws Exception;

   void appendAddRecordTransactional(long txID, long id, byte recordType, EncodingSupport record) throws Exception;

   void appendUpdateRecordTransactional(long txID, long id, byte recordType, byte[] record) throws Exception;

   void appendUpdateRecordTransactional(long txID, long id, byte recordType, EncodingSupport record) throws Exception;

   void appendDeleteRecordTransactional(long txID, long id, byte[] record) throws Exception;

   void appendDeleteRecordTransactional(long txID, long id, EncodingSupport record) throws Exception;

   void appendDeleteRecordTransactional(long txID, long id) throws Exception;

   void appendCommitRecord(long txID, boolean sync) throws Exception;

   void appendCommitRecord(long txID, boolean sync, IOCompletion callback) throws Exception;

   /**
    * @param txID
    * @param sync
    * @param callback
    * @param lineUpContext if appendCommitRecord should call a storeLineUp. This is because the
    *                      caller may have already taken into account
    * @throws Exception
    */
   void appendCommitRecord(long txID, boolean sync, IOCompletion callback, boolean lineUpContext) throws Exception;

   /**
    * <p>If the system crashed after a prepare was called, it should store information that is required to bring the transaction
    * back to a state it could be committed. </p>
    *
    * <p> transactionData allows you to store any other supporting user-data related to the transaction</p>
    *
    * @param txID
    * @param transactionData - extra user data for the prepare
    * @throws Exception
    */
   void appendPrepareRecord(long txID, EncodingSupport transactionData, boolean sync) throws Exception;

   void appendPrepareRecord(long txID,
                            EncodingSupport transactionData,
                            boolean sync,
                            IOCompletion callback) throws Exception;

   void appendPrepareRecord(long txID, byte[] transactionData, boolean sync) throws Exception;

   void appendRollbackRecord(long txID, boolean sync) throws Exception;

   void appendRollbackRecord(long txID, boolean sync, IOCompletion callback) throws Exception;

   // Load

   JournalLoadInformation load(LoaderCallback reloadManager) throws Exception;

   /**
    * Load internal data structures and not expose any data. This is only useful if you're using the
    * journal but not interested on the current data. Useful in situations where the journal is
    * being replicated, copied... etc.
    */
   JournalLoadInformation loadInternalOnly() throws Exception;

   /**
    * Load internal data structures, and remain waiting for synchronization to complete.
    *
    * @param state the current state of the journal, this parameter ensures consistency.
    */
   JournalLoadInformation loadSyncOnly(JournalState state) throws Exception;

   void lineUpContext(IOCompletion callback);

   JournalLoadInformation load(List<RecordInfo> committedRecords,
                               List<PreparedTransactionInfo> preparedTransactions,
                               TransactionFailureCallback transactionFailure) throws Exception;

   int getAlignment() throws Exception;

   int getNumberOfRecords();

   int getUserVersion();

   void perfBlast(int pages);

   void runDirectJournalBlast() throws Exception;

   /**
    * Reserves journal file IDs, creates the necessary files for synchronization, and places
    * references to these (reserved for sync) files in the map.
    * <p>
    * During the synchronization between a live server and backup, we reserve in the backup the
    * journal file IDs used in the live server. This call also makes sure the files are created
    * empty without any kind of headers added.
    *
    * @param fileIds IDs to reserve for synchronization
    * @return map to be filled with id and journal file pairs for <b>synchronization</b>.
    * @throws Exception
    */
   Map<Long, JournalFile> createFilesForBackupSync(long[] fileIds) throws Exception;

   /**
    * Write lock the Journal and write lock the compacting process. Necessary only during
    * replication for backup synchronization.
    */
   void synchronizationLock();

   /**
    * Unlock the Journal and the compacting process.
    *
    * @see Journal#synchronizationLock()
    */
   void synchronizationUnlock();

   /**
    * Force the usage of a new {@link JournalFile}.
    *
    * @throws Exception
    */
   void forceMoveNextFile() throws Exception;

   /**
    * Returns the {@link JournalFile}s in use.
    *
    * @return array with all {@link JournalFile}s in use
    */
   JournalFile[] getDataFiles();

   SequentialFileFactory getFileFactory();

   int getFileSize();

   /**
    * This method will start compact using the compactorExecutor and block up to timeout seconds
    *
    * @param timeout the timeout in seconds or block forever if {@code <= 0}
    * @throws Exception
    */
   void scheduleCompactAndBlock(int timeout) throws Exception;

   /**
    * Stops any operation that may delete or modify old (stale) data.
    * <p>
    * Meant to be used during synchronization of data between a live server and its replicating
    * (remote) backup. Old files must not be compacted or deleted during synchronization.
    */
   void replicationSyncPreserveOldFiles();

   /**
    * Restarts file reclaim and compacting on the journal.
    * <p>
    * Meant to be used to revert the effect of {@link #replicationSyncPreserveOldFiles()}. it should
    * only be called once the synchronization of the backup and live servers is completed.
    */
   void replicationSyncFinished();
}
