//
// LDASTools frameCPP - A library implementing the LIGO/Virgo frame
// specification
//
// Copyright (C) 2018 California Institute of Technology
//
// LDASTools frameCPP is free software; you may redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 (GPLv2) of the
// License or at your discretion, any later version.
//
// LDASTools frameCPP is distributed in the hope that it will be useful, but
// without any warranty or even the implied warranty of merchantability
// or fitness for a particular purpose. See the GNU General Public
// License (GPLv2) for more details.
//
// Neither the names of the California Institute of Technology (Caltech),
// The Massachusetts Institute of Technology (M.I.T), The Laser
// Interferometer Gravitational-Wave Observatory (LIGO), nor the names
// of its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// You should have received a copy of the licensing terms for this
// software included in the file LICENSE located in the top-level
// directory of this package. If you did not, you can view a copy at
// http://dcc.ligo.org/M1500244/LICENSE
//

#ifndef FRAME_CPP_INTERFACE__FRAME_C_INTERNAL_HH
#define FRAME_CPP_INTERFACE__FRAME_C_INTERNAL_HH

#ifndef __cplusplus
#error This file needs to be compiled using C++
#endif /* __cplusplus */

#include <cstdlib>

#include <set>

#include "ldastoolsal/unordered_map.hh"
#include "ldastoolsal/mutexlock.hh"

#include "framecppc/FrameC.h"

//-----------------------------------------------------------------------
/// \brief Routines related to implementing the C interface
///
/// The C Interface allows for C applications to interface with the
/// C++ interface without having to use a C++ compiler.
///
/// \note
/// All exceptions are caught and translated into numeric error values.
//-----------------------------------------------------------------------
namespace FrameC
{
    extern const char* UnknownError;

    void Set( FrameCError** Error );

    void Set( FrameCError**      Error,
              framec_errno_type  Errno,
              const std::string& Message );

    struct Handle;

    enum pointer_type
    {
        POINTER_STREAM,
        POINTER_FR_CHANNEL,
        POINTER_FR_DETECTOR,
        POINTER_FR_EVENT,
        POINTER_FR_HISTORY,
        POINTER_FRAME_H,
        POINTER_FR_TOC
    };

    class Pointers
    {
    public:
        Pointers( );

        template < int T >
        static const char* Name( );

    private:
    };

    struct Handle
    {
    public:
        Handle( pointer_type Type );

        virtual ~Handle( );

        pointer_type Type( ) const;

        static void Register( FrameCError**     Error,
                              void*             Pointer,
                              pointer_type      ExpectedType,
                              framec_errno_type ErrorCode );

        static void Free( FrameCError**     Error,
                          void*             Pointer,
                          pointer_type      ExpectedType,
                          framec_errno_type ErrorCode );

        static void Deposit( const Handle* Ptr );

        static void Validate( const Handle* Ptr, pointer_type Type );

        static void Withdraw( const Handle* Ptr );

    private:
        typedef std::set< const Handle* > handle_container_type;

        static LDASTools::AL::MutexLock::baton_type m_baton;

        static handle_container_type m_handles;

        const pointer_type m_type;
    };

    inline pointer_type
    Handle::Type( ) const
    {
        return m_type;
    }

    template < typename T >
    pointer_type PointerType( T* );

    inline void
    Set( FrameCError** Error )
    {
        if ( *Error )
        {
            free( (void*)( ( *Error )->s_message ) );
            free( *Error );
        }

        *Error = (FrameCError*)NULL;
    }

    inline void
    Set( FrameCError** Error, framec_errno_type Errno, const char* Message )
    {
        FrameCError* retval =
            (FrameCError*)std::malloc( sizeof( FrameCError ) );

        retval->s_errno = Errno;
        retval->s_message = strdup( Message );

        *Error = retval;
    }

    inline void
    Set( FrameCError**      Error,
         framec_errno_type  Errno,
         const std::string& Message )
    {
        FrameCError* retval =
            (FrameCError*)std::malloc( sizeof( FrameCError ) );

        retval->s_errno = Errno;
        retval->s_message = strdup( Message.c_str( ) );

        *Error = retval;
    }

    template < class H >
    H
    HandleCast( const void* Base )
    {
        const Handle* base = reinterpret_cast< const Handle* >( Base );
        return dynamic_cast< H >( base );
    }

    template < class H >
    H
    HandleCast( void* Base )
    {
        Handle* base = reinterpret_cast< Handle* >( Base );
        return dynamic_cast< H >( base );
    }
} // namespace FrameC

#endif /* FRAME_CPP_INTERFACE__FRAME_C_INTERNAL_HH */
