/* Copyright (C) 2014 InfiniDB, Inc.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; version 2 of
   the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   MA 02110-1301, USA. */

/****************************************************************************/
/*                                                                          */
/*   we_indexlist_add_hdr.cpp                                               */
/*   (c) 2001 Author                                                        */
/*                                                                          */
/*   Description                                                            */
/*                                                                          */
/************************************************************************** */
#include <stdio.h>
#include <string.h>
#ifndef _MSC_VER
#include <inttypes.h>
#endif
#include "we_indexlist.h"
using namespace std;

namespace WriteEngine
{
/****************************************************************
 * DESCRIPTION:
 * RETURN:
 *    success    - successfully created the index list header
 *    failure    - it did not create the index list header
 ***********************************************************/
const int IndexList::updateHdrSub(const RID& newRid, const uint64_t& key)
{
    int rc = ERR_IDX_LIST_INVALID_UP_HDR;
    CommBlock cb;
    cb.file.oid = m_oid;
    cb.file.pFile = m_pFile;

    if (m_curIdxRidListHdr.firstIdxRidListEntry.type
            == (int)LIST_NOT_USED_TYPE)
    {
        m_curIdxRidListHdr.firstIdxRidListEntry.type = LIST_RID_TYPE;
        m_curIdxRidListHdr.firstIdxRidListEntry.rid = newRid;
        m_curIdxRidListHdr.idxRidListSize.size++;

        if (m_hdrBlock.lbid == m_hdrLbid)
            rc = writeSubBlockEntry( cb, &m_hdrBlock,  m_hdrLbid,
                                     m_hdrSbid, m_hdrEntry,
                                     LIST_HDR_SIZE,
                                     &m_curIdxRidListHdr );
        else
            return ERR_IDX_LIST_WRONG_LBID_WRITE;

        m_hdrBlock.state = BLK_READ;
        return rc;
    };//Done

    //Check Header last entry's type and go to different next step
    m_nextType = m_curIdxRidListHdr.nextIdxRidListPtr.type;

    switch (m_nextType)
    {
        case LIST_NOT_USED_TYPE://Header is not full
            //insert row id into header last enty
            m_curIdxRidListHdr.nextIdxRidListPtr.type = LIST_RID_TYPE;
            m_curIdxRidListHdr.nextIdxRidListPtr.llp = newRid;
            m_curIdxRidListHdr.idxRidListSize.size++;

            if (m_hdrBlock.lbid == m_hdrLbid)
                rc = writeSubBlockEntry( cb, &m_hdrBlock, m_hdrLbid,
                                         m_hdrSbid, m_hdrEntry,
                                         LIST_HDR_SIZE, &m_curIdxRidListHdr);
            else
                return ERR_IDX_LIST_WRONG_LBID_WRITE;

            m_hdrBlock.state = BLK_READ;
            m_lastLbid = INVALID_LBID;
            break;

        case LIST_RID_TYPE://Header is full, need a new sub-block
            RID oldRid ;
            IdxEmptyListEntry newIdxListEntry;
            oldRid = m_curIdxRidListHdr.nextIdxRidListPtr.llp;
            //need a sub block
            m_segType = LIST_SUBBLOCK_TYPE;

            rc = moveRidsToNewSub(m_pFile, oldRid, newRid, &newIdxListEntry);

            if (rc != NO_ERROR)
                return rc;

            //update header count twice
            m_curIdxRidListHdr.nextIdxRidListPtr.type = m_segType;
            m_curIdxRidListHdr.nextIdxRidListPtr.llp =
                ((IdxRidListHdrPtr*)&newIdxListEntry)->llp;
            m_curIdxRidListHdr.nextIdxRidListPtr.spare = 0;
            m_curIdxRidListHdr.idxRidListSize.size++;
            setSubBlockEntry(m_hdrBlock.data, m_hdrSbid,
                             m_hdrEntry, LIST_HDR_SIZE,
                             &m_curIdxRidListHdr );
            rc = writeDBFile( cb, m_hdrBlock.data, m_hdrLbid );

            if (rc != NO_ERROR)
                return rc;

            m_hdrBlock.state = BLK_READ;
            m_lastLbid = INVALID_LBID;

            if (m_curBlock.state == BLK_WRITE)
            {
                rc = writeDBFile( cb, m_curBlock.data, m_lbid );
                m_curBlock.state = BLK_READ;
            }

            break;

        case LIST_SUBBLOCK_TYPE: //first one is a sub block

            m_lbid = ((IdxEmptyListEntry*)
                      & (m_curIdxRidListHdr.nextIdxRidListPtr))->fbo;
            m_sbid = ((IdxEmptyListEntry*)
                      & (m_curIdxRidListHdr.nextIdxRidListPtr))->sbid;
            m_entry = ((IdxEmptyListEntry*)
                       & (m_curIdxRidListHdr.nextIdxRidListPtr))->entry;
            m_curType = m_nextType;
            m_segType = LIST_BLOCK_TYPE;
            rc = readCurBlk();
            rc = getNextInfoFromBlk(m_lastIdxRidListPtr);
            rc = addRidInSub(newRid, m_lastIdxRidListPtr);
            break;

        default:
            rc = ERR_IDX_LIST_INVALID_UP_HDR;
            break;
    }	//end of switch

    return rc;
}
/************************************************
 * No Change
 * RETURN:
 *    success    - successfully created the index list header
 *    failure    - it did not create the index list header
 ***********************************************************/
const int IndexList::addRidInSub(const RID& newRid,
                                 IdxRidListPtr& lastIdxRidListPtr)
{
    int rc = NO_ERROR;
    int maxCount;
    int count;
    CommBlock cb;

    cb.file.oid   = m_oid;
    cb.file.pFile = m_pFile;

    m_curType  = LIST_SUBBLOCK_TYPE;
    m_segType  = LIST_BLOCK_TYPE;
    m_nextType = lastIdxRidListPtr.type;

    maxCount      = MAX_SUB_RID_CNT;
    count      = lastIdxRidListPtr.count;
    //For n-array
    m_curLevel = 0;
    m_curLevelPos = 0;
    m_curBlkPos = 0;
    m_parentLbid = INVALID_LBID;

    if ((count == maxCount) && (m_nextType == LIST_SIZE_TYPE))
    {
        //Full, need a new segment

        IdxEmptyListEntry newIdxListEntryPtr;
        memset(&newIdxListEntryPtr, 0, sizeof(newIdxListEntryPtr));
        rc = getSegment( m_pFile,  ENTRY_BLK, &newIdxListEntryPtr);

        if (rc != NO_ERROR)
            return rc;

        lastIdxRidListPtr.type = LIST_BLOCK_TYPE;
        lastIdxRidListPtr.llp = ((IdxRidListPtr*)&newIdxListEntryPtr)->llp;
        lastIdxRidListPtr.spare = 0x0;
        rc = setNextInfoFromBlk( lastIdxRidListPtr);
        //New Block initialization
        m_lbid = newIdxListEntryPtr.fbo;
        m_sbid = 0;
        m_entry = 0;
        m_curType =  m_segType;
        rc = readCurBlk();
        //make sure no garbage in the new block last entry
        IdxRidListPtr idxRidListPtr;
        memset(&idxRidListPtr, 0, sizeof(idxRidListPtr));
        rc = setNextInfoFromBlk( idxRidListPtr);
        count = 0;

        if (m_useNarray)
            rc = initCurBlock();

        if (m_lastLbid != m_lbid)
        {
            rc = setLastLbid(m_lbid);
        }
    }//end if FULL
    else if (count < maxCount) // if less than maxCount either type =7 or 0
    {
        if (m_lastLbid != INVALID_LBID)
        {
            uint64_t zlbid = INVALID_LBID;
            rc = setLastLbid(zlbid);
        }
    } //endif count
    else if ((count == maxCount) && (m_nextType == LIST_BLOCK_TYPE))
    {
        m_lbid = ((IdxEmptyListEntry*)&lastIdxRidListPtr)->fbo;
        m_sbid = 0;
        m_entry = 0;
        m_curType = LIST_BLOCK_TYPE;
        rc = addRidInBlk(newRid);
        return rc;
    }

    rc = insertRid(newRid, count);// count is the position
    rc = updateCurCount();
    rc = updateHdrCount();

    return rc;
}
/***********************************************************
 * DESCRIPTION:
 * RETURN:
 *    success NO_ERROR
 *    fail
 ***********************************************************/
const int IndexList::moveRidsToNewSub(FILE* pFile, const RID& oldRid,
                                      const RID& newRid,
                                      IdxEmptyListEntry* newIdxListEntryPtr)
{
    int rc;
    //Write everything out in getSegment
    m_segType = LIST_SUBBLOCK_TYPE;

    rc = getSegment( m_pFile, ENTRY_32, newIdxListEntryPtr );

    if (rc != NO_ERROR)
        return rc;

    //This is the new segment
    m_curType = LIST_SUBBLOCK_TYPE;
    m_lbid   = newIdxListEntryPtr->fbo;
    m_sbid   = newIdxListEntryPtr->sbid;
    m_entry  = newIdxListEntryPtr->entry;

    if  (m_lbid != m_hdrLbid)
        rc = readCurBlk();

    rc = insertRid(oldRid, m_entry);
    m_entry++;
    rc = insertRid(newRid, m_entry);
    rc = updateCurCount(2);

    return rc;
}
/****************************************************************************
 * RETURN:
 *    success    - successfully created the index list header
 *    failure    - it did not create the index list header
 ***************************************************************************/
const int IndexList::updateHdrCount()
{
    int rc = NO_ERROR;

    if (m_hdrBlock.state == BLK_INIT)
        return ERR_IDX_LIST_UPDATE_HDR_COUNT;

    m_curIdxRidListHdr.idxRidListSize.size++;
    setSubBlockEntry( m_hdrBlock.data, m_hdrSbid,
                      m_hdrEntry, LIST_HDR_SIZE,
                      &m_curIdxRidListHdr );
    m_hdrBlock.state = BLK_WRITE;

    return rc;
}
}
