//-----------------------------------------------------------------------------
// Copyright (c) 1994,1995 Southeastern Universities Research Association,
//                         Continuous Electron Beam Accelerator Facility
//
// This software was developed under a United States Government license
// described in the NOTICE file included as part of this distribution.
//
//-----------------------------------------------------------------------------
//
// Description:
//      Implementation of block list which holds pointer of transaction object
//
// Author:  Jie Chen
//
// Revision History:
//   cdevBlockList.cc,v
// Revision 1.1  1995/12/08  15:33:15  chen
// linked block list
//
// Revision 1.2  1995/10/16  18:38:21  chen
// Change to work under SunOs and g++
//
// Revision 1.1.1.1  1995/06/16  17:14:07  epics
// initial import of cdev
//
//
#include "cdevBlockList.h"

//============================================================================
//        class cdevBlockList implementation
//============================================================================
cdevBlockList::cdevBlockList (unsigned int blockSize)
:firstBlock_(0, 0, blockSize),blockSize_(blockSize), size_ (blockSize),
 defValue_ (0)
{
  // empty
}

cdevBlockList::cdevBlockList (const blockListItem& defVal, 
				    unsigned int blockSize)
:firstBlock_(0, 0, defVal, blockSize), blockSize_(blockSize), size_ (blockSize)
{
  defValue_ = new blockListItem;
  *defValue_ = defVal;
}

cdevBlockList::cdevBlockList (const cdevBlockList &rsc)
:firstBlock_ (rsc.firstBlock_), blockSize_ (rsc.blockSize_), 
 size_ (rsc.size_)
{
  if (rsc.defValue_){
    defValue_ = new blockListItem;
    *defValue_ = *rsc.defValue_;
  }
  else
    defValue_ = 0;
}

cdevBlockList&
cdevBlockList::operator = (const cdevBlockList &rsc)
{
  if (this != &rsc){
    deleteAll();
    firstBlock_ = rsc.firstBlock_;
    blockSize_ = rsc.blockSize_;
    size_ = rsc.size_;
    if (rsc.defValue_){
      defValue_ = new blockListItem;
      *defValue_ = *rsc.defValue_;
    }
    else
      defValue_ = 0;
  }
  return *this;
}

cdevBlockList::~cdevBlockList (void)
{
  deleteAll ();
}

cdevBlockList*
cdevBlockList::duplicate (void) const
{
  cdevBlockList *newList = new cdevBlockList (*this);
  return newList;
}

blockListItem &
cdevBlockList::operator [] (unsigned int index) const
{
  // get value indexed by number 'index'
  assert (index < length());
  int steps = index/blockSize_;
  int i = 0;
  const cdevBlockLink *p = &firstBlock_;

  while (i < steps){
    p = p->ptrToNextLink_;
    i++;
  }

  int slots = index - steps*blockSize_;
  return p->value_[slots];
}
    

int
cdevBlockList::includes (blockListItem value) const
{
  for (const cdevBlockLink *p = &firstBlock_; p != 0; p = p->ptrToNextLink_){
    for (int i = 0; i < blockSize_; i++){
      if (p->value_[i] == value){
	return 1;
      }
    }
  }
  return 0;
}

int
cdevBlockList::length (void) const
{
  return size_;
}

unsigned int
cdevBlockList::entryNumber (blockListItem *ptr)
{
  unsigned int num = 0;

  for (const cdevBlockLink *q = &firstBlock_; q != 0; q = q->ptrToNextLink_){
    for (int i = 0; i < blockSize_; i++){
      if (ptr == &(q->value_[i]))
	return num;
      num++;
    }
  }
  return 0;
}

blockListItem *
cdevBlockList::entryPointer (unsigned int steps)
{
  unsigned int numBlocks = steps/blockSize_;
  unsigned int slotNum = steps - blockSize_*numBlocks;
  cdevBlockLink *p = &firstBlock_;
  
  int i = 0;
  while(i < numBlocks+1){
    p = p->ptrToNextLink_;
    i++;
  }
  return &(p->value_[slotNum]);
}

