// edm_tkey.h
#ifndef INCLUDED_EDM_TKEY
#define INCLUDED_EDM_TKEY

// PURPOSE
//   Provide the basic interface for all keys used to identify data chunks.
//
// CLASSES
//           edm_TKey: Identify a non-const data chunk based on type and load
//                     it into a handle.  
//      edm_ConstTKey: Identify and load a handle with a const data chunk with
//                     a particular parameterized type and load it into a
//                     handle. 
//        edm_PredKey: Identify a non-const data chunk based on type and the
//                     results of a unary predicate function and load it into a
//                     handle. 
//   edm_ConstPredKey: Identify a const data chunk based on type and the
//                     results of a unary predicate function and load it into a
//                     handle. 
//
// DESCRIPTION
//   The isMatch function in edm_TKey returns true if the specified chunk isA T
//   (where T is the parameterized type) and loads a given handle.
//
//   The isMatch function in edm_PredKey returns true if the specified chunk
//   isA T (where T is the parameterized type) and the results of applying the
//   parameterized Compare unary predicate.  The Compare type can be a template
//   or non-template of the form (for example)
//
//       class MomConserved {
//         public:
//           bool operator()(const ChunkA& c) const
//           {
//               // compare something here! e.g. check if momentum values of
//               // particles was not conserved.
//           }
//       };
//
//   Where ChunkA is a type derived from edm_Chunk.

#ifndef INCLUDED_EDM_KEY
#include "edm_key.h"
#endif

#ifndef INCLUDED_EDM_HANDLE
#include "edm_handle.h"
#endif

#ifndef INCLUDED_EDM_CHUNK
#include "edm_chunk.h"
#endif

                        // ==============
                        // CLASS edm_TKey
                        // ==============

template <class T>
class edm_TKey : public edm_Key {
    edm_Handle<T> *d_handle_p;

  public:
    // CREATORS
    edm_TKey(edm_Handle<T> *handle = 0) : d_handle_p(handle) {}
        // Create a typed key that will load the specified handle with a chunk
        // of type T.

    ~edm_TKey() {}

    // ACCESSORS
    T* isMatch(edm_Chunk* chunk)
        // Return true if the specified chunk isA T; else return false.
    {
        if (d_handle_p) {
            d_handle_p->load(dynamic_cast<T*>(chunk));
            return *d_handle_p;
        }
        else {
            return dynamic_cast<T*>(chunk);
        }
    }
};

                        // ===================
                        // CLASS edm_ConstTKey
                        // ===================

template <class T>
class edm_ConstTKey : public edm_ConstKey {
    edm_Handle<const T> *d_handle_p;

  public:
    // CREATORS
    edm_ConstTKey(edm_Handle<const T> *handle = 0) : d_handle_p(handle) {}
        // Create a typed key that will load the specified handle with a chunk
        // of type T.

    ~edm_ConstTKey() {}

    // ACCESSORS
    const T* isMatch(const edm_Chunk* chunk)
        // Return true if the specified chunk isA T; else return false.
    {
        if (d_handle_p) {
            d_handle_p->load(dynamic_cast<T*>((edm_Chunk*)chunk));
            return *d_handle_p;
        }
        else {
            return dynamic_cast<T*>((edm_Chunk*)chunk);
        }
    }
};

                        // =================
                        // CLASS edm_PredKey
                        // =================

template <class T, class Compare>
class edm_PredKey : public edm_Key {
    edm_Handle<T> *d_handle_p;

  public:
    // CREATORS
    edm_PredKey(edm_Handle<T> *handle = 0) : d_handle_p(handle) {}
        // Create a predicate typed key that will load the specified handle
        // with a chunk of type T.

    ~edm_PredKey() {}
           
    // ACCESSORS
    T* isMatch(edm_Chunk* chunk) 
        // Return the valid address of the specified chunk if it isA T and the
        // results of applying the predicate function are true; else return 0.
    {
        T* type = dynamic_cast<T*>(chunk);
        if (type && Compare<T>(*type) ) {
            d_handle_p->load(type);
            return type;
        }
        return 0;
    }
};

                        // ======================
                        // CLASS edm_ConstPredKey
                        // ======================

template <class T, class Compare>
class edm_ConstPredKey : public edm_ConstKey {
    edm_Handle<const T> *d_handle_p;

  public:
    // CREATORS
    edm_ConstPredKey(edm_Handle<const T> *handle = 0) : d_handle_p(handle) {}
        // Create a predicate typed key that will load the specified handle
        // with a chunk of type T.

    ~edm_ConstPredKey() {}
           
    // ACCESSORS
    const T* isMatch(const edm_Chunk* chunk) 
        // Return the valid address of the specified chunk if it isA T and the
        // results of applying the predicate function are true; else return 0.
    {
        T* type = dynamic_cast<T*>((edm_Chunk*)chunk);
        if (type && Compare<const T>(*type) ) {
            d_handle_p->load(type);
            return type;
        }
        return 0;
    }
};

#endif