/*
 * 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 "PullAPIWrapper.h"
#include "CommunicationMode.h"
#include "MQClientFactory.h"
#include "PullResultExt.h"
#include "PullSysFlag.h"
namespace rocketmq {
//<!************************************************************************
PullAPIWrapper::PullAPIWrapper(MQClientFactory* mQClientFactory, const string& consumerGroup) {
  m_MQClientFactory = mQClientFactory;
  m_consumerGroup = consumerGroup;
}

PullAPIWrapper::~PullAPIWrapper() {
  m_MQClientFactory = NULL;
  m_pullFromWhichNodeTable.clear();
}

void PullAPIWrapper::updatePullFromWhichNode(const MQMessageQueue& mq, int brokerId) {
  boost::lock_guard<boost::mutex> lock(m_lock);
  m_pullFromWhichNodeTable[mq] = brokerId;
}

int PullAPIWrapper::recalculatePullFromWhichNode(const MQMessageQueue& mq) {
  boost::lock_guard<boost::mutex> lock(m_lock);
  if (m_pullFromWhichNodeTable.find(mq) != m_pullFromWhichNodeTable.end()) {
    return m_pullFromWhichNodeTable[mq];
  }
  return MASTER_ID;
}

PullResult PullAPIWrapper::processPullResult(const MQMessageQueue& mq,
                                             PullResult* pullResult,
                                             SubscriptionData* subscriptionData) {
  PullResultExt* pResultExt = static_cast<PullResultExt*>(pullResult);
  if (pResultExt == NULL) {
    string errMsg("The pullResult NULL of");
    errMsg.append(mq.toString());
    THROW_MQEXCEPTION(MQClientException, errMsg, -1);
  }

  //<!update;
  updatePullFromWhichNode(mq, pResultExt->suggestWhichBrokerId);

  vector<MQMessageExt> msgFilterList;
  if (pResultExt->pullStatus == FOUND) {
    //<!decode all msg list;
    vector<MQMessageExt> msgAllList;
    MQDecoder::decodes(&pResultExt->msgMemBlock, msgAllList);

    //<!filter msg list again;
    if (subscriptionData != NULL && !subscriptionData->getTagsSet().empty()) {
      msgFilterList.reserve(msgAllList.size());
      vector<MQMessageExt>::iterator it = msgAllList.begin();
      for (; it != msgAllList.end(); ++it) {
        string msgTag = (*it).getTags();
        if (subscriptionData->containTag(msgTag)) {
          msgFilterList.push_back(*it);
        }
      }
    } else {
      msgFilterList.swap(msgAllList);
    }
  }

  return PullResult(pResultExt->pullStatus, pResultExt->nextBeginOffset, pResultExt->minOffset, pResultExt->maxOffset,
                    msgFilterList);
}

PullResult* PullAPIWrapper::pullKernelImpl(const MQMessageQueue& mq,        // 1
                                           string subExpression,            // 2
                                           int64 subVersion,                // 3
                                           int64 offset,                    // 4
                                           int maxNums,                     // 5
                                           int sysFlag,                     // 6
                                           int64 commitOffset,              // 7
                                           int brokerSuspendMaxTimeMillis,  // 8
                                           int timeoutMillis,               // 9
                                           int communicationMode,           // 10
                                           PullCallback* pullCallback,
                                           const SessionCredentials& session_credentials,
                                           void* pArg /*= NULL*/) {
  unique_ptr<FindBrokerResult> pFindBrokerResult(
      m_MQClientFactory->findBrokerAddressInSubscribe(mq.getBrokerName(), recalculatePullFromWhichNode(mq), false));
  //<!goto nameserver;
  if (pFindBrokerResult == NULL) {
    m_MQClientFactory->updateTopicRouteInfoFromNameServer(mq.getTopic(), session_credentials);
    pFindBrokerResult.reset(
        m_MQClientFactory->findBrokerAddressInSubscribe(mq.getBrokerName(), recalculatePullFromWhichNode(mq), false));
  }

  if (pFindBrokerResult != NULL) {
    int sysFlagInner = sysFlag;

    if (pFindBrokerResult->slave) {
      sysFlagInner = PullSysFlag::clearCommitOffsetFlag(sysFlagInner);
    }

    PullMessageRequestHeader* pRequestHeader = new PullMessageRequestHeader();
    pRequestHeader->consumerGroup = m_consumerGroup;
    pRequestHeader->topic = mq.getTopic();
    pRequestHeader->queueId = mq.getQueueId();
    pRequestHeader->queueOffset = offset;
    pRequestHeader->maxMsgNums = maxNums;
    pRequestHeader->sysFlag = sysFlagInner;
    pRequestHeader->commitOffset = commitOffset;
    pRequestHeader->suspendTimeoutMillis = brokerSuspendMaxTimeMillis;
    pRequestHeader->subscription = subExpression;
    pRequestHeader->subVersion = subVersion;

    return m_MQClientFactory->getMQClientAPIImpl()->pullMessage(pFindBrokerResult->brokerAddr, pRequestHeader,
                                                                timeoutMillis, communicationMode, pullCallback, pArg,
                                                                session_credentials);
  }
  THROW_MQEXCEPTION(MQClientException, "The broker not exist", -1);
}

}  // namespace rocketmq
