/* /////////////////////////////////////////////////////////////////////////////
 * File:        comstl_safearray_sequence.h (originally MOEnSeq.h, ::SynesisCom)
 *
 * Purpose:     STL sequence for COM collection interfaces.
 *
 * Created:     17th April 2004
 * Updated:     17th April 2004
 *
 * Author:      Matthew Wilson, Synesis Software Pty Ltd.
 *
 * License:     (Licensed under the Synesis Software Standard Source License)
 *
 *              Copyright (C) 2002-2003, Synesis Software Pty Ltd.
 *
 *              All rights reserved.
 *
 *              www:        http://www.synesis.com.au/comstl
 *                          http://www.comstl.org/
 *
 *              email:      submissions@comstl.org  for submissions
 *                          admin@comstl.org        for other enquiries
 *
 *              Redistribution and use in source and binary forms, with or
 *              without modification, are permitted provided that the following
 *              conditions are met:
 *
 *              (i) Redistributions of source code must retain the above
 *              copyright notice and contact information, this list of
 *              conditions and the following disclaimer.
 *
 *              (ii) Any derived versions of this software (howsoever modified)
 *              remain the sole property of Synesis Software.
 *
 *              (iii) Any derived versions of this software (howsoever modified)
 *              remain subject to all these conditions.
 *
 *              (iv) Neither the name of Synesis Software nor the names of any
 *              subdivisions, employees or agents of Synesis Software, nor the
 *              names of any other contributors to this software may be used to
 *              endorse or promote products derived from this software without
 *              specific prior written permission.
 *
 *              This source code is provided by Synesis Software "as is" and any
 *              warranties, whether expressed or implied, including, but not
 *              limited to, the implied warranties of merchantability and
 *              fitness for a particular purpose are disclaimed. In no event
 *              shall the Synesis Software be liable for any direct, indirect,
 *              incidental, special, exemplary, or consequential damages
 *              (including, but not limited to, procurement of substitute goods
 *              or services; loss of use, data, or profits; or business
 *              interruption) however caused and on any theory of liability,
 *              whether in contract, strict liability, or tort (including
 *              negligence or otherwise) arising in any way out of the use of
 *              this software, even if advised of the possibility of such
 *              damage.
 *
 * ////////////////////////////////////////////////////////////////////////// */


#ifndef COMSTL_INCL_H_COMSTL_SAFEARRAY_SEQUENCE
#define COMSTL_INCL_H_COMSTL_SAFEARRAY_SEQUENCE

#ifndef __STLSOFT_DOCUMENTATION_SKIP_SECTION
# define _COMSTL_VER_H_COMSTL_SAFEARRAY_SEQUENCE_MAJOR      1
# define _COMSTL_VER_H_COMSTL_SAFEARRAY_SEQUENCE_MINOR      0
# define _COMSTL_VER_H_COMSTL_SAFEARRAY_SEQUENCE_REVISION   2
# define _COMSTL_VER_H_COMSTL_SAFEARRAY_SEQUENCE_EDIT       2
#endif /* !__STLSOFT_DOCUMENTATION_SKIP_SECTION */

/* ////////////////////////////////////////////////////////////////////////////
 * Includes
 */

#ifndef COMSTL_INCL_H_COMSTL
# include "comstl.h"                    // Include the COMSTL root header
#endif /* !COMSTL_INCL_H_COMSTL */
#if 0
    #ifndef COMSTL_INCL_H_COMSTL_REFCOUNT_FUNCTIONS
    # include "comstl_refcount_functions.h" // safe_release, release_set_null
    #endif /* !COMSTL_INCL_H_COMSTL_REFCOUNT_FUNCTIONS */
    #ifndef COMSTL_INCL_H_COMSTL_ENUMERATOR_POLICIES
    # include "comstl_enumerator_policies.h" // input_cloning_policy
    #endif /* !COMSTL_INCL_H_COMSTL_ENUMERATOR_POLICIES */
    #ifndef COMSTL_INCL_H_COMSTL_INTERFACE_TRAITS
    # include "comstl_interface_traits.h"
    #endif /* !COMSTL_INCL_H_COMSTL_INTERFACE_TRAITS */
#endif /* 0 */
#ifndef STLSOFT_INCL_H_STLSOFT_ITERATOR
# include "stlsoft_iterator.h"
#endif /* !STLSOFT_INCL_H_STLSOFT_ITERATOR */
#include <algorithm>

/* /////////////////////////////////////////////////////////////////////////////
 * Namespace
 *
 * The COMSTL components are contained within the comstl namespace. This is
 * actually an alias for stlsoft::comstl_project,
 *
 * The definition matrix is as follows:
 *
 * _STLSOFT_NO_NAMESPACE    _COMSTL_NO_NAMESPACE    comstl definition
 * ---------------------    --------------------    -----------------
 *  not defined              not defined             = stlsoft::comstl_project
 *  not defined              defined                 not defined
 *  defined                  not defined             comstl
 *  defined                  defined                 not defined
 *
 */

