/* 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_narray.cpp                                                       */
/*                                                                            */
/*                                                                            */
/*   Description                                                              */
/*                                                                            */
/* ========================================================================== */
#include <stdio.h>
//#include <pthread.h>
#include <string.h>
#include <inttypes.h>
#include <math.h>
#include "we_indextree.h"
#include "we_indexlist.h"
using namespace std;

namespace WriteEngine
{
/****************************************************************
 * DESCRIPTION:
 * Private Function for print the block trees
 *
 ***************************************************************/
const int IndexList::printBlocks(uint64_t& lbid)
{
  int rc;
  IdxRidListArrayPtr idxRidListArrayPtr;
  int curLevel = 0, curCount = 0;

  memset(&idxRidListArrayPtr, 0, sizeof(idxRidListArrayPtr));
  m_lbid = lbid;
  rc = readCurBlk();
  getSubBlockEntry(m_curBlock.data, 0, BEGIN_LIST_BLK_LLP_POS, LIST_BLK_LLP_ENTRY_WIDTH, &idxRidListArrayPtr);
  curLevel = idxRidListArrayPtr.nextIdxListPtr.curLevel;
  curCount = idxRidListArrayPtr.nextIdxListPtr.count;

  for (int i = 0; i < curLevel; i++)
  {
    cout << "        ";
  }

  cout << "   Lbid->" << lbid << " curLevel->" << curLevel << " curCount->" << curCount << endl;

  for (int i = 0; i < TOTAL_NUM_ARRAY_PTR; i++)
  {
    uint64_t lbid;
    lbid = idxRidListArrayPtr.childIdxRidListPtr[i].childLbid;

    if (lbid != (uint64_t)INVALID_LBID)
      printBlocks(lbid);
  }

  return rc;
}

/****************************************************************
 * DESCRIPTION:
 * Private Function for getting the last Fbo on header
 *
 ***************************************************************/
const int IndexList::setCurBlkNextPtr(uint64_t& nextLbid, int count)
{
  int rc;
  CommBlock cb;

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

  if (nextLbid == (uint64_t)INVALID_LBID)
    return ERR_IDX_LIST_SET_NEXT_LBID;

  m_idxRidListArrayPtr.nextIdxListPtr.type = (int)LIST_BLOCK_TYPE;
  m_idxRidListArrayPtr.nextIdxListPtr.curLevel = m_curLevel;
  m_idxRidListArrayPtr.nextIdxListPtr.count = count;
  m_idxRidListArrayPtr.nextIdxListPtr.nextLbid = nextLbid;
  m_idxRidListArrayPtr.nextIdxListPtr.spare = 0;

  if ((m_curBlock.lbid == m_lbid) && (m_curBlock.state >= BLK_READ))
  {
    rc = writeSubBlockEntry(cb, &m_curBlock, m_lbid, 0, BEGIN_LIST_BLK_LLP_POS + NEXT_BLK_PTR_OFFSET,
                            LIST_ENTRY_WIDTH, &m_idxRidListArrayPtr.nextIdxListPtr);
    m_curBlock.state = BLK_READ;

    if (rc != NO_ERROR)
      return rc;
  }
  else
    return ERR_IDX_LIST_SET_NEXT_LBID;

  return NO_ERROR;
}

/****************************************************************
 * DESCRIPTION:
 * Private Function for setting the last Fbo on header
 *
 ***************************************************************/
const int IndexList::initCurBlock()
{
  if ((m_curBlock.lbid == m_lbid) && (m_curBlock.state >= BLK_READ))
  {
    memset(&m_idxRidListArrayPtr, 0, sizeof(m_idxRidListArrayPtr));

    for (int i = 0; i < TOTAL_NUM_ARRAY_PTR; i++)
    {
      m_idxRidListArrayPtr.childIdxRidListPtr[i].type = LIST_NOT_USED_TYPE;
      m_idxRidListArrayPtr.childIdxRidListPtr[i].llpStat = LLP_NOT_FULL;
      m_idxRidListArrayPtr.childIdxRidListPtr[i].spare = 0;
      m_idxRidListArrayPtr.childIdxRidListPtr[i].childLbid = INVALID_LBID;
    }

    m_idxRidListArrayPtr.nextIdxListPtr.type = LIST_SIZE_TYPE;
    m_idxRidListArrayPtr.nextIdxListPtr.spare = 0;
    m_idxRidListArrayPtr.nextIdxListPtr.curLevel = m_curLevel;
    m_idxRidListArrayPtr.nextIdxListPtr.count = 0;
    m_idxRidListArrayPtr.nextIdxListPtr.nextLbid = INVALID_LBID;

    m_idxRidListArrayPtr.parentIdxListPtr.type = LIST_LLP_TYPE;
    m_idxRidListArrayPtr.parentIdxListPtr.spare = 0;
    m_idxRidListArrayPtr.parentIdxListPtr.curLevelPos = m_curLevelPos;
    m_idxRidListArrayPtr.parentIdxListPtr.curBlkPos = m_curBlkPos;
    m_idxRidListArrayPtr.parentIdxListPtr.parentLbid = m_parentLbid;

    setSubBlockEntry(m_curBlock.data, 0, BEGIN_LIST_BLK_LLP_POS, LIST_BLK_LLP_ENTRY_WIDTH,
                     &m_idxRidListArrayPtr);
  }
  else
    return ERR_IDX_LIST_INIT_LINK_BLKS;

  return NO_ERROR;
}
/****************************************************************
 * DESCRIPTION:
 * Private Function for setting the last Fbo on header
 *
 ***************************************************************/
const int IndexList::getNextInfoFromArray(IdxRidNextListPtr& nextIdxListPtr)
{
  if ((m_curBlock.lbid == m_lbid) && (m_curBlock.state >= BLK_READ))
    getSubBlockEntry(m_curBlock.data, 0, BEGIN_LIST_BLK_LLP_POS + NEXT_BLK_PTR_OFFSET, LIST_ENTRY_WIDTH,
                     &nextIdxListPtr);
  else
    return ERR_IDX_LIST_GET_NEXT;

  return NO_ERROR;
}
/****************************************************************
 * DESCRIPTION:
 * Private Function for setting the last Fbo on header
 *
 ***************************************************************/
const int IndexList::getParentInfoFromArray(IdxRidParentListPtr& parentIdxListPtr)
{
  int rc;
  CommBlock cb;

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

  if ((m_curBlock.lbid == m_lbid) && (m_curBlock.state >= BLK_READ))
    getSubBlockEntry(m_curBlock.data, 0, BEGIN_LIST_BLK_LLP_POS + PARENT_PTR_OFFSET, LIST_ENTRY_WIDTH,
                     &parentIdxListPtr);
  else
    return ERR_IDX_LIST_GET_PARENT;

  m_curLevelPos = parentIdxListPtr.curLevelPos;
  m_curBlkPos = parentIdxListPtr.curBlkPos;
  m_parentLbid = parentIdxListPtr.parentLbid;

  if (m_parentLbid == (uint64_t)INVALID_LBID)
    return NO_ERROR;

  memset(m_parentBlock.data, 0, sizeof(m_parentBlock.data));
  rc = readDBFile(cb, &m_parentBlock, m_parentLbid);
  m_parentBlock.lbid = m_parentLbid;
  m_parentBlock.state = BLK_READ;
  m_parentBlock.dirty = true;
  return NO_ERROR;
}
/****************************************************************
 * DESCRIPTION:
 * Private Function for setting the last Fbo on header
 *
 ***************************************************************/
const int IndexList::updateCurCountInArray(int insCnt)
{
  int rc = NO_ERROR;
  IdxRidNextListPtr nextIdxListPtr;

  memset(&nextIdxListPtr, 0, sizeof(IdxRidNextListPtr));

  if ((m_curBlock.lbid == m_lbid) && (m_curBlock.state >= BLK_READ))
  {
    getSubBlockEntry(m_curBlock.data, 0, BEGIN_LIST_BLK_LLP_POS + NEXT_BLK_PTR_OFFSET, LIST_ENTRY_WIDTH,
                     &nextIdxListPtr);

    nextIdxListPtr.count = nextIdxListPtr.count + insCnt;
    setSubBlockEntry(m_curBlock.data, 0, BEGIN_LIST_BLK_LLP_POS + NEXT_BLK_PTR_OFFSET, LIST_ENTRY_WIDTH,
                     &nextIdxListPtr);
    m_curBlock.state = BLK_WRITE;
  }
  else
    return ERR_IDX_LIST_GET_COUNT;

  return rc;
}
/****************************************************************
 * DESCRIPTION:
 * childLbid is the new child block and need to register it in
 * the correct parent and assigned level
 * Private Function for getting the header
 *
 ***************************************************************/
const int IndexList::updateParentStatus(uint64_t& childLbid)
{
  int rc;
  CommBlock cb;

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

  IdxRidListArrayPtr idxRidListArrayPtr;

  // Get the parent block read out or get the 4 children pointers out
  if ((m_parentBlock.state == BLK_INIT) || (m_parentBlock.lbid != m_parentLbid))
  {
    rc = readSubBlockEntry(cb, &m_parentBlock, m_parentLbid, 0, BEGIN_LIST_BLK_LLP_POS,
                           LIST_BLK_LLP_ENTRY_WIDTH, &idxRidListArrayPtr);
    m_parentBlock.dirty = true;
    m_parentBlock.state = BLK_READ;
    m_parentBlock.lbid = m_parentLbid;
  }
  else
    getSubBlockEntry(m_parentBlock.data, 0, BEGIN_LIST_BLK_LLP_POS, LIST_BLK_LLP_ENTRY_WIDTH,
                     &idxRidListArrayPtr);

  // update current Child Block to full
  // The reason to update parent is becuse CURRENT child link is full
  // The CURRENT child lbid is m_lbid, m_curBlkPos is where it was registered
  if (m_lbid != m_parentLbid)  // The fulled child is not the parent itself
  {
    // normal case found the child block is full and set it full

    if (idxRidListArrayPtr.childIdxRidListPtr[m_curBlkPos].type == (int)LIST_BLOCK_TYPE)
      idxRidListArrayPtr.childIdxRidListPtr[m_curBlkPos].llpStat = LLP_FULL;
    // else if ((m_curBlkPos==0) && (m_flag)) //Next level parent, a new parent, no child
    else if (m_curBlkPos == 0)  // get here only when a brand new parent given,
                                // it went up one level and the go back down to the same level,

    {
      if (!m_flag)  // it is NOT the first block, this flag is useless, cannot be FALSE!
        return ERR_IDX_LIST_WRONG_TYPE;

      idxRidListArrayPtr.childIdxRidListPtr[m_curBlkPos].type = LIST_BLOCK_TYPE;
      idxRidListArrayPtr.childIdxRidListPtr[m_curBlkPos].llpStat = LLP_NOT_FULL;
      idxRidListArrayPtr.childIdxRidListPtr[m_curBlkPos].spare = 0;
      idxRidListArrayPtr.childIdxRidListPtr[m_curBlkPos].childLbid = childLbid;
      setSubBlockEntry(m_parentBlock.data, 0, BEGIN_LIST_BLK_LLP_POS + m_curBlkPos, LIST_ENTRY_WIDTH,
                       &idxRidListArrayPtr.childIdxRidListPtr[m_curBlkPos]);
      rc = writeDBFile(cb, m_parentBlock.data, m_parentLbid);
      m_parentBlock.state = BLK_READ;
      m_curLevelPos++;
      return rc;
    }
    else  // m_flag cannot be false since it is not the first block
      return ERR_IDX_LIST_WRONG_TYPE;
  }
  else  // This is the first block-> the current block's parent is itself
  {
    // only done once when first block was full
    m_curBlkPos = 0;
    idxRidListArrayPtr.childIdxRidListPtr[m_curBlkPos].type = (int)LIST_BLOCK_TYPE;
    idxRidListArrayPtr.childIdxRidListPtr[m_curBlkPos].llpStat = LLP_FULL;
    idxRidListArrayPtr.childIdxRidListPtr[m_curBlkPos].spare = 0;
    idxRidListArrayPtr.childIdxRidListPtr[m_curBlkPos].childLbid = childLbid;
    setSubBlockEntry(m_parentBlock.data, 0, BEGIN_LIST_BLK_LLP_POS + m_curBlkPos, LIST_ENTRY_WIDTH,
                     &idxRidListArrayPtr.childIdxRidListPtr[m_curBlkPos]);
    rc = writeDBFile(cb, m_parentBlock.data, m_parentLbid);
    m_parentBlock.state = BLK_READ;
    m_curLevel = 1;
    m_curLevelPos = 0;
    return rc;
  }

  int i;

  for (i = 0; i < TOTAL_NUM_ARRAY_PTR; i++)
  {
    if (idxRidListArrayPtr.childIdxRidListPtr[i].type != (int)LIST_BLOCK_TYPE)
    {
      idxRidListArrayPtr.childIdxRidListPtr[i].type = LIST_BLOCK_TYPE;
      idxRidListArrayPtr.childIdxRidListPtr[i].llpStat = LLP_NOT_FULL;
      idxRidListArrayPtr.childIdxRidListPtr[i].spare = 0;
      idxRidListArrayPtr.childIdxRidListPtr[i].childLbid = childLbid;
      setSubBlockEntry(m_parentBlock.data, 0, BEGIN_LIST_BLK_LLP_POS + i, LIST_ENTRY_WIDTH,
                       &idxRidListArrayPtr.childIdxRidListPtr[i]);
      rc = writeDBFile(cb, m_parentBlock.data, m_parentLbid);
      m_parentBlock.state = BLK_READ;
      m_curBlkPos = i;  // Need to figure out this where to put it
      m_curLevelPos++;
      return rc;
    }
  }

  // Parent is full and all children are full Not found any child pointer is available to add
  // then go to sibling in the same level or down
  // Need to look for the next block on parent block, on level down
  // The level will be increment by 1 HERE
  // Change parent lbid go to next link
  if (m_curLevelPos == (pow(4.0, m_curLevel) - 1))  // if the last node of this level
  {
    m_curLevel++;
    m_curLevelPos = -1;
  }

  m_flag = true;  // this need to go
  // A new parent
  m_curBlkPos = 0;
  m_parentLbid = idxRidListArrayPtr.nextIdxListPtr.nextLbid;
  memset(m_parentBlock.data, 0, sizeof(m_parentBlock.data));
  m_parentBlock.state = BLK_INIT;
  m_parentBlock.dirty = false;

  rc = updateParentStatus(childLbid);
  return rc;
}
}  // namespace WriteEngine
