// Copyright (C) 2006  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.
#ifndef DLIB_BYTE_ORDEREr_KERNEL_1_ 
#define DLIB_BYTE_ORDEREr_KERNEL_1_ 
#include "byte_orderer_kernel_abstract.h"
#include "../algs.h"
#include "../assert.h"
namespace dlib
{
    class byte_orderer 
    {
        /*!
            INITIAL VALUE
                - if (this machine is little endian) then
                    - little_endian == true
                - else
                    - little_endian == false
            CONVENTION
                - host_is_big_endian() == !little_endian
                - host_is_little_endian() == little_endian
                - if (this machine is little endian) then
                    - little_endian == true
                - else
                    - little_endian == false
        !*/
    public:
        // this is here for backwards compatibility with older versions of dlib.
        typedef byte_orderer kernel_1a;
        byte_orderer (        
        )
        {
            // This will probably never be false but if it is then it means chars are not 8bits
            // on this system.  Which is a problem for this object.
            COMPILE_TIME_ASSERT(sizeof(short) >= 2);
            unsigned long temp = 1;
            unsigned char* ptr = reinterpret_cast<unsigned char*>(&temp);
            if (*ptr == 1)
                little_endian = true;
            else
                little_endian = false;
        }
        virtual ~byte_orderer (
        ){}
        bool host_is_big_endian (
        ) const { return !little_endian; }
        bool host_is_little_endian (
        ) const { return little_endian; }
        template <
            typename T
            >
        inline void host_to_network (
            T& item
        ) const
        { if (little_endian) flip(item); }
        template <
            typename T
            >
        inline void network_to_host (
            T& item
        ) const { if (little_endian) flip(item); }
        template <
            typename T
            >
        void host_to_big (
            T& item
        ) const { if (little_endian) flip(item); }
        template <
            typename T
            >
        void big_to_host (
            T& item
        ) const { if (little_endian) flip(item); }
        template <
            typename T
            >
        void host_to_little (
            T& item
        ) const { if (!little_endian) flip(item); }
        template <
            typename T
            >
        void little_to_host (
            T& item
        ) const { if (!little_endian) flip(item); }
    private:
        template <
            typename T,
            size_t size
            >
        inline void flip (
            T (&array)[size]
        ) const
        /*!
            ensures
                - flips the bytes in every element of this array
        !*/
        {
            for (size_t i = 0; i < size; ++i)
            {
                flip(array[i]);
            }
        }
        template <
            typename T
            >
        inline void flip (
            T& item
        ) const
        /*!
            ensures
                - reverses the byte ordering in item
        !*/
        {
            DLIB_ASSERT_HAS_STANDARD_LAYOUT(T);
            T value;
            // If you are getting this as an error then you are probably using
            // this object wrong.  If you think you aren't then send me (Davis) an
            // email and I'll either set you straight or change/remove this check so
            // your stuff works :)
            COMPILE_TIME_ASSERT(sizeof(T) <= sizeof(long double));
            // If you are getting a compile error on this line then it means T is
            // a pointer type.  It doesn't make any sense to byte swap pointers
            // since they have no meaning outside the context of their own process.
            // So you probably just forgot to dereference that pointer before passing
            // it to this function  :)
            COMPILE_TIME_ASSERT(is_pointer_type<T>::value == false);
            const size_t size = sizeof(T);
            unsigned char* const ptr = reinterpret_cast<unsigned char*>(&item);
            unsigned char* const ptr_temp = reinterpret_cast<unsigned char*>(&value);
            for (size_t i = 0; i < size; ++i)
                ptr_temp[size-i-1] = ptr[i];
            item = value;
        }
        bool little_endian;
    };    
    // make flip not do anything at all for chars
    template <> inline void byte_orderer::flip<char> ( char& ) const {} 
    template <> inline void byte_orderer::flip<unsigned char> ( unsigned char& ) const {} 
    template <> inline void byte_orderer::flip<signed char> ( signed char& ) const {} 
}
#endif // DLIB_BYTE_ORDEREr_KERNEL_1_