#ifndef _PYTHONQTMISC_H
#define _PYTHONQTMISC_H

/*
 *
 *  Copyright (C) 2010 MeVis Medical Solutions AG All Rights Reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  Further, this software is distributed without any warranty that it is
 *  free of the rightful claim of any third person regarding infringement
 *  or the like.  Any license provided herein, whether implied or
 *  otherwise, applies only to this software file.  Patent licenses, if
 *  any, provided herein do not apply to combinations of this program with
 *  other software, or any other product whatsoever.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *  Contact information: MeVis Medical Solutions AG, Universitaetsallee 29,
 *  28359 Bremen, Germany or:
 *
 *  http://www.mevis.de
 *
 */

//----------------------------------------------------------------------------------
/*!
// \file    PythonQtMisc.h
// \author  Florian Link
// \author  Last changed by $Author: florian $
// \date    2006-05
*/
//----------------------------------------------------------------------------------


#include <QList>

#define PythonQtValueStorage_ADD_VALUE(store, type, value, ptr) \
{  type* item = (type*)store.nextValuePtr(); \
   *item = value; \
   ptr = (void*)item; \
}

#define PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedPtr,store, type, value, ptr) \
{ \
  type* item = (type*)(alreadyAllocatedPtr?alreadyAllocatedPtr:store.nextValuePtr()); \
  *item = value; \
  ptr = (void*)item; \
}

//! stores a position in the PythonQtValueStorage
class PythonQtValueStoragePosition {

public:
  PythonQtValueStoragePosition() { chunkIdx = 0; chunkOffset = 0; }

  int chunkIdx;
  int chunkOffset;

};

//! a helper class that stores basic C++ value types in chunks
template <typename T, int chunkEntries> class PythonQtValueStorage
{
public:
  PythonQtValueStorage() {
    _chunkIdx  = 0;
    _chunkOffset = 0;
    _currentChunk = new T[chunkEntries];
    _chunks.append(_currentChunk);
  };

  //! clear all memory
  void clear() {
    T* chunk;
    Q_FOREACH(chunk, _chunks) {
      delete[]chunk;
    }
    _chunks.clear();
  }

  //! get the current position to be restored with setPos
  void getPos(PythonQtValueStoragePosition & pos) {
    pos.chunkIdx = _chunkIdx;
    pos.chunkOffset = _chunkOffset;
  }

  //! set the current position (without freeing memory, thus caching old entries for reuse)
  void setPos(const PythonQtValueStoragePosition& pos) {
    _chunkOffset = pos.chunkOffset;
    if (_chunkIdx != pos.chunkIdx) {
      _chunkIdx = pos.chunkIdx;
      _currentChunk = _chunks.at(_chunkIdx);
    }
  }

  //! add one default constructed value and return the pointer to it
  T* nextValuePtr() {
    if (_chunkOffset>=chunkEntries) {
      _chunkIdx++;
      if (_chunkIdx >= _chunks.size()) {
        T* newChunk = new T[chunkEntries];
        _chunks.append(newChunk);
        _currentChunk = newChunk;
      } else {
        _currentChunk = _chunks.at(_chunkIdx);
      }
      _chunkOffset = 0;
    }
    T* newEntry = _currentChunk + _chunkOffset;
    _chunkOffset++;
    return newEntry;
  };

protected:
  QList<T*> _chunks;

  int _chunkIdx;
  int _chunkOffset;
  T*  _currentChunk;

};

//! a helper class that stores basic C++ value types in chunks and clears the unused values on setPos() usage.
template <typename T, int chunkEntries> class PythonQtValueStorageWithCleanup : public PythonQtValueStorage<T, chunkEntries>
{  
public:
  void setPos(const PythonQtValueStoragePosition& pos) {
    if (_chunkIdx > pos.chunkIdx) {
      T*  firstChunk = _chunks.at(pos.chunkIdx);
      // clear region in first chunk
      for (int i = pos.chunkOffset; i < chunkEntries; i++) {
        firstChunk[i] = T();
      }
      for (int chunk = pos.chunkIdx + 1; chunk < _chunkIdx; chunk++) {
        // clear the full chunks between the first and last chunk
        T*  fullChunk = _chunks.at(chunk);
        for (int i = 0; i < chunkEntries; i++) {
          fullChunk[i] = T();
        }
      }
      // clear region in last chunk
      T*  lastChunk = _chunks.at(_chunkIdx);
      for (int i = 0; i < _chunkOffset; i++) {
        lastChunk[i] = T();
      }
    } else if (_chunkIdx == pos.chunkIdx) {
      // clear the region in the last chunk only
      T*  lastChunk = _chunks.at(_chunkIdx);
      for (int i = pos.chunkOffset; i<_chunkOffset; i++) {
        lastChunk[i] = T();
      }
    }

    PythonQtValueStorage<T, chunkEntries>::setPos(pos);
  }

private:
  using PythonQtValueStorage<T, chunkEntries>::_chunks;
  using PythonQtValueStorage<T, chunkEntries>::_chunkIdx;
  using PythonQtValueStorage<T, chunkEntries>::_chunkOffset;
  using PythonQtValueStorage<T, chunkEntries>::_currentChunk;
};

#endif