cdevBlockLink *
cdevBlockList::blockPointer (blockListItem *ptr)
{
  blockListItem *p;

  for (cdevBlockLink *q = &firstBlock_; q != 0; q = q->ptrToNextLink_){
    p = q->value_;
    for (int i = 0; i < blockSize_; i++){
      if (p == ptr)
	return q;
      p++;
    }
  }
  return 0;  
}

void
cdevBlockList::deleteAll (void)
{
  // clear all out from the list except the first link
  if (firstBlock_.ptrToNextLink_){
    cdevBlockLink *next;
    for (cdevBlockLink * p = firstBlock_.ptrToNextLink_; p != 0; p = next){
      // delete the element pointed by p
      next = p->ptrToNextLink_;
      p->ptrToNextLink_ = 0;
      p->ptrToPrevLink_ = 0;
      delete p;
    }
  }
  blockListItem junk = NULL;
  for (int i = 0; i < blockSize_; i++)
    firstBlock_.value_[i] = junk;
  if (defValue_){
    delete defValue_;
    defValue_ = 0;
  }
  size_ = blockSize_;
}

void
cdevBlockList::clearAll (void)
{
  blockListItem junk = NULL;
  for (cdevBlockLink * p = &firstBlock_; p != 0; p = p->ptrToNextLink_){
    for (int i = 0; i < blockSize_; i++)
      p->value_[i] = junk;
  }
  if (defValue_){
    delete defValue_;
    defValue_ = 0;
  }
}
  
//============================================================================
//        class blockLink implementation
//============================================================================
cdevBlockLink::cdevBlockLink (cdevBlockLink *nxt, 
				    cdevBlockLink *pre,
				    unsigned int blkSize)
:blockSize_ (blkSize), ptrToNextLink_ (nxt), ptrToPrevLink_ (pre)
{
  value_ = new blockListItem[blkSize];
  assert (value_);
  if (nxt)
    nxt->ptrToPrevLink_ = this;
  if (pre)
    pre->ptrToNextLink_ = this;
}

cdevBlockLink::cdevBlockLink (cdevBlockLink *nxt, 
				    cdevBlockLink *pre,
				    const blockListItem& defVal,
				    unsigned int blkSize)
:blockSize_ (blkSize), ptrToNextLink_ (nxt), ptrToPrevLink_ (pre)
{
  value_ = new blockListItem[blkSize];
  assert (value_);
  for (int i = 0; i < blkSize; i++)
    value_[i] = defVal;
  if (nxt)
    nxt->ptrToPrevLink_ = this;
  if (pre)
    pre->ptrToNextLink_ = this;
}

cdevBlockLink::cdevBlockLink (cdevBlockLink *nxt, 
				    cdevBlockLink *pre,
				    blockListItem *data, unsigned int blkSize)
:blockSize_ (blkSize), ptrToNextLink_ (nxt), ptrToPrevLink_ (pre)
{
  value_ = new blockListItem[blkSize];
  assert (value_);
  for (int i = 0; i < blkSize; i++)
    value_[i] = data[i];
  if (nxt)
    nxt->ptrToPrevLink_ = this;
  if (pre)
    pre->ptrToNextLink_ = this;
}

cdevBlockLink::cdevBlockLink (const cdevBlockLink &link)
:blockSize_ (link.blockSize_)
{
  ptrToNextLink_ = ptrToPrevLink_ = 0;
  value_ = new blockListItem[link.blockSize_];
  for (int i = 0; i < blockSize_; i++)
    value_[i] = link.value_[i];
  if (link.ptrToNextLink_){
    ptrToNextLink_ = (link.ptrToNextLink_)->duplicate(0);
    ptrToNextLink_->ptrToPrevLink_ = this;
  }
  if (link.ptrToPrevLink_){
    ptrToPrevLink_ = (link.ptrToPrevLink_)->duplicate (1);
    ptrToPrevLink_->ptrToNextLink_ = this;
  }
}

