// ***************************************************************** -*- C++ -*-
/*
 * Copyright (C) 2004-2008 Andreas Huggel <ahuggel@gmx.net>
 *
 * This program is part of the Exiv2 distribution.
 *
 * This program is free software; you can 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
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
 */
/*!
  @file    types.hpp
  @brief   Type definitions for %Exiv2 and related functionality
  @version $Rev: 1366 $
  @author  Andreas Huggel (ahu)
           <a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a>
  @date    09-Jan-04, ahu: created<BR>
           11-Feb-04, ahu: isolated as a component
           31-Jul-04, brad: added Time, Data and String values
 */
#ifndef TYPES_HPP_
#define TYPES_HPP_

// *****************************************************************************
// included header files
#ifdef _MSC_VER
# include "exv_msvc.h"
#else
# include "exv_conf.h"
#endif

#include "version.hpp"

// + standard includes
#include <string>
#include <iosfwd>
#include <utility>
#include <algorithm>
#include <sstream>
#ifdef EXV_HAVE_STDINT_H
# include <stdint.h>
#endif

// MSVC doesn't provide C99 types, but it has MS specific variants
#ifdef _MSC_VER
typedef unsigned __int8  uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
typedef __int16          int16_t;
typedef __int32          int32_t;
#endif

/*!
  @brief Macro to make calls to member functions through a pointer more readable.
         See the C++ FAQ LITE, item
         <a href="http://www.parashift.com/c++-faq-lite/pointers-to-members.html#faq-33.5" title="[33.5] How can I avoid syntax errors when calling a member function using a pointer-to-member-function?">[33.5] How can I avoid syntax errors when calling a member function using a pointer-to-member-function?</a>.
 */
#define EXV_CALL_MEMBER_FN(object,ptrToMember)  ((object).*(ptrToMember))

// *****************************************************************************
// forward declarations
struct tm;

// *****************************************************************************
// namespace extensions
namespace Exiv2 {

// *****************************************************************************
// type definitions

    //! 1 byte unsigned integer type.
    typedef uint8_t byte;

    //! 8 byte unsigned rational type.
    typedef std::pair<uint32_t, uint32_t> URational;
    //! 8 byte signed rational type.
    typedef std::pair<int32_t, int32_t> Rational;

    //! Type to express the byte order (little or big endian)
    enum ByteOrder { invalidByteOrder, littleEndian, bigEndian };

    //! An identifier for each type of metadata
    enum MetadataId { mdExif=1, mdIptc=2, mdComment=4, mdXmp=8 };

    //! An identifier for each mode of metadata support
    enum AccessMode { amNone=0, amRead=1, amWrite=2, amReadWrite=3 };

    //! Type identifiers for IFD format types
    enum TypeId { invalidTypeId, unsignedByte, asciiString, unsignedShort,
                  unsignedLong, unsignedRational, signedByte, undefined,
                  signedShort, signedLong, signedRational,
                  string, date, time,
                  comment,
                  directory,
                  xmpText, xmpAlt, xmpBag, xmpSeq, langAlt,
                  lastTypeId };

    // Todo: decentralize IfdId, so that new ids can be defined elsewhere
    //! Type to specify the IFD to which a metadata belongs
    enum IfdId { ifdIdNotSet,
                 ifd0Id, exifIfdId, gpsIfdId, iopIfdId, ifd1Id,
                 canonIfdId, canonCsIfdId, canonSiIfdId, canonCfIfdId,
                 canonPiIfdId, canonPaIfdId,
                 fujiIfdId,
                 minoltaIfdId, minoltaCs5DIfdId, minoltaCs7DIfdId,
                 minoltaCsOldIfdId, minoltaCsNewIfdId,
                 nikon1IfdId, nikon2IfdId, nikon3IfdId,
                 olympusIfdId, panasonicIfdId, pentaxIfdId, sigmaIfdId, sonyIfdId,
                 lastIfdId };

    //! Type to identify where the data is stored in a directory
    enum DataLocId { invalidDataLocId,
                     valueData, directoryData,
                     lastDataLocId };

// *****************************************************************************
// class definitions

    //! Information pertaining to the defined types
    struct TypeInfoTable {
        //! Constructor
        TypeInfoTable(TypeId typeId, const char* name, long size);
        TypeId typeId_;                         //!< Type id
        const char* name_;                      //!< Name of the type
        long size_;                             //!< Bytes per data entry
    }; // struct TypeInfoTable

    //! Type information lookup functions. Implemented as a static class.
    class TypeInfo {
        //! Prevent construction: not implemented.
        TypeInfo() {}
        //! Prevent copy-construction: not implemented.
        TypeInfo(const TypeInfo& rhs);
        //! Prevent assignment: not implemented.
        TypeInfo& operator=(const TypeInfo& rhs);

