

#include "framecppc/FrDetector.h"
#include "framecppc/Stream.h"

#include "FrameCInternal.hh"
#include "FrDetectorInternal.hh"
#include "StreamInternal.hh"

using FrameC::Set;

namespace FrameC
{
    void
    FrDetector::Read( fr_file_t* restrict Stream, const char* restrict Name )
    {
        FrameCPP::IFrameStream* fs( StreamAsInput( Stream ) );

        ::FrameCPP::Common::FrameSpec::ObjectInterface::object_type o(
            fs->ReadDetector( Name ) );
        m_data = boost::dynamic_pointer_cast< data_t::element_type >( o );

        if ( !m_data )
        {
            std::ostringstream msg;

            msg << "Unable to locate the detector named " << Name
                << " within the stream.";

            throw std::range_error( msg.str( ) );
        }
    }

    template <>
    const char*
    Pointers::Name< POINTER_FR_DETECTOR >( )
    {
        return "FrDetector";
    }
} // namespace FrameC

fr_detector_t*
FrameCFrDetectorAlloc( FrameCError** Error,
                       const char* restrict name,
                       const char* restrict         prefix,
                       fr_detector_latitude_t       latitude,
                       fr_detector_longitude_t      longitude,
                       fr_detector_elevation_t      elevation,
                       fr_detector_arm_x_azimuth_t  azimuth_x,
                       fr_detector_arm_y_azimuth_t  azimuth_y,
                       fr_detector_arm_x_altitude_t altitude_x,
                       fr_detector_arm_y_altitude_t altitude_y,
                       fr_detector_arm_x_midpoint_t midpoint_x,
                       fr_detector_arm_y_midpoint_t midpoint_y )
{
    std::unique_ptr< FrameC::FrDetector > retval_sptr;

    Set( Error );

    try
    {
        retval_sptr.reset( new FrameC::FrDetector( name,
                                                   prefix,
                                                   latitude,
                                                   longitude,
                                                   elevation,
                                                   azimuth_x,
                                                   azimuth_y,
                                                   altitude_x,
                                                   altitude_y,
                                                   midpoint_x,
                                                   midpoint_y ) );
    }
    catch ( const std::exception& Exception )
    {
        Set( Error, FRAMEC_ERRNO_FR_DETECTOR_ALLOC_ERROR, Exception.what( ) );
        retval_sptr.reset( NULL );
    }
    catch ( ... )
    {
        Set(
            Error, FRAMEC_ERRNO_FR_DETECTOR_ALLOC_ERROR, FrameC::UnknownError );
        retval_sptr.reset( NULL );
    }
    //---------------------------------------------------------------------
    // Report the results
    //---------------------------------------------------------------------
    FrameC::Handle::Deposit( retval_sptr.get( ) );
    return reinterpret_cast< fr_detector_t* >( retval_sptr.release( ) );
}

void
FrameCFrDetectorFree( FrameCError** Error, fr_detector_t* detector )
{
    Set( Error );

    FrameC::FrDetector* oodetector( (FrameC::FrDetector*)detector );

    try
    {
        if ( oodetector )
        {
            FrameC::Handle::Free( Error,
                                  oodetector,
                                  FrameC::POINTER_FR_DETECTOR,
                                  FRAMEC_ERRNO_FR_DETECTOR_FREE_ERROR );
        }
    }
    catch ( const std::exception& Exception )
    {
        Set( Error, FRAMEC_ERRNO_FR_DETECTOR_FREE_ERROR, Exception.what( ) );
    }
}

fr_detector_t*
FrameCFrDetectorRead( FrameCError** Error,
                      fr_file_t* restrict Stream,
                      const char* restrict Name )
{
    Set( Error );

    std::unique_ptr< FrameC::FrDetector > retval_sptr;

    try
    {
        retval_sptr.reset( new FrameC::FrDetector );
        retval_sptr->Read( Stream, Name );
    }
    catch ( const std::exception& Exception )
    {
        Set( Error, FRAMEC_ERRNO_FR_DETECTOR_READ_ERROR, Exception.what( ) );
        retval_sptr.reset( NULL );
    }

    FrameC::Handle::Deposit( retval_sptr.get( ) );
    return reinterpret_cast< fr_detector_t* >( retval_sptr.release( ) );
}

