/*****************************************************************************

  Licensed to Accellera Systems Initiative Inc. (Accellera) under one or
  more contributor license agreements.  See the NOTICE file distributed
  with this work for additional information regarding copyright ownership.
  Accellera 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.

 *****************************************************************************/
 
/*
Instance specific extensions, are extension that only a single instance of a module
may access. They are invisible to all other modules; they are private to this
instance so to speak.

As they are only of value to a certain instance, this instance knows very well
when it needs them and when it does not need them any longer (usually when
a transaction passes through a module for the last time).
It does not have to care if anyone else in the system may still have a
reference to the transaction as this one is not able to access the extension
anyway.
Therefore the instance is obliged to call set_extension when it wants to add a
private extension and clear_extension when it does not need it any more.

To get access to an instance specifc extension the module must own a so called
instance_specific_extension_accessor that provides the exclusive access rights.
Assuming the instance_specific_extension_accessor of a given module is called m_accessor
and the transaction of which the private extension is about to be accessed
is called txn, then the calls have to be

m_accessor(txn).set_extension(...);
or
m_accessor(txn).clear_extension(...);

The owner of the private extension is responsible to allocate/deallocate
the extension before/after setting/clearing the extension.
*/
 
#ifndef __INSTANCE_SPECIFIC_EXTENSIONS_H__
#define __INSTANCE_SPECIFIC_EXTENSIONS_H__

#include "tlm.h"

namespace tlm_utils {

//Helper to do the numbering of private extension accessors
inline unsigned int max_num_ispex_accessors(bool increment=false)
{
    static unsigned int max_num = 0;
    if (increment) ++max_num;
    return max_num;
}

//Helper to do the index generation for private extensions
inline unsigned int max_num_ispex(bool increment=false)
{
    static unsigned int max_num = 0;
    if (increment) ++max_num;
    return max_num;
}

//The private extension base. Similar to normal extension base, but without clone and free
class ispex_base
{
public:
    virtual ~ispex_base() {}
protected:
    static unsigned int register_private_extension()
    {
        return (max_num_ispex(true) - 1);
    };
};

//The templated private extension. Similar to normal extension
template <typename T>
class
instance_specific_extension : public ispex_base{
public:
    virtual ~instance_specific_extension() {}
    const static unsigned int priv_id;
};

template <typename T>
const
unsigned int instance_specific_extension<T>::priv_id = ispex_base::register_private_extension();


//this thing is basically a snippet of the generic_payload
// it contains all the extension specific code (the extension API so to speak)
// the differences are:
// - it calls back to its owner whenever a real (==non-NULL) extension gets set for the first time
// - it calls back to its owner whenever a living (==non-NULL) extension gets cleared
template<typename U>
class instance_specific_extensions_per_accessor{
public:

  typedef void (U::*cb)();

  instance_specific_extensions_per_accessor(U* container, cb inc, cb dec): m_container(container), m_inc(inc), m_dec(dec){
  }

  template <typename T> T* set_extension(T* ext)
  {
      resize_extensions();
      T* tmp = static_cast<T*>(m_extensions[T::priv_id]);
      m_extensions[T::priv_id] = static_cast<ispex_base*>(ext);
      if (!tmp && ext) (m_container->*m_inc)();
      return tmp;
  }
  // non-templatized version with manual index:
  ispex_base* set_extension(unsigned int index,
                                    ispex_base* ext)
  {
      resize_extensions();
      ispex_base* tmp = m_extensions[index];
      m_extensions[index] = ext;
      if (!tmp && ext) (m_container->*m_inc)();
      return tmp;
  }

  // Check for an extension, ext will point to 0 if not present
  template <typename T> void get_extension(T*& ext) const
  {
      ext = static_cast<T*>(m_extensions[T::priv_id]);
  }
  // Non-templatized version:
   ispex_base* get_extension(unsigned int index) const
  {
      return m_extensions[index];
  }

  // Clear extension, the argument is needed to find the right index:
  template <typename T> void clear_extension(const T* ext)
  {
      resize_extensions();
      if (m_extensions[T::priv_id]) (m_container->*m_dec)();
      m_extensions[T::priv_id] = static_cast<ispex_base*>(0);
  }
  // Non-templatized version with manual index
  void clear_extension(unsigned int index)
  {
      if (index < m_extensions.size())
      {
          if (m_extensions[index]) (m_container->*m_dec)();
          m_extensions[index] = static_cast<ispex_base*>(0);
      }
  }

