/*
 * notifier_ext.cxx
 *
 * Smart Notifiers and Notifier Lists
 *
 * Portable Windows Library
 *
 * Copyright (c) 2004 Reitek S.p.A.
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is Portable Windows Library.
 *
 * The Initial Developer of the Original Code is Post Increment
 *
 * Contributor(s): ______________________________________.
 *
 * $Revision: 20385 $
 * $Author: rjongbloed $
 * $Date: 2008-06-04 05:40:38 -0500 (Wed, 04 Jun 2008) $
 */

#ifdef __GNUC__
#pragma implementation "notifier_ext.h"
#endif

#include <ptlib.h>
#include <ptlib/notifier_ext.h>

//////////////////////////////////////////////////////////////////////////////

class PPointer : public PObject
{
  PCLASSINFO(PPointer, PObject);
protected:
  void *  m_Pointer;
public:
  PPointer(void * pointer) : m_Pointer(pointer) { }
  void * GetPointer() const { return m_Pointer; }
};

PDICTIONARY(PNotifierBroker, POrdinalKey, PPointer);

static unsigned s_ID = 0;
static PNotifierBroker s_Broker;
static PMutex s_BrokerLock;


unsigned PSmartNotifieeRegistrar::RegisterNotifiee(void * obj)
{
  s_BrokerLock.Wait();
  unsigned id = ++s_ID;
  s_Broker.SetAt(POrdinalKey(id), new PPointer(obj));
  s_BrokerLock.Signal();
  return id;
}


PBoolean PSmartNotifieeRegistrar::UnregisterNotifiee(unsigned id)
{
  PWaitAndSignal l(s_BrokerLock);
  if (s_Broker.Contains(id))
  {
    s_Broker.RemoveAt(id);
    return PTrue;
  }
  else
    return PFalse;
}


PBoolean PSmartNotifieeRegistrar::UnregisterNotifiee(void * /*obj*/)
{
  PAssertAlways(PUnimplementedFunction);
  return PFalse;
}


void * PSmartNotifieeRegistrar::GetNotifiee(unsigned id)
{
  void * obj = 0;

  s_BrokerLock.Wait();
  if (s_Broker.Contains(id))
  {
    obj = s_Broker.GetAt(id)->GetPointer();
  }
  s_BrokerLock.Signal();

  return obj;
}

//////////////////////////////////////////////////////////////////////////////

class PSmartFuncInspector : public PNotifierFunction
{
  PCLASSINFO(PSmartFuncInspector, PNotifierFunction);

public:
  PSmartFuncInspector(void *obj) : PNotifierFunction(obj) { }

  void * GetTarget() const { return object; }
  virtual void Call(PObject &, INT) const {}
};


class PSmartPtrInspector : public PSmartPointer
{
  PCLASSINFO(PSmartPtrInspector, PSmartPointer);

public:
  PSmartPtrInspector(const PNotifier& ptr) : PSmartPointer(ptr) { }

  void * GetObject() const { return object; }
  void * GetTarget() const;
};

void * PSmartPtrInspector::GetTarget() const
{
  if (!object)
    return 0;

  PObject * ptr = (PObject *)object;

  if (PIsDescendant(ptr, PSmartNotifierFunction))
    return ((PSmartNotifierFunction *)ptr)->GetNotifiee();
  else
    return ((PSmartFuncInspector *)ptr)->GetTarget();
}

//////////////////////////////////////////////////////////////////////////////

PBoolean PNotifierList::RemoveTarget(PObject * obj)
{
  Cleanup();

  for (_PNotifierList::iterator i = m_TheList.begin(); i != m_TheList.end() ; i++) {
    PSmartPtrInspector sptr(*i);

    if (sptr.GetTarget() == obj) {
      m_TheList.erase(i);
      return true;
    }
  }

  return false;
}


void PNotifierList::Move(PNotifierList& that)
{
  Cleanup();
  that.Cleanup();

  that.m_TheList.DisallowDeleteObjects();

  while (that.m_TheList.GetSize())
    m_TheList.Append(that.m_TheList.RemoveAt(0));

  that.m_TheList.AllowDeleteObjects();
}


void PNotifierList::Cleanup()
{
  for (_PNotifierList::iterator i = m_TheList.begin(); i != m_TheList.end() ; i++) {
    PSmartPtrInspector sptr(*i);
    PObject * ptr = (PObject *)sptr.GetObject();

    if (!ptr || (PIsDescendant(ptr, PSmartNotifierFunction) &&
      ((PSmartNotifierFunction *)ptr)->GetNotifiee() == 0))
    {
      PTRACE(2, "PNotifierList\tRemoving invalid notifier " << ((PSmartNotifierFunction *)ptr)->GetNotifieeID());
      m_TheList.erase(i);
      i = m_TheList.begin();
    }
  }
}


PBoolean PNotifierList::Fire(PObject& obj, INT val)
{
  if (!m_TheList.GetSize()) return PFalse;

  for (_PNotifierList::iterator i = m_TheList.begin(); i != m_TheList.end() ; i++)
  {
    PNotifier& n = *i;

#ifdef _DEBUG
    if (PTrace::CanTrace(6)) // Debug only
    {
      PSmartPtrInspector sptr(n);
      PObject * obj = (PObject *)sptr.GetTarget();

      if (obj)
      {
        PTRACE(6, "PNotifierList\tInvoking on " << obj->GetClass());
      }
    }
#endif

    n(obj, val);
  }

  return PTrue;
}

// End of File ///////////////////////////////////////////////////////////////