int
FrDetectorWrite( FrameCError** Error,
                 fr_file_t* restrict  Stream,
                 const fr_detector_t* restrict Detector )
{
    int retval = 1;

    Set( Error );

    try
    {
        if ( Stream )
        {
            FrameC::FrDetector* detector( (FrameC::FrDetector*)Detector );

            if ( detector )
            {
                //--------------------------------------------------------------
                // Create or modify an FrDetector entry
                //--------------------------------------------------------------
                // Use emplace to construct in place since assignment is deleted
                Stream->s_detectors.erase( detector->m_data->GetName( ) );
                Stream->s_detectors.emplace( detector->m_data->GetName( ), 
                                           *( detector->m_data ) );
            }
            else
            {
                Set( Error,
                     FRAMEC_ERRNO_FR_DETECTOR_WRITE_ERROR,
                     "Invalid (NULL) Detector handle" );
                retval = 0;
            }
        }
        else
        {
            Set( Error,
                 FRAMEC_ERRNO_FR_DETECTOR_WRITE_ERROR,
                 "Invalid (NULL) Stream handle" );
            retval = 0;
        }
    }
    catch ( const std::exception& Exception )
    {
        Set( Error, FRAMEC_ERRNO_FR_DETECTOR_READ_ERROR, Exception.what( ) );
        retval = 0;
    }

    return retval;
}

namespace FrameC
{
    FrDetector::FrDetector( ) : Handle( POINTER_FR_DETECTOR )
    {
    }

    FrDetector::FrDetector( const char* restrict name,
                            const char* restrict         prefix,
                            fr_detector_latitude_t       latitude,
                            fr_detector_longitude_t      longitude,
                            fr_detector_elevation_t      elevation,
                            fr_detector_arm_x_azimuth_t  azimuth_x,
                            fr_detector_arm_y_azimuth_t  azimuth_y,
                            fr_detector_arm_x_altitude_t altitude_x,
                            fr_detector_arm_y_altitude_t altitude_y,
                            fr_detector_arm_x_midpoint_t midpoint_x,
                            fr_detector_arm_y_midpoint_t midpoint_y )
        : Handle( POINTER_FR_DETECTOR )
    {
        m_data.reset( new FrameCPP::FrDetector( name,
                                                prefix,
                                                latitude,
                                                longitude,
                                                elevation,
                                                azimuth_x,
                                                azimuth_y,
                                                altitude_x,
                                                altitude_y,
                                                midpoint_x,
                                                midpoint_y ) );
    }

    FrDetector::FrDetector( const data_core_t& Source )
        : Handle( POINTER_FR_DETECTOR )
    {
        m_data.reset( new data_core_t( Source ) );
    }

    FrDetector::~FrDetector( )
    {
    }
} // namespace FrameC

/**
 * Retrieve information about the FrDetector structure.
 * The Option parameter dictates the number and type of
 * parameters to follow.
 * Multiple pieces of information can be retrieved by having
 * multiple Option/parameter sets.
 * The last value for Option must be FR_DETECTOR_FIELD_LAST to indicate
 * the end of the variable length argument list.
 *
 */
