/*
 * Copyright 2011 Google Inc. All Rights Reserved.
 *
 * Licensed 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.
 */

// Object reference count and smart pointer implementation.

// Smart pointer usage in sfntly:
//
// sfntly carries a smart pointer implementation like COM.  Ref-countable object
// type inherits from RefCounted<>, which have AddRef and Release just like
// IUnknown (but no QueryInterface).  Use a Ptr<> based smart pointer to hold
// the object so that the object ref count is handled correctly.
//
// class Foo : public RefCounted<Foo> {
//  public:
//   static Foo* CreateInstance() {
//     Ptr<Foo> obj = new Foo();  // ref count = 1
//     return obj.Detach();
//   }
// };
// typedef Ptr<Foo> FooPtr;  // common short-hand notation
// FooPtr obj;
// obj.Attach(Foo::CreatedInstance());  // ref count = 1
// {
//   FooPtr obj2 = obj;  // ref count = 2
// }  // ref count = 1, obj2 out of scope
// obj.Release();  // ref count = 0, object destroyed

// Notes on usage:
// 1. Virtual inherit from RefCount interface in base class if smart pointers
//    are going to be defined.
// 2. All RefCounted objects must be instantiated on the heap.  Allocating the
//    object on stack will cause crash.
// 3. Be careful when you have complex inheritance.  For example,
//    class A : public RefCounted<A>;
//    class B : public A, public RefCounted<B>;
//    In this case the smart pointer is pretty dumb and don't count on it to
//    nicely destroy your objects as designed. Try refactor your code like
//    class I;  // the common interface and implementations
//    class A : public I, public RefCounted<A>;  // A specific implementation
//    class B : public I, public RefCounted<B>;  // B specific implementation
// 4. Smart pointers here are very bad candidates for function parameters.  Use
//    dumb pointers in function parameter list.
// 5. When down_cast is performed on a dangling pointer due to bugs in code,
//    VC++ will generate SEH which is not handled well in VC++ debugger.  One
//    can use WinDBG to run it and get the faulting stack.
// 6. Idioms for heap object as return value
//    Foo* createFoo() { FooPtr obj = new Foo(); return obj.Detach(); }
//    Foo* passthru() { FooPtr obj = createFoo(), return obj; }
//    FooPtr end_scope_pointer;
//    end_scope_pointer.Attach(passThrough);
//    If you are not passing that object back, you are the end of scope.

#ifndef SFNTLY_CPP_SRC_SFNTLY_PORT_REFCOUNT_H_
#define SFNTLY_CPP_SRC_SFNTLY_PORT_REFCOUNT_H_

#if !defined (NDEBUG)
  #define ENABLE_OBJECT_COUNTER
//  #define REF_COUNT_DEBUGGING
#endif

#if defined (REF_COUNT_DEBUGGING)
  #include <stdio.h>
  #include <typeinfo>
#endif

#include "sfntly/port/atomic.h"
#include "sfntly/port/type.h"

// Special tag for functions that requires caller to attach instead of using
// assignment operators.
#define CALLER_ATTACH

#if defined (REF_COUNT_DEBUGGING)
  #define DEBUG_OUTPUT(a) \
      fprintf(stderr, "%s%s:oc=%d,oid=%d,rc=%d\n", a, \
              typeid(this).name(), object_counter_, object_id_, ref_count_)
#else
  #define DEBUG_OUTPUT(a)
#endif

#if defined (_MSC_VER)
  // VC 2008/2010 incorrectly gives this warning for pure virtual functions
  // in virtual inheritance.  The only way to get around it is to disable it.
  #pragma warning(disable:4250)
#endif

namespace sfntly {

template <typename T>
class Ptr;

class RefCount {
 public:
  // Make gcc -Wnon-virtual-dtor happy.
  virtual ~RefCount() {}

 private:
  template <typename T>
  friend class Ptr;