  // Make sure the extension array is large enough. Can be called once by
  // an initiator module (before issuing the first transaction) to make
  // sure that the extension array is of correct size. This is only needed
  // if the initiator cannot guarantee that the generic payload object is
  // allocated after C++ static construction time.
  void resize_extensions()
  {
      m_extensions.expand(max_num_ispex());
  }
  
private:
  tlm::tlm_array<ispex_base*> m_extensions;
  U* m_container;
  cb m_inc, m_dec;
  
};

class instance_specific_extension_container;


//the pool for the container, plain as can be
class instance_specific_extension_container_pool{
  friend class instance_specific_extension_carrier;
  friend class instance_specific_extension_container;
  instance_specific_extension_container_pool() : unused(NULL){}
  inline ~instance_specific_extension_container_pool();
  inline static instance_specific_extension_container_pool& get_ispexcont_pool(){ static instance_specific_extension_container_pool tmp; return tmp;}
  inline instance_specific_extension_container* create();
  inline void free(instance_specific_extension_container*);
  
  instance_specific_extension_container* unused;
};

class instance_specific_extension_carrier;

//this thing contains the vector of extensions per accessor
//which can be really large so this one should be pool allocated
// therefore it keeps a use_count of itself to automatically free itself
// - to this end it provides callbacks to the extensions per accessor
//   to increment and decrement the use_count
class instance_specific_extension_container{
  friend class instance_specific_extension_container_pool;
  friend class instance_specific_extension_accessor;
  friend class instance_specific_extension_carrier;
  
  instance_specific_extension_container(): use_count(0), next(NULL){resize();}
  
  void resize(){
    m_ispex_per_accessor.resize(max_num_ispex_accessors());
    for (unsigned int i=0; i<m_ispex_per_accessor.size(); i++) {
      m_ispex_per_accessor[i]=new instance_specific_extensions_per_accessor<instance_specific_extension_container>(this, 
                                                                                   &instance_specific_extension_container::inc_use_count, 
                                                                                   &instance_specific_extension_container::dec_use_count
                                                                                   );
      m_ispex_per_accessor[i]->resize_extensions();
    }
  }
 
  ~instance_specific_extension_container(){
    for (unsigned int i=0; i<m_ispex_per_accessor.size(); i++) delete m_ispex_per_accessor[i];
  }
 
  void inc_use_count(){use_count++;}
  inline void dec_use_count();
  
  std::vector<instance_specific_extensions_per_accessor<instance_specific_extension_container>* > m_ispex_per_accessor; 
  unsigned int use_count;
  tlm::tlm_generic_payload* my_txn;
  instance_specific_extension_carrier* my_carrier;
  instance_specific_extension_container* next; //for pooling
};


inline instance_specific_extension_container_pool::~instance_specific_extension_container_pool(){
  while(unused) { instance_specific_extension_container* tmp=unused; unused=unused->next; delete tmp;}
}

instance_specific_extension_container* instance_specific_extension_container_pool::create(){
  if (!unused) {unused=new instance_specific_extension_container();}
  instance_specific_extension_container* tmp=unused;
  unused=unused->next;
  return tmp; 
}

void instance_specific_extension_container_pool::free(instance_specific_extension_container* cont){
  cont->next=unused;
  unused=cont;
}

//This is the class that actually sits in the extension array
//we keep this small since that one gets allocated and deallocated all the times
class instance_specific_extension_carrier: public tlm::tlm_extension<instance_specific_extension_carrier>{
  friend class instance_specific_extension_accessor;

public:
  instance_specific_extension_carrier(){
    m_container=instance_specific_extension_container_pool::get_ispexcont_pool().create();
    m_container->my_carrier=this;
  }
  
  virtual tlm::tlm_extension_base* clone() const {
    //we don't clone since private info is instance specific and associated to a given txn (the original)
    //so the deep copied txn will be virgin in terms of private info
    return NULL;
  }
  void copy_from(tlm::tlm_extension_base const &){return;}
  void free(){return;}
private:
  instance_specific_extension_container* m_container;
};

inline void instance_specific_extension_container::dec_use_count(){
  if ((--use_count)==0) { //if this container isn't used any more
    instance_specific_extension_container_pool::get_ispexcont_pool().free(this);  //we send it back to our pool
    //we have to do that manually, as we cannot rely on the fact that there is MM in the txn
    my_txn->clear_extension(my_carrier); //and remove it from the transaction's extension array
    delete my_carrier;
  }
}


//This class 'hides' all the instance specific extension stuff from the user
// he instantiates one of those (e.g. instance_specific_extension_accessor extAcc;) and can then access
// the private extensions
//    extAcc(txn).extensionAPIFnCall()
//  where extensionAPIFnCall is set_extension, get_extension, clear_extension,...
class instance_specific_extension_accessor{
public:
  instance_specific_extension_accessor(): m_index(max_num_ispex_accessors(true)-1){}
  
  template<typename T>
  inline instance_specific_extensions_per_accessor<instance_specific_extension_container>& operator()(T& txn){
    instance_specific_extension_carrier* carrier;
    txn.get_extension(carrier);
    if (!carrier){
      carrier=new instance_specific_extension_carrier();
      carrier->m_container->my_txn=&txn;
      txn.set_extension(carrier);
    }
    return *(carrier->m_container->m_ispex_per_accessor[m_index]);
  }
  
protected:
  unsigned int m_index;
};

}

#endif