void
FrameCFrDetectorQuery( FrameCError**        Error,
                       const fr_detector_t* Detector,
                       int                  Option,
                       ... )
{
    using namespace FrameC;

    Set( Error );

    const FrDetector* detector = HandleCast< const FrDetector* >( Detector );

    try
    {
        va_list ap;
        va_start( ap, Option );

        if ( detector )
        {
            while ( Option != FR_DETECTOR_FIELD_LAST )
            {
                /**
                 * The following is a discription of each value of Option and
                 * the parameters it takes.
                 *
                 * <ul>
                 *   <li> <b>FR_DETECTOR_FIELD_LAST</b>
                 *      <p> This is the last option in the list and specifies
                 *          the end of the query.
                 *      </p>
                 *   </li>
                 */
                switch ( Option )
                {
                case FR_DETECTOR_FIELD_ARM_X_ALTITUDE:
                    /**
                     *   <li> <b>FR_DETECTOR_FIELD_ARM_X_ALTITUDE</b>
                     *      <p> Retrieve the arm_x_altitude of the instrument
                     * arm_x_altitude. A single argument should follow this
                     * option. The argument needs to be the address of type
                     * <b>fr_detector_arm_x_altitude_t</b> The description will
                     * be stored here. <b>NOTE:</b> This is a shallow pointer
                     * which is only valid while the fr_detector_t object
                     * exists.
                     *      </p>
                     *   </li>
                     */
                    {
                        typedef fr_detector_arm_x_altitude_t x_t;
                        x_t*                                 x;

                        x = va_arg( ap, x_t* );
                        *x = detector->m_data->GetArmXaltitude( );
                    }
                    break;
                case FR_DETECTOR_FIELD_ARM_X_AZIMUTH:
                    /**
                     *   <li> <b>FR_DETECTOR_FIELD_ARM_X_AZIMUTH</b>
                     *      <p> Retrieve the arm_x_azimuth of the instrument
                     * arm_x_azimuth. A single argument should follow this
                     * option. The argument needs to be the address of type
                     * <b>fr_detector_arm_x_azimuth_t</b> The description will
                     * be stored here. <b>NOTE:</b> This is a shallow pointer
                     * which is only valid while the fr_detector_t object
                     * exists.
                     *      </p>
                     *   </li>
                     */
                    {
                        typedef fr_detector_arm_x_azimuth_t x_t;
                        x_t*                                x;

                        x = va_arg( ap, x_t* );
                        *x = detector->m_data->GetArmXazimuth( );
                    }
                    break;
                case FR_DETECTOR_FIELD_ARM_X_MIDPOINT:
                    /**
                     *   <li> <b>FR_DETECTOR_FIELD_ARM_X_MIDPOINT</b>
                     *      <p> Retrieve the arm_x_midpoint of the instrument
                     * arm_x_midpoint. A single argument should follow this
                     * option. The argument needs to be the address of type
                     * <b>fr_detector_arm_x_midpoint_t</b> The description will
                     * be stored here. <b>NOTE:</b> This is a shallow pointer
                     * which is only valid while the fr_detector_t object
                     * exists.
                     *      </p>
                     *   </li>
                     */
                    {
                        typedef fr_detector_arm_x_midpoint_t x_t;
                        x_t*                                 x;

                        x = va_arg( ap, x_t* );
                        *x = detector->m_data->GetArmXmidpoint( );
                    }
                    break;
                case FR_DETECTOR_FIELD_ARM_Y_ALTITUDE:
                    /**
                     *   <li> <b>FR_DETECTOR_FIELD_ARM_Y_ALTITUDE</b>
                     *      <p> Retrieve the arm_y_altitude of the instrument
                     * arm_y_altitude. A single argument should follow this
                     * option. The argument needs to be the address of type
                     * <b>fr_detector_arm_y_altitude_t</b> The description will
                     * be stored here. <b>NOTE:</b> This is a shallow pointer
                     * which is only valid while the fr_detector_t object
                     * exists.
                     *      </p>
                     *   </li>
                     */
                    {
                        typedef fr_detector_arm_y_altitude_t x_t;
                        x_t*                                 x;

                        x = va_arg( ap, x_t* );
                        *x = detector->m_data->GetArmYaltitude( );
                    }
                    break;
                case FR_DETECTOR_FIELD_ARM_Y_AZIMUTH:
                    /**
                     *   <li> <b>FR_DETECTOR_FIELD_ARM_Y_AZIMUTH</b>
                     *      <p> Retrieve the arm_y_azimuth of the instrument
                     * arm_y_azimuth. A single argument should follow this
                     * option. The argument needs to be the address of type
                     * <b>fr_detector_arm_y_azimuth_t</b> The description will
                     * be stored here. <b>NOTE:</b> This is a shallow pointer
                     * which is only valid while the fr_detector_t object
                     * exists.
                     *      </p>
                     *   </li>
                     */
                    {
                        typedef fr_detector_arm_y_azimuth_t x_t;
                        x_t*                                x;

                        x = va_arg( ap, x_t* );
                        *x = detector->m_data->GetArmYazimuth( );
                    }
                    break;
                case FR_DETECTOR_FIELD_ARM_Y_MIDPOINT:
                    /**
                     *   <li> <b>FR_DETECTOR_FIELD_ARM_Y_MIDPOINT</b>
                     *      <p> Retrieve the arm_y_midpoint of the instrument
                     * arm_y_midpoint. A single argument should follow this
                     * option. The argument needs to be the address of type
                     * <b>fr_detector_arm_y_midpoint_t</b> The description will
                     * be stored here. <b>NOTE:</b> This is a shallow pointer
                     * which is only valid while the fr_detector_t object
                     * exists.
                     *      </p>
                     *   </li>
                     */
                    {
                        typedef fr_detector_arm_y_midpoint_t x_t;
                        x_t*                                 x;

                        x = va_arg( ap, x_t* );
                        *x = detector->m_data->GetArmYmidpoint( );
                    }
                    break;
                case FR_DETECTOR_FIELD_ELEVATION:
                    /**
                     *   <li> <b>FR_DETECTOR_FIELD_ELEVATION</b>
                     *      <p> Retrieve the detector vertex elevation.
                     * 	    A single argument should follow this option.
                     *          The argument needs to be the address
                     *          of type <b>fr_detector_elevation_t</b>
                     *          The detector's vertex elevation
                     *          will be stored here.
                     *      </p>
                     *   </li>
                     */
                    {
                        typedef fr_detector_elevation_t x_t;
                        x_t*                            x;

                        x = va_arg( ap, x_t* );
                        *x = detector->m_data->GetElevation( );
                    }
                    break;
                case FR_DETECTOR_FIELD_LATITUDE:
                    /**
                     *   <li> <b>FR_DETECTOR_FIELD_LATITUDE</b>
                     *      <p> Retrieve the detector vertex latitude.
                     * 	    A single argument should follow this option.
                     *          The argument needs to be the address
                     *          of type <b>fr_detector_latitude_t</b>
                     *          The detector's vertex latitude
                     *          will be stored here.
                     *      </p>
                     *   </li>
                     */
                    {
                        typedef fr_detector_latitude_t x_t;
                        x_t*                           x;

                        x = va_arg( ap, x_t* );
                        *x = detector->m_data->GetLatitude( );
                    }
                    break;
                case FR_DETECTOR_FIELD_LONGITUDE:
                    /**
                     *   <li> <b>FR_DETECTOR_FIELD_LONGITUDE</b>
                     *      <p> Retrieve the detector vertex longitude.
                     * 	    A single argument should follow this option.
                     *          The argument needs to be the address
                     *          of type <b>fr_detector_longitude_t</b>
                     *          The detector's vertex longitude
                     *          will be stored here.
                     *      </p>
                     *   </li>
                     */
                    {
                        typedef fr_detector_longitude_t x_t;
                        x_t*                            x;

                        x = va_arg( ap, x_t* );
                        *x = detector->m_data->GetLongitude( );
                    }
                    break;
                case FR_DETECTOR_FIELD_NAME:
                    /**
                     *   <li> <b>FR_DETECTOR_FIELD_NAME</b>
                     *      <p> Retrieve the name of the instrument name.
                     * 	    A single argument should follow this option.
                     *          The argument needs to be the address
                     *          of type <b>fr_detector_name_t</b>
                     *          The description
                     *          will be stored here.
                     *          <b>NOTE:</b>
                     *          This is a shallow pointer which is only valid
                     * while the fr_detector_t object exists.
                     *      </p>
                     *   </li>
                     */
                    {
                        typedef fr_detector_name_t x_t;
                        x_t*                       x;

                        x = va_arg( ap, x_t* );
                        *x = detector->m_data->GetName( ).c_str( );
                    }
                    break;
                case FR_DETECTOR_FIELD_PREFIX:
                    /**
                     *   <li> <b>FR_DETECTOR_FIELD_PREFIX</b>
                     *      <p> Retrieve the name of the instrument name.
                     * 	    A single argument should follow this option.
                     *          The argument needs to be the address
                     *          of type <b>fr_detector_prefix_t</b>
                     *          The prefix
                     *          will be stored here.
                     *      </p>
                     *   </li>
                     */
                    {
                        typedef fr_detector_prefix_t x_t;
                        x_t*                         x;

                        x = va_arg( ap, x_t* );
                        x->prefix[ 0 ] = detector->m_data->GetPrefix( )[ 0 ];
                        x->prefix[ 1 ] = detector->m_data->GetPrefix( )[ 1 ];
                    }
                    break;
                default:
                    /**************************************************************
                      An unknown option has been specified. Terminate the
                    request.
                    ***************************************************************/
                    Option = FR_DETECTOR_FIELD_LAST;
                    continue;
                }
                /**
                 * </ul>
                 */
#if 0
	int next_opt = va_arg(ap, int /* fr_frame_fields*/ );
	Option = (fr_detector_fields)next_opt;
#else
                Option = va_arg( ap, int /* fr_frame_fields*/ );
#endif
            }
        }
        else
        {
            Set( Error,
                 FRAMEC_ERRNO_FR_DETECTOR_ACCESSOR_ERROR,
                 "NULL reference to the frame" );
        }
        va_end( ap );
    }
    catch ( const std::exception& Exception )
    {
        Set(
            Error, FRAMEC_ERRNO_FR_DETECTOR_ACCESSOR_ERROR, Exception.what( ) );
    }
    catch ( ... )
    {
        Set( Error,
             FRAMEC_ERRNO_FR_DETECTOR_ACCESSOR_ERROR,
             FrameC::UnknownError );
    }
}

