/*
 * 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.
 */

#include "TXEntryState.hpp"

#include <geode/ExceptionTypes.hpp>

#include "util/exception.hpp"

namespace apache {
namespace geode {
namespace client {

TXEntryState::TXEntryState()
    : /* adongre
       * CID 28946: Uninitialized scalar field (UNINIT_CTOR)
       */
      // UNUSED m_modSerialNum(0),
      m_op(OP_NULL)
// UNUSED , m_bulkOp(false)
{}

TXEntryState::~TXEntryState() {}

int8_t TXEntryState::adviseOp(int8_t requestedOpCode) {
  int8_t advisedOpCode = OP_NULL;
  // Determine new operation based on
  // the requested operation 'requestedOpCode' and
  // the previous operation 'm_op'
  switch (requestedOpCode) {
    case OP_L_DESTROY:
      switch (m_op) {
        case OP_NULL:
          advisedOpCode = requestedOpCode;
          break;
        case OP_L_DESTROY:
        case OP_CREATE_LD:
        case OP_LLOAD_CREATE_LD:
        case OP_NLOAD_CREATE_LD:
        case OP_PUT_LD:
        case OP_LLOAD_PUT_LD:
        case OP_NLOAD_PUT_LD:
        case OP_D_INVALIDATE_LD:
        case OP_D_DESTROY:
          GfErrTypeThrowException(
              "Unexpected current op  {0}  for requested op  {1}",
              GF_CACHE_ILLEGAL_STATE_EXCEPTION);
          // not expected to be reached
          break;
        case OP_L_INVALIDATE:
          advisedOpCode = requestedOpCode;
          break;
        case OP_PUT_LI:
          advisedOpCode = OP_PUT_LD;
          break;
        case OP_LLOAD_PUT_LI:
          advisedOpCode = OP_LLOAD_PUT_LD;
          break;
        case OP_NLOAD_PUT_LI:
          advisedOpCode = OP_NLOAD_PUT_LD;
          break;
        case OP_D_INVALIDATE:
          advisedOpCode = OP_D_INVALIDATE_LD;
          break;
        case OP_CREATE_LI:
          advisedOpCode = OP_CREATE_LD;
          break;
        case OP_LLOAD_CREATE_LI:
          advisedOpCode = OP_LLOAD_CREATE_LD;
          break;
        case OP_NLOAD_CREATE_LI:
          advisedOpCode = OP_NLOAD_CREATE_LD;
          break;
        case OP_CREATE:
          advisedOpCode = OP_CREATE_LD;
          break;
        case OP_SEARCH_CREATE:
          advisedOpCode = requestedOpCode;
          break;
        case OP_LLOAD_CREATE:
          advisedOpCode = OP_LLOAD_CREATE_LD;
          break;
        case OP_NLOAD_CREATE:
          advisedOpCode = OP_NLOAD_CREATE_LD;
          break;
        case OP_LOCAL_CREATE:
          advisedOpCode = requestedOpCode;
          break;
        case OP_PUT:
          advisedOpCode = OP_PUT_LD;
          break;
        case OP_SEARCH_PUT:
          advisedOpCode = requestedOpCode;
          break;
        case OP_LLOAD_PUT:
          advisedOpCode = OP_LLOAD_PUT_LD;
          break;
        case OP_NLOAD_PUT:
          advisedOpCode = OP_NLOAD_PUT_LD;
          break;
        default:
          GfErrTypeThrowException("Unhandled  {0}",
                                  GF_CACHE_ILLEGAL_STATE_EXCEPTION);
      }
      break;
    case OP_D_DESTROY:
      // Assert.assertTrue(!isOpDestroy(),
      //                  "Transactional destroy assertion op=" + m_op);
      advisedOpCode = requestedOpCode;
      break;
    case OP_L_INVALIDATE:
      switch (m_op) {
        case OP_NULL:
          advisedOpCode = requestedOpCode;
          break;
        case OP_L_DESTROY:
        case OP_CREATE_LD:
        case OP_LLOAD_CREATE_LD:
        case OP_NLOAD_CREATE_LD:
        case OP_PUT_LD:
        case OP_LLOAD_PUT_LD:
        case OP_NLOAD_PUT_LD:
        case OP_D_DESTROY:
        case OP_D_INVALIDATE_LD:
          GfErrTypeThrowException(
              "Unexpected current op  {0}  for requested op  {1}",
              GF_CACHE_ILLEGAL_STATE_EXCEPTION);
          // not expected to be reached
          break;
        case OP_L_INVALIDATE:
          advisedOpCode = requestedOpCode;
          break;
        case OP_LLOAD_PUT_LI:
        case OP_NLOAD_PUT_LI:
        case OP_LLOAD_CREATE_LI:
        case OP_NLOAD_CREATE_LI:
          advisedOpCode = m_op;
          break;
        case OP_PUT_LI:
          advisedOpCode = OP_PUT_LI;
          break;
        case OP_CREATE_LI:
          advisedOpCode = OP_CREATE_LI;
          break;
        case OP_D_INVALIDATE:
          advisedOpCode = OP_D_INVALIDATE;
          break;
        case OP_CREATE:
          advisedOpCode = OP_CREATE_LI;
          break;
        case OP_SEARCH_CREATE:
          advisedOpCode = OP_LOCAL_CREATE;
          // pendingValue will be set to LOCAL_INVALID
          break;
        case OP_LLOAD_CREATE:
          advisedOpCode = OP_LLOAD_CREATE_LI;
          break;
        case OP_NLOAD_CREATE:
          advisedOpCode = OP_NLOAD_CREATE_LI;
          break;
        case OP_LOCAL_CREATE:
          advisedOpCode = OP_LOCAL_CREATE;
          break;
        case OP_PUT:
          advisedOpCode = OP_PUT_LI;
          break;
        case OP_SEARCH_PUT:
          advisedOpCode = requestedOpCode;
          break;
        case OP_LLOAD_PUT:
          advisedOpCode = OP_LLOAD_PUT_LI;
          break;
        case OP_NLOAD_PUT:
          advisedOpCode = OP_NLOAD_PUT_LI;
          break;
        default:
          GfErrTypeThrowException("Unhandled  {0}",
                                  GF_CACHE_ILLEGAL_STATE_EXCEPTION);
      }
      break;
    case OP_D_INVALIDATE:
      switch (m_op) {
        case OP_NULL:
          advisedOpCode = requestedOpCode;
          break;
        case OP_L_DESTROY:
        case OP_CREATE_LD:
        case OP_LLOAD_CREATE_LD:
        case OP_NLOAD_CREATE_LD:
        case OP_PUT_LD:
        case OP_LLOAD_PUT_LD:
        case OP_NLOAD_PUT_LD:
        case OP_D_INVALIDATE_LD:
        case OP_D_DESTROY:
          GfErrTypeThrowException(
              "Unexpected current op  {0}  for requested op  {1}",
              GF_CACHE_ILLEGAL_STATE_EXCEPTION);
          // not expected to be reached
          break;
        case OP_D_INVALIDATE:
        case OP_L_INVALIDATE:
          advisedOpCode = OP_D_INVALIDATE;
          break;

        case OP_PUT_LI:
        case OP_LLOAD_PUT_LI:
        case OP_NLOAD_PUT_LI:
        case OP_CREATE_LI:
        case OP_LLOAD_CREATE_LI:
        case OP_NLOAD_CREATE_LI:
          /*
           * No change, keep it how it was.
           */
          advisedOpCode = m_op;
          break;
        case OP_CREATE:
          advisedOpCode = OP_CREATE;
          // pendingValue will be set to INVALID turning it into create invalid
          break;
        case OP_SEARCH_CREATE:
          advisedOpCode = OP_LOCAL_CREATE;
          // pendingValue will be set to INVALID to indicate dinvalidate
          break;
        case OP_LLOAD_CREATE:
          advisedOpCode = OP_CREATE;
          // pendingValue will be set to INVALID turning it into create invalid
          break;
        case OP_NLOAD_CREATE:
          advisedOpCode = OP_CREATE;
          // pendingValue will be set to INVALID turning it into create invalid
          break;
        case OP_LOCAL_CREATE:
          advisedOpCode = OP_LOCAL_CREATE;
          // pendingValue will be set to INVALID to indicate dinvalidate
          break;
        case OP_PUT:
        case OP_SEARCH_PUT:
        case OP_LLOAD_PUT:
        case OP_NLOAD_PUT:
          advisedOpCode = requestedOpCode;
          break;
        default:
          GfErrTypeThrowException("Unhandled  {0}",
                                  GF_CACHE_ILLEGAL_STATE_EXCEPTION);
      }
      break;
    case OP_CREATE:
    case OP_SEARCH_CREATE:
    case OP_LLOAD_CREATE:
    case OP_NLOAD_CREATE:
      advisedOpCode = requestedOpCode;
      break;
    case OP_PUT:
      switch (m_op) {
        case OP_CREATE:
        case OP_SEARCH_CREATE:
        case OP_LLOAD_CREATE:
        case OP_NLOAD_CREATE:
        case OP_LOCAL_CREATE:
        case OP_CREATE_LI:
        case OP_LLOAD_CREATE_LI:
        case OP_NLOAD_CREATE_LI:
        case OP_CREATE_LD:
        case OP_LLOAD_CREATE_LD:
        case OP_NLOAD_CREATE_LD:
        case OP_PUT_LD:
        case OP_LLOAD_PUT_LD:
        case OP_NLOAD_PUT_LD:
        case OP_D_INVALIDATE_LD:
        case OP_L_DESTROY:
        case OP_D_DESTROY:
          advisedOpCode = OP_CREATE;
          break;
        default:
          advisedOpCode = requestedOpCode;
          break;
      }
      break;
    case OP_SEARCH_PUT:
      switch (m_op) {
        case OP_NULL:
          advisedOpCode = requestedOpCode;
          break;
        case OP_L_INVALIDATE:
          advisedOpCode = requestedOpCode;
          break;
        // The incoming search put value should match
        // the pendingValue from the previous tx operation.
        // So it is ok to simply drop the _LI from the op
        case OP_PUT_LI:
          advisedOpCode = OP_PUT;
          break;
        case OP_LLOAD_PUT_LI:
          advisedOpCode = OP_LLOAD_PUT;
          break;
        case OP_NLOAD_PUT_LI:
          advisedOpCode = OP_NLOAD_PUT;
          break;
        case OP_CREATE_LI:
          advisedOpCode = OP_CREATE;
          break;
        case OP_LLOAD_CREATE_LI:
          advisedOpCode = OP_LLOAD_CREATE;
          break;
        case OP_NLOAD_CREATE_LI:
          advisedOpCode = OP_NLOAD_CREATE;
          break;
        default:
          // Note that OP_LOCAL_CREATE and OP_CREATE with invalid values
          // are not possible because they would cause the netsearch to
          // fail and we would do a load or a total miss.
          // Note that OP_D_INVALIDATE followed by OP_SEARCH_PUT is not
          // possible since the netsearch will alwsys "miss" in this case.
          GfErrTypeThrowException(
              "Unexpected current op  {0}  for requested op  {1}",
              GF_CACHE_ILLEGAL_STATE_EXCEPTION);
      }
      break;
    case OP_LLOAD_PUT:
    case OP_NLOAD_PUT:
      switch (m_op) {
        case OP_NULL:
        case OP_L_INVALIDATE:
        case OP_PUT_LI:
        case OP_LLOAD_PUT_LI:
        case OP_NLOAD_PUT_LI:
        case OP_D_INVALIDATE:
          advisedOpCode = requestedOpCode;
          break;
        case OP_CREATE:
        case OP_LOCAL_CREATE:
        case OP_CREATE_LI:
        case OP_LLOAD_CREATE_LI:
        case OP_NLOAD_CREATE_LI:
          if (requestedOpCode == OP_LLOAD_PUT) {
            advisedOpCode = OP_LLOAD_CREATE;
          } else {
            advisedOpCode = OP_NLOAD_CREATE;
          }
          break;
        default:
          // note that other invalid states are covered by this default
          // case because they should have caused a OP_SEARCH_PUT
          // to be requested.
          GfErrTypeThrowException(
              "Unexpected current op  {0}  for requested op  {1}",
              GF_CACHE_ILLEGAL_STATE_EXCEPTION);
      }
      break;
    default:
      GfErrTypeThrowException("OpCode  {0}  should never be requested",
                              GF_CACHE_ILLEGAL_STATE_EXCEPTION);
  }
  return advisedOpCode;
}
}  // namespace client
}  // namespace geode
}  // namespace apache
