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

/**
 * \file wrfc.cpp
 *
 * Qpid asynchronous store plugin library
 *
 * File containing code for class mrg::journal::wrfc (rotating
 * file controller). See comments in file wrfc.h for details.
 *
 * \author Kim van der Riet
 */

#include "qpid/legacystore/jrnl/wrfc.h"

#include <cmath>
#include "qpid/legacystore/jrnl/jerrno.h"
#include "qpid/legacystore/jrnl/jexception.h"

namespace mrg
{
namespace journal
{

wrfc::wrfc(const lpmgr* lpmp):
        rfc(lpmp),
        _fsize_sblks(0),
        _fsize_dblks(0),
        _enq_cap_offs_dblks(0),
        _rid(0),
        _reset_ok(false),
        _owi(false),
        _frot(true)
{}

wrfc::~wrfc()
{}

void
wrfc::initialize(const u_int32_t fsize_sblks, rcvdat* rdp)
{
    if (rdp)
    {
        _fc_index = rdp->_lfid;
        _curr_fc = _lpmp->get_fcntlp(_fc_index);
        _curr_fc->wr_reset(rdp);
        _rid = rdp->_h_rid + 1;
        _reset_ok = true;
        _owi = rdp->_owi;
        _frot = rdp->_frot;
        if (rdp->_lffull)
            rotate();
    }
    else
    {
        rfc::initialize();
        rfc::set_findex(0);
        _rid = 0ULL;
        _reset_ok = false;
    }
    _fsize_sblks = fsize_sblks;
    _fsize_dblks = fsize_sblks * JRNL_SBLK_SIZE;
    _enq_cap_offs_dblks = (u_int32_t)std::ceil(_fsize_dblks * _lpmp->num_jfiles() * (100.0 - JRNL_ENQ_THRESHOLD) / 100);
    // Check the offset is at least one file; if not, make it so
    if (_enq_cap_offs_dblks < _fsize_dblks)
        _enq_cap_offs_dblks = _fsize_dblks;
}

iores wrfc::rotate()
{
    if (!_lpmp->num_jfiles())
        throw jexception(jerrno::JERR__NINIT, "wrfc", "rotate");
    _fc_index++;
    if (_fc_index == _lpmp->num_jfiles())
    {
        _fc_index = 0;
        _owi = !_owi;
        _frot = false;
    }
    _curr_fc = _lpmp->get_fcntlp(_fc_index);
    if (_curr_fc->aio_cnt())
        return RHM_IORES_FILE_AIOWAIT;
    if (!wr_reset()) //Checks if file is still in use (ie not fully dequeued yet)
        return RHM_IORES_FULL;
    return RHM_IORES_SUCCESS;
}

u_int16_t wrfc::earliest_index() const
{
    if (_frot)
        return 0;
    u_int16_t next_index = _fc_index + 1;
    if (next_index >= _lpmp->num_jfiles())
        next_index = 0;
    return next_index;
}

bool
wrfc::enq_threshold(const u_int32_t enq_dsize_dblks) const
{
    u_int32_t subm_dblks = subm_cnt_dblks(); // includes file hdr if > 0
    // This compensates for new files which don't have their file headers written yet,
    // as file header space cannot be included in this calculation.
    if (subm_dblks != 0)
        subm_dblks -= 4;
    u_int32_t fwd_dblks = subm_dblks + enq_dsize_dblks + _enq_cap_offs_dblks;
    u_int16_t findex = _fc_index;
    fcntl* fcp = _curr_fc;
    bool in_use = false;
    while (fwd_dblks && !(findex != _fc_index && fcp->enqcnt()))
    {
        fwd_dblks -= fwd_dblks > _fsize_dblks ? _fsize_dblks : fwd_dblks;
        if (fwd_dblks)
        {
            if (++findex == _lpmp->num_jfiles())
                findex = 0;
            fcp = _lpmp->get_fcntlp(findex);
        }
        in_use |= fcp->enqcnt() > 0;
    }
    // Return true if threshold exceeded
    return findex != _fc_index && in_use;
}

bool wrfc::wr_reset()
{
    _reset_ok = _curr_fc->reset(); // returns false if full (ie file still contains enqueued recs)
    return _reset_ok;
}

// TODO: update this to reflect all status data
std::string
wrfc::status_str() const
{
    std::ostringstream oss;
    oss << "wrfc: " << rfc::status_str();
    if (is_active())
        oss << " fcntl[" << _fc_index << "]: " << _curr_fc->status_str();
    return oss.str();
}

} // namespace journal
} // namespace mrg