  virtual size_t AddRef() const = 0;
  virtual size_t Release() const = 0;
};

template <typename TDerived>
class RefCounted : virtual public RefCount {
 public:
  RefCounted() : ref_count_(0) {
#if defined (ENABLE_OBJECT_COUNTER)
    object_id_ = AtomicIncrement(&next_id_);
    AtomicIncrement(&object_counter_);
    DEBUG_OUTPUT("C ");
#endif
  }
  RefCounted(const RefCounted<TDerived>&) : ref_count_(0) {}
  virtual ~RefCounted() {
#if defined (ENABLE_OBJECT_COUNTER)
    AtomicDecrement(&object_counter_);
    DEBUG_OUTPUT("D ");
#endif
  }

  RefCounted<TDerived>& operator=(const RefCounted<TDerived>&) {
    // Each object maintains own ref count, don't propagate.
    return *this;
  }

 private:
  virtual size_t AddRef() const {
    size_t new_count = AtomicIncrement(&ref_count_);
    DEBUG_OUTPUT("A ");
    return new_count;
  }

  virtual size_t Release() const {
    size_t new_ref_count = AtomicDecrement(&ref_count_);
    DEBUG_OUTPUT("R ");
    if (new_ref_count == 0) {
      // A C-style is used to cast away const-ness and to derived.
      // lint does not like this but this is how it works.
      delete (TDerived*)(this);
    }
    return new_ref_count;
  }

  mutable size_t ref_count_;  // reference count of current object
#if defined (ENABLE_OBJECT_COUNTER)
  static size_t object_counter_;
  static size_t next_id_;
  mutable size_t object_id_;
#endif
};

#if defined (ENABLE_OBJECT_COUNTER)
template <typename TDerived> size_t RefCounted<TDerived>::object_counter_ = 0;
template <typename TDerived> size_t RefCounted<TDerived>::next_id_ = 0;
#endif

// semi-smart pointer for RefCount derived objects, similar to CComPtr
template <typename T>
class Ptr {
 public:
  Ptr() : p_(NULL) {
  }

  // This constructor shall not be explicit.
  // lint does not like this but this is how it works.
  Ptr(T* pT) : p_(NULL) {
    *this = pT;
  }

  Ptr(const Ptr<T>& p) : p_(NULL) {
    *this = p;
  }

  ~Ptr() {
    Release();
  }

  T* operator=(T* pT) {
    if (p_ == pT) {
      return p_;
    }
    if (pT) {
      RefCount* p = static_cast<RefCount*>(pT);
      if (p == NULL) {
        return NULL;
      }
      p->AddRef();  // always AddRef() before Release()
    }
    Release();
    p_ = pT;
    return p_;
  }

  T* operator=(const Ptr<T>& p) {
    if (p_ == p.p_) {
      return p_;
    }
    return operator=(p.p_);
  }

  operator T*&() {
    return p_;
  }

  T& operator*() const {
    return *p_;  // It can throw!
  }

  T* operator->() const {
    return p_;  // It can throw!
  }

  bool operator!() const {
    return (p_ == NULL);
  }

  bool operator<(const Ptr<T>& p) const {
    return (p_ < p.p_);
  }

  bool operator!=(T* pT) const {
    return !operator==(pT);
  }

  bool operator==(T* pT) const {
    return (p_ == pT);
  }

  size_t Release() const {
    size_t ref_count = 0;
    if (p_) {
      RefCount* p = static_cast<RefCount*>(p_);
      if (p) {
        ref_count = p->Release();
      }
      p_ = NULL;
    }
    return ref_count;
  }

  void Attach(T* pT) {
    if (p_ != pT) {
      Release();
      p_ = pT;
    }
  }

  T* Detach() {
    T* pT = p_;
    p_ = NULL;
    return pT;
  }

  mutable T* p_;
};

}  // namespace sfntly

#endif  // SFNTLY_CPP_SRC_SFNTLY_PORT_REFCOUNT_H_