/**
 * This will retrieve information about standard interferometers.
 * The caller is responsible for releasing the handle back to the system.
 * The handle may be modified as the changes are local only to the
 * returned handle.
 *
 */
fr_detector_t*
FrameCGetDetector( FrameCError**    Error,
                   std_detectors    Type )
{
    std::unique_ptr< FrameC::FrDetector > retval_sptr;

    Set( Error );
    try
    {
      auto source = FrameCPP::FrDetector::GetDetector(
            ( FrameCPP::FrDetector::dataQualityOffsets )( Type ) );

        retval_sptr.reset( new FrameC::FrDetector( *source ) );
    }
    catch ( const std::exception& Exception )
    {
        Set( Error, FRAMEC_ERRNO_FR_DETECTOR_ALLOC_ERROR, Exception.what( ) );
        retval_sptr.reset( NULL );
    }
    catch ( ... )
    {
        Set(
            Error, FRAMEC_ERRNO_FR_DETECTOR_ALLOC_ERROR, FrameC::UnknownError );
        retval_sptr.reset( NULL );
    }
    //---------------------------------------------------------------------
    // Report the results
    //---------------------------------------------------------------------
    FrameC::Handle::Deposit( retval_sptr.get( ) );
    return reinterpret_cast< fr_detector_t* >( retval_sptr.release( ) );
} /* Func - FrmaeCGetDetector */