#ifndef _COMSTL_NO_NAMESPACE
# ifdef _STLSOFT_NO_NAMESPACE
/* There is no stlsoft namespace, so must define ::comstl */
namespace comstl
{
# else
/* Define stlsoft::comstl_project */

namespace stlsoft
{

namespace comstl_project
{

# endif /* _STLSOFT_NO_NAMESPACE */
#endif /* !_COMSTL_NO_NAMESPACE */

/* ////////////////////////////////////////////////////////////////////////// */

/// \weakgroup comstl_automation_library Automation Library
/// \ingroup COMSTL libraries
/// \brief This library provides facilities for manipulating OLE Automation types
/// @{

/* /////////////////////////////////////////////////////////////////////////////
 * Classes
 */

class variant_type_exception
    : public std::exception
{};

template <ss_typename_param_k T>
class safearray_sequence
{
private:
    typedef SAFEARRAY const                                                 *LPCSAFEARRAY;
public:
    typedef T                                                               value_type;
    typedef size_t                                                          size_type;
    typedef ptrdiff_t                                                       difference_type;
    typedef value_type                                                      &reference;
    typedef value_type const                                                &const_reference;
    typedef value_type                                                      *pointer;
    typedef value_type const                                                *const_pointer;
    typedef value_type                                                      *iterator;
    typedef value_type const                                                *const_iterator;
    /// The non-mutating (const) reverse iterator type
#if defined(__STLSOFT_CF_BIDIRECTIONAL_ITERATOR_SUPPORT)
    typedef stlsoft_ns_qual(reverse_iterator_base)      <   iterator
                                                        ,   value_type
                                                        ,   reference
                                                        ,   pointer
                                                        ,   difference_type
                                                        >                   reverse_iterator;

    typedef stlsoft_ns_qual(const_reverse_iterator_base)<   const_iterator
                                                        ,   value_type const
                                                        ,   const_reference
                                                        ,   const_pointer
                                                        ,   difference_type
                                                        >                   const_reverse_iterator;
#endif /* __STLSOFT_CF_BIDIRECTIONAL_ITERATOR_SUPPORT */

public:
    ss_explicit_k safearray_sequence(LPCSAFEARRAY array); // throw variant_type_exception

public:
    /// Begins the iteration
    ///
    /// \return An iterator representing the start of the sequence
    const_iterator          begin() const;
    /// Ends the iteration
    ///
    /// \return An iterator representing the end of the sequence
    const_iterator          end() const;
#if defined(__STLSOFT_CF_BIDIRECTIONAL_ITERATOR_SUPPORT)
    /// Begins the reverse iteration
    ///
    /// \return An iterator representing the start of the reverse sequence
    const_reverse_iterator  rbegin() const;
    /// Ends the reverse iteration
    ///
    /// \return An iterator representing the end of the reverse sequence
    const_reverse_iterator  rend() const;
#endif /* __STLSOFT_CF_BIDIRECTIONAL_ITERATOR_SUPPORT */

    /// Begins the iteration
    ///
    /// \return An iterator representing the start of the sequence
    iterator                begin();
    /// Ends the iteration
    ///
    /// \return An iterator representing the end of the sequence
    iterator                end();
#if defined(__STLSOFT_CF_BIDIRECTIONAL_ITERATOR_SUPPORT)
    /// Begins the reverse iteration
    ///
    /// \return An iterator representing the start of the reverse sequence
    reverse_iterator        rbegin();
    /// Ends the reverse iteration
    ///
    /// \return An iterator representing the end of the reverse sequence
    reverse_iterator        rend();
#endif /* __STLSOFT_CF_BIDIRECTIONAL_ITERATOR_SUPPORT */

public:
    size_type       size() const;
    bool            empty() const;

public:
//  void            **access_data();    // Should RAII this
//  void            unaccess_data();

private:
    static bool     type_is_compatible_(LPCSAFEARRAY array);
    static DWORD    calc_size_(LPCSAFEARRAY array);

private:
    LPCSAFEARRAY    m_sa;
    DWORD const     m_cItems;
};

////////////////////////////////////////////////////////////////////////////////
// Unit-testing

#ifdef STLSOFT_UNITTEST

namespace unittest
{
    ss_bool_t test_comstl_safearray_sequence(unittest_reporter *r)
    {
        ss_bool_t   bSuccess    =   true;

        r->set_project("COMSTL");
        r->set_file(__FILE__);
        r->set_component("safearray_sequence");

        /* Create a safe-array. */
        SAFEARRAYBOUND  bounds[2] = 
        {
                {   10,     0   }
            ,   {   5,      0   }
        };
        LPSAFEARRAY     psa =   ::SafeArrayCreate(VT_I4, stlsoft_num_elements(bounds), bounds);
        void            *pv;
        HRESULT         hr  =   ::SafeArrayAccessData(psa, &pv);

        long            *pl =   static_cast<long*>(pv);
        size_t          l;
        long            total;

        for(l = 0, total = 0; l < bounds[0].cElements * bounds[1].cElements; ++l)
        {
            pl[l] = l;
            total += l;
        }
        ::SafeArrayUnaccessData(psa);

        comstl::safearray_sequence<long>    array(psa);

        if(std::accumulate(array.begin(), array.end(), static_cast<long>(0)) != total)
        {
            r->report("array contents (forward iteration) test failed ", __LINE__);
            bSuccess = false;
        }

        if(std::accumulate(array.rbegin(), array.rend(), static_cast<long>(0)) != total)
        {
            r->report("array contents (reverse iteration) test failed ", __LINE__);
            bSuccess = false;
        }

        ::SafeArrayDestroy(psa);

        return bSuccess;
    }