cdevBlockLink&
cdevBlockLink::operator = (const cdevBlockLink &link)
{
  if (this != &link){
    ptrToNextLink_ = ptrToPrevLink_ = 0;
    freeAllMemory ();
    value_ = new blockListItem[link.blockSize_];
    blockSize_ = link.blockSize_;
    for (int i = 0; i < link.blockSize_; i++)
      value_[i] = link.value_[i];
    if (link.ptrToNextLink_){
      ptrToNextLink_ = (link.ptrToNextLink_)->duplicate(0);
      ptrToNextLink_->ptrToPrevLink_ = this;
    }
    if (link.ptrToPrevLink_){
      ptrToPrevLink_ = (link.ptrToPrevLink_)->duplicate (1);
      ptrToPrevLink_->ptrToNextLink_ = this;
    }
  }
  return *this;
}

void
cdevBlockLink::freeAllMemory (void)
{
  cdevBlockLink *next, *prev;

  if (ptrToNextLink_){
    for (cdevBlockLink * p = ptrToNextLink_; p != 0; p = next){
      next = p->ptrToNextLink_;
      p->ptrToNextLink_ = 0;
      p->ptrToPrevLink_ = 0;
      delete p;
    }
  }
  if (ptrToPrevLink_){
    for (cdevBlockLink *p = ptrToPrevLink_; p != 0; p = prev){
      prev = p->ptrToPrevLink_;
      p->ptrToPrevLink_ = 0;
      p->ptrToNextLink_ = 0;
      delete p;
    }
  }
  ptrToNextLink_ = 0;
  ptrToPrevLink_ = 0;
  delete []value_;
  blockSize_ = 0;
}

cdevBlockLink *
cdevBlockLink::duplicate (void)
{
  cdevBlockLink *newLink, *prev, *p, *q, *next;

  newLink = new cdevBlockLink (0, 0, value_, blockSize_);
  prev = newLink;
  // if there is a next field, copy remainder of the list
  if (ptrToNextLink_ != 0){
    for (p = ptrToNextLink_; p != 0; p = p->ptrToNextLink_){
      q = new cdevBlockLink (0, prev, p->value_, blockSize_);
      prev = q;
    }
  }

  // if there is previous filed
  next = newLink;
  if (ptrToPrevLink_ != 0){
    for (p = ptrToPrevLink_; p != 0; p = p->ptrToPrevLink_){
      q = new cdevBlockLink (next, 0, p->value_, blockSize_);
      next = q;
    }
  }
  assert (newLink != 0);
  return newLink;
}

cdevBlockLink *
cdevBlockLink::duplicate (int type)
{
  cdevBlockLink *newLink, *prev, *p, *q, *next;

  newLink = new cdevBlockLink (0, 0, value_, blockSize_);
  if (type == 0){ // copy from here to right
    prev = newLink;
    // if there is a next field, copy remainder of the list
    if (ptrToNextLink_ != 0){
      for (p = ptrToNextLink_; p != 0; p = p->ptrToNextLink_){
	q = new cdevBlockLink (0, prev, p->value_, blockSize_);
	prev = q;
      }
    }
  }
  else{ // copy from here to left
    // if there is previous filed
    next = newLink;
    if (ptrToPrevLink_ != 0){
      for (p = ptrToPrevLink_; p != 0; p = p->ptrToPrevLink_){
	q = new cdevBlockLink (next, 0, p->value_, blockSize_);
	next = q;
      }
    }
  }    
  assert (newLink != 0);
  return newLink;
}
    
cdevBlockLink::~cdevBlockLink (void)
{
  delete []value_;
  blockSize_ = 0;
  ptrToNextLink_ = 0;
  ptrToPrevLink_ = 0;
}

cdevBlockLink *
cdevBlockLink::addNewBlock (const blockListItem &defValue)
{
  // insert a new block after current block
  ptrToNextLink_ = new cdevBlockLink (ptrToNextLink_, this, 
				     defValue, blockSize_);
  assert (ptrToNextLink_ != 0);
  return ptrToNextLink_;
}

cdevBlockLink *
cdevBlockLink::addNewBlock (void)
{
  // insert a new block after current block
  ptrToNextLink_ = new cdevBlockLink (ptrToNextLink_, this, blockSize_);
  assert (ptrToNextLink_ != 0);
  return ptrToNextLink_;
}