    public:
        //! Return the name of the type
        static const char* typeName(TypeId typeId);
        //! Return the type id for a type name
        static TypeId typeId(const std::string& typeName);
        //! Return the size in bytes of one element of this type
        static long typeSize(TypeId typeId);

    private:
        static const TypeInfoTable typeInfoTable_[];
    };

    /*!
      @brief Auxiliary type to enable copies and assignments, similar to
             std::auto_ptr_ref. See http://www.josuttis.com/libbook/auto_ptr.html
             for a discussion.
     */
    struct DataBufRef {
        //! Constructor
        DataBufRef(std::pair<byte*, long> rhs) : p(rhs) {}
        //! Pointer to a byte array and its size
        std::pair<byte*, long> p;
    };

    /*!
      @brief Utility class containing a character array. All it does is to take
             care of memory allocation and deletion. Its primary use is meant to
             be as a stack variable in functions that need a temporary data
             buffer. Todo: this should be some sort of smart pointer,
             essentially an std::auto_ptr for a character array. But it isn't...
     */
    class DataBuf {
    public:
        //! @name Creators
        //@{
        //! Default constructor
        DataBuf() : pData_(0), size_(0) {}
        //! Constructor with an initial buffer size
        explicit DataBuf(long size) : pData_(new byte[size]), size_(size) {}
        //! Constructor, copies an existing buffer
        DataBuf(const byte* pData, long size);
        /*!
          @brief Copy constructor. Transfers the buffer to the newly created
                 object similar to std::auto_ptr, i.e., the original object is
                 modified.
         */
        DataBuf(DataBuf& rhs);
        //! Destructor, deletes the allocated buffer
        ~DataBuf() { delete[] pData_; }
        //@}

        //! @name Manipulators
        //@{
        /*!
          @brief Assignment operator. Transfers the buffer and releases the
                 buffer at the original object similar to std::auto_ptr, i.e.,
                 the original object is modified.
         */
        DataBuf& operator=(DataBuf& rhs);
        //! Allocate a data buffer of the given size
        void alloc(long size);
        /*!
          @brief Release ownership of the buffer to the caller. Returns the
                 buffer as a data pointer and size pair, resets the internal
                 buffer.
         */
        std::pair<byte*, long> release();
        //! Reset value
        void reset(std::pair<byte*, long> =std::make_pair((byte*)(0),long(0)));
        //@}

        /*!
          @name Conversions

          Special conversions with auxiliary type to enable copies
          and assignments, similar to those used for std::auto_ptr.
          See http://www.josuttis.com/libbook/auto_ptr.html for a discussion.
         */
        //@{
        DataBuf(DataBufRef rhs) : pData_(rhs.p.first), size_(rhs.p.second) {}
        DataBuf& operator=(DataBufRef rhs) { reset(rhs.p); return *this; }
        operator DataBufRef() { return DataBufRef(release()); }
        //@}

        // DATA
        //! Pointer to the buffer, 0 if none has been allocated
        byte* pData_;
        //! The current size of the buffer
        long size_;
    }; // class DataBuf


// *****************************************************************************
// free functions

    //! Read a 2 byte unsigned short value from the data buffer
    uint16_t getUShort(const byte* buf, ByteOrder byteOrder);
    //! Read a 4 byte unsigned long value from the data buffer
    uint32_t getULong(const byte* buf, ByteOrder byteOrder);
    //! Read an 8 byte unsigned rational value from the data buffer
    URational getURational(const byte* buf, ByteOrder byteOrder);
    //! Read a 2 byte signed short value from the data buffer
    int16_t getShort(const byte* buf, ByteOrder byteOrder);
    //! Read a 4 byte signed long value from the data buffer
    int32_t getLong(const byte* buf, ByteOrder byteOrder);
    //! Read an 8 byte signed rational value from the data buffer
    Rational getRational(const byte* buf, ByteOrder byteOrder);

    //! Output operator for our fake rational
    std::ostream& operator<<(std::ostream& os, const Rational& r);
    //! Input operator for our fake rational
    std::istream& operator>>(std::istream& is, Rational& r);
    //! Output operator for our fake unsigned rational
    std::ostream& operator<<(std::ostream& os, const URational& r);
    //! Input operator for our fake unsigned rational
    std::istream& operator>>(std::istream& is, URational& r);