    unittest_initialiser    unittest_comstl_safearray_sequence(test_comstl_safearray_sequence);

} // namespace unittest

#endif /* STLSOFT_UNITTEST */

////////////////////////////////////////////////////////////////////////////////
// Implementation

template <ss_typename_param_k T>
inline /* static */ bool safearray_sequence<T>::type_is_compatible_(LPCSAFEARRAY array)
{
    return sizeof(value_type) == array->cbElements;
}

template <ss_typename_param_k T>
inline /* static */ DWORD safearray_sequence<T>::calc_size_(LPCSAFEARRAY array)
{
    DWORD   cItems  =   1;

    for(USHORT dim = 0; dim < array->cDims; ++dim)
    {
        cItems *= array->rgsabound[dim].cElements;
    }

    return cItems;
}

template <ss_typename_param_k T>
inline safearray_sequence<T>::safearray_sequence(LPCSAFEARRAY array) // throw variant_type_exception
    : m_sa(array)
    , m_cItems(calc_size_(array))
{
    if(!type_is_compatible_(array))
    {
        throw variant_type_exception();
    }
}

template <ss_typename_param_k T>
inline ss_typename_type_k safearray_sequence<T>::const_iterator safearray_sequence<T>::begin() const
{
    return static_cast<pointer>(m_sa->pvData);
}

template <ss_typename_param_k T>
inline ss_typename_type_k safearray_sequence<T>::const_iterator safearray_sequence<T>::end() const
{
    return static_cast<pointer>(m_sa->pvData) + size();
}

#if defined(__STLSOFT_CF_BIDIRECTIONAL_ITERATOR_SUPPORT)
template <ss_typename_param_k T>
inline ss_typename_type_k safearray_sequence<T>::const_reverse_iterator safearray_sequence<T>::rbegin() const
{
    return const_reverse_iterator(end());
}

template <ss_typename_param_k T>
inline ss_typename_type_k safearray_sequence<T>::const_reverse_iterator safearray_sequence<T>::rend() const
{
    return const_reverse_iterator(begin());
}
#endif /* __STLSOFT_CF_BIDIRECTIONAL_ITERATOR_SUPPORT */

template <ss_typename_param_k T>
inline ss_typename_type_k safearray_sequence<T>::iterator safearray_sequence<T>::begin()
{
    return static_cast<pointer>(m_sa->pvData);
}

template <ss_typename_param_k T>
inline ss_typename_type_k safearray_sequence<T>::iterator safearray_sequence<T>::end()
{
    return static_cast<pointer>(m_sa->pvData) + size();
}

#if defined(__STLSOFT_CF_BIDIRECTIONAL_ITERATOR_SUPPORT)
template <ss_typename_param_k T>
inline ss_typename_type_k safearray_sequence<T>::reverse_iterator safearray_sequence<T>::rbegin()
{
    return reverse_iterator(end());
}

template <ss_typename_param_k T>
inline ss_typename_type_k safearray_sequence<T>::reverse_iterator safearray_sequence<T>::rend()
{
    return reverse_iterator(begin());
}
#endif /* __STLSOFT_CF_BIDIRECTIONAL_ITERATOR_SUPPORT */

template <ss_typename_param_k T>
inline ss_typename_type_k safearray_sequence<T>::size_type safearray_sequence<T>::size() const
{
    return m_cItems;
}

template <ss_typename_param_k T>
inline bool safearray_sequence<T>::empty() const
{
    return 0 != size();
}

/* ////////////////////////////////////////////////////////////////////////// */

/// @} // end of group comstl_automation_library

/* ////////////////////////////////////////////////////////////////////////// */

#ifndef _COMSTL_NO_NAMESPACE
# ifdef _STLSOFT_NO_NAMESPACE
} // namespace comstl
# else
} // namespace stlsoft::comstl_project
} // namespace stlsoft
# endif /* _STLSOFT_NO_NAMESPACE */
#endif /* !_COMSTL_NO_NAMESPACE */

/* ////////////////////////////////////////////////////////////////////////// */

#endif /* !COMSTL_INCL_H_COMSTL_SAFEARRAY_SEQUENCE */

/* ////////////////////////////////////////////////////////////////////////// */