//============================================================================
//          class cdevBlockListIterator implementation
//============================================================================
cdevBlockListIterator::cdevBlockListIterator (cdevBlockList & l)
:data (l), curPos_ (0)
{
  currEntry_ = &(data.firstBlock_.value_[0]);
  blockStart_ = currEntry_;
  blockEnd_ = &(data.firstBlock_.value_[data.blockSize_ - 1]);
  cdevBlockLink *p = 0;
  for (p = &(data.firstBlock_); p->ptrToNextLink_ != 0; 
       p = p->ptrToNextLink_)
    ;
  listEnd_ = &(p->value_[data.blockSize_ - 1]);
}

int
cdevBlockListIterator::init (void)
{
  curPos_ = 0;
  currEntry_ = &(data.firstBlock_.value_[0]);
  blockStart_ = currEntry_;
  blockEnd_ = &(data.firstBlock_.value_[data.blockSize_ - 1]);
  cdevBlockLink *p = 0;
  for (p = &data.firstBlock_; p->ptrToNextLink_ != 0; 
       p = p->ptrToNextLink_)
    ;
  listEnd_ = &(p->value_[data.blockSize_ - 1]);
  return 1;
}

int
cdevBlockListIterator::end (void)
{
  cdevBlockLink *p = 0;
  for (p = &data.firstBlock_; p->ptrToNextLink_ != 0;
       p = p->ptrToNextLink_)
    ;
  curPos_ = data.length() - 1;
  blockStart_ = &(p->value_[0]);
  blockEnd_ = &(p->value_[data.blockSize_ -1]);
  currEntry_ = blockEnd_;
  listEnd_ = blockEnd_;
  return 1;
}

blockListItem
cdevBlockListIterator::operator () (void)
{
  return *currEntry_;
}

int
cdevBlockListIterator::operator ! (void)
{
  return curPos_ < data.length() && curPos_ >= 0;
}

int
cdevBlockListIterator::operator ++ (void)
{
  if (currEntry_ != blockEnd_){ // still inside one block
    currEntry_++;
    curPos_++;
    return 1;
  }
  else if (currEntry_ != listEnd_){ // reach one end of block
    cdevBlockLink *currBlock = data.blockPointer (currEntry_);
    cdevBlockLink *p = currBlock->ptrToNextLink_;
    currEntry_ = &(p->value_[0]);
    blockStart_ = currEntry_;
    blockEnd_ = &(p->value_[data.blockSize_ - 1]);
    curPos_++;
    return 1;
  }
  else{
    curPos_++;
    return 0;
  }
}

int
cdevBlockListIterator::operator -- (void)
{
  if (currEntry_ != blockStart_){ // still inside one block
    currEntry_--;
    curPos_--;
    return 1;
  }
  else if (currEntry_ != &(data.firstBlock_.value_[0])){ // reach beginning 
    cdevBlockLink *currBlock = data.blockPointer (currEntry_);
    cdevBlockLink *p = currBlock->ptrToPrevLink_;
    blockStart_ = &(p->value_[0]);
    blockEnd_ = &(p->value_[data.blockSize_ - 1]);
    currEntry_ = blockEnd_;
    curPos_--;
    return 1;
  }
  else { // begnning of the list
    curPos_--;
    return 0;
  }
}

void
cdevBlockListIterator::operator = (blockListItem newVal)
{
  *currEntry_ = newVal;
}

blockListItem *
cdevBlockListIterator::currentPosition (void)
{
  return currEntry_;
}

int
cdevBlockListIterator::forcePut (blockListItem newVal)
{
  if (curPos_ < data.length()){
    *currEntry_ = newVal;
  }
  else{
    cdevBlockLink *p = 0;
    for (p = &data.firstBlock_; p->ptrToNextLink_ != 0; 
	 p = p->ptrToNextLink_)
      ;
    if (data.defValue_){
      p->addNewBlock(*(data.defValue_));
      data.size_ += data.blockSize_;
    }
    else{
      p->addNewBlock();
      data.size_ += data.blockSize_;
    }
    cdevBlockLink *curr = p->ptrToNextLink_;
    currEntry_ = &(curr->value_[0]);
    blockStart_ = currEntry_;
    blockEnd_ = &(curr->value_[data.blockSize_ - 1]);
    listEnd_ = blockEnd_;
    *currEntry_ = newVal;
  }
  return 1;
}