    /*!
      @brief Convert an unsigned short to data, write the data to the buffer,
             return number of bytes written.
     */
    long us2Data(byte* buf, uint16_t s, ByteOrder byteOrder);
    /*!
      @brief Convert an unsigned long to data, write the data to the buffer,
             return number of bytes written.
     */
    long ul2Data(byte* buf, uint32_t l, ByteOrder byteOrder);
    /*!
      @brief Convert an unsigned rational to data, write the data to the buffer,
             return number of bytes written.
     */
    long ur2Data(byte* buf, URational l, ByteOrder byteOrder);
    /*!
      @brief Convert a signed short to data, write the data to the buffer,
             return number of bytes written.
     */
    long s2Data(byte* buf, int16_t s, ByteOrder byteOrder);
    /*!
      @brief Convert a signed long to data, write the data to the buffer,
             return number of bytes written.
     */
    long l2Data(byte* buf, int32_t l, ByteOrder byteOrder);
    /*!
      @brief Convert a signed rational to data, write the data to the buffer,
             return number of bytes written.
     */
    long r2Data(byte* buf, Rational l, ByteOrder byteOrder);

    /*!
      @brief Print len bytes from buf in hex and ASCII format to the given
             stream, prefixed with the position in the buffer adjusted by
             offset.
     */
    void hexdump(std::ostream& os, const byte* buf, long len, long offset =0);

    /*!
      @brief Return true if str is a hex number starting with prefix followed
             by size hex digits, false otherwise. If size is 0, any number of
             digits is allowed and all are checked.
     */
    bool isHex(const std::string& str,
               size_t size =0,
               const std::string& prefix ="");

    /*!
      @brief Converts a string in the form "%Y:%m:%d %H:%M:%S", e.g.,
             "2007:05:24 12:31:55" to broken down time format,
             returns 0 if successful, else 1.
     */
    int exifTime(const char* buf, struct tm* tm);

    /*!
      @brief Translate a string using the gettext framework. This wrapper hides
             all the implementation details from the interface.
     */
    const char* exvGettext(const char* str);

// *****************************************************************************
// template and inline definitions

    /*!
      @brief Find an element that matches \em key in the array \em src.

      Designed to be used with lookup tables as shown in the example below.
      Requires a %Key structure (ideally in the array) and a comparison operator
      to compare a key with an array element.  The size of the array is
      determined automagically. Thanks to Stephan Broennimann for this nifty
      implementation.

      @code
      struct Bar {
          int i;
          int k;
          const char* data;

          struct Key;
          bool operator==(const Bar::Key& rhs) const;
      };

      struct Bar::Key {
          Key(int a, int b) : i(a), k(b) {}
          int i;
          int k;
      };

      bool Bar::operator==(const Bar::Key& key) const // definition
      {
          return i == key.i && k == key.k;
      }

      const Bar bars[] = {
          { 1, 1, "bar data 1" },
          { 1, 2, "bar data 2" },
          { 1, 3, "bar data 3" }
      };

      int main ( void ) {
          const Bar* bar = find(bars, Bar::Key(1, 3));
          if (bar) std::cout << bar->data << "\n";
          else std::cout << "Key not found.\n";
          return 0;
      }
      @endcode
    */
    template<typename T, typename K, int N>
    const T* find(T (&src)[N], const K& key)
    {
        const T* rc = std::find(src, src + N, key);
        return rc == src + N ? 0 : rc;
    }

    //! Template used in the COUNTOF macro to determine the size of an array
    template <typename T, int N> char (&sizer(T (&)[N]))[N];
//! Macro to determine the size of an array
#define EXV_COUNTOF(a) (sizeof(sizer(a)))

    //! Utility function to convert the argument of any type to a string
    template<typename T>
    std::string toString(const T& arg)
    {
        std::ostringstream os;
        os << arg;
        return os.str();
    }

    //! Utility function to convert a string to a value of type T.
    template<typename T>
    T stringTo(const std::string& s, bool& ok)
    {
        std::istringstream is(s);
        T tmp;
        ok = is >> tmp ? true : false;
        return tmp;
    }

    /*!
      @brief Return the greatest common denominator of n and m.
             (implementation from Boost rational.hpp)

      @note We use n and m as temporaries in this function, so there is no
            value in using const IntType& as we would only need to make a copy
            anyway...
     */
    template <typename IntType>
    IntType gcd(IntType n, IntType m)
    {
        // Avoid repeated construction
        IntType zero(0);

        // This is abs() - given the existence of broken compilers with Koenig
        // lookup issues and other problems, I code this explicitly. (Remember,
        // IntType may be a user-defined type).
#ifdef _MSC_VER
#pragma warning( disable : 4146 )
#endif
        if (n < zero)
            n = -n;
        if (m < zero)
            m = -m;
#ifdef _MSC_VER
#pragma warning( default : 4146 )
#endif

        // As n and m are now positive, we can be sure that %= returns a
        // positive value (the standard guarantees this for built-in types,
        // and we require it of user-defined types).
        for(;;) {
            if(m == zero)
                return n;
            n %= m;
            if(n == zero)
                return m;
            m %= n;
        }
    }

}                                       // namespace Exiv2

#endif                                  // #ifndef TYPES_HPP_
