/*
 * Original work Copyright 2009 - 2010 Kevin Ackley (kackley@gwi.net)
 * Modified work Copyright 2018 - 2020 Andy Maloney <asmaloney@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person or organization
 * obtaining a copy of the software and accompanying documentation covered by
 * this license (the "Software") to use, reproduce, display, distribute,
 * execute, and transmit the Software, and to prepare derivative works of the
 * Software, and to permit third-parties to whom the Software is furnished to
 * do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer,
 * must be included in all copies of the Software, in whole or in part, and
 * all derivative works of the Software, unless such copies or derivative
 * works are solely in the form of machine-executable object code generated by
 * a source language processor.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#pragma once

#include <iomanip>
#include <iostream>
#include <set>
#include <sstream>
#include <string>
#include <vector>

// Define the following symbol adds some functions to the API for implementation
// purposes. These functions are not available to a normal API user.
#define E57_INTERNAL_IMPLEMENTATION_ENABLE 1
#include "E57Format.h"

// Uncomment the lines below to enable various levels of cross checking and
// verification in the code. The extra code does not change the file contents.
// Recommend that E57_DEBUG remain defined even for production versions.
#define E57_DEBUG 1
//#define E57_MAX_DEBUG   1

// Uncomment the lines below to enable various levels of printing to the console
// of what is going on in the code.
//#define E57_VERBOSE     1
//#define E57_MAX_VERBOSE 1

// Uncomment the line below to enable writing packets that are correct but will
// stress the reader.
//#define E57_WRITE_CRAZY_PACKET_MODE 1

#ifdef _MSC_VER
// Disable MSVC warning: warning C4224: nonstandard extension used : formal
// parameter 'locale' was previously defined as a type
#pragma warning( disable : 4224 )
#endif

namespace e57
{
//!!! inline these rather than macros?
#define E57_EXCEPTION1( ecode )                                                                                        \
   ( E57Exception( ( ecode ), ustring(), __FILE__, __LINE__, static_cast<const char *>( __FUNCTION__ ) ) )
#define E57_EXCEPTION2( ecode, context )                                                                               \
   ( E57Exception( ( ecode ), ( context ), __FILE__, __LINE__, static_cast<const char *>( __FUNCTION__ ) ) )

   /// Create whitespace of given length, for indenting printouts in dump()
   /// functions
   inline std::string space( size_t n )
   {
      return ( std::string( n, ' ' ) );
   }

   /// Convert number to decimal, hexadecimal, and binary strings  (Note hex
   /// strings don't have leading zeros).
   template <class T> std::string toString( T x )
   {
      std::ostringstream ss;
      ss << x;
      return ( ss.str() );
   }

   inline std::string hexString( uint64_t x )
   {
      std::ostringstream ss;
      ss << "0x" << std::hex << std::setw( 16 ) << std::setfill( '0' ) << x;
      return ( ss.str() );
   }
   inline std::string hexString( uint32_t x )
   {
      std::ostringstream ss;
      ss << "0x" << std::hex << std::setw( 8 ) << std::setfill( '0' ) << x;
      return ( ss.str() );
   }
   inline std::string hexString( uint16_t x )
   {
      std::ostringstream ss;
      ss << "0x" << std::hex << std::setw( 4 ) << std::setfill( '0' ) << x;
      return ( ss.str() );
   }
   inline std::string hexString( uint8_t x )
   {
      std::ostringstream ss;
      ss << "0x" << std::hex << std::setw( 2 ) << std::setfill( '0' ) << static_cast<unsigned>( x );
      return ( ss.str() );
   }
   inline std::string binaryString( uint64_t x )
   {
      std::ostringstream ss;
      for ( int i = 63; i >= 0; i-- )
      {
         ss << ( ( x & ( 1LL << i ) ) ? 1 : 0 );
         if ( i > 0 && i % 8 == 0 )
            ss << " ";
      }
      return ( ss.str() );
   }
   inline std::string binaryString( uint32_t x )
   {
      std::ostringstream ss;
      for ( int i = 31; i >= 0; i-- )
      {
         ss << ( ( x & ( 1LL << i ) ) ? 1 : 0 );
         if ( i > 0 && i % 8 == 0 )
            ss << " ";
      }
      return ( ss.str() );
   }
   inline std::string binaryString( uint16_t x )
   {
      std::ostringstream ss;
      for ( int i = 15; i >= 0; i-- )
      {
         ss << ( ( x & ( 1LL << i ) ) ? 1 : 0 );
         if ( i > 0 && i % 8 == 0 )
            ss << " ";
      }
      return ( ss.str() );
   }
   inline std::string binaryString( uint8_t x )
   {
      std::ostringstream ss;
      for ( int i = 7; i >= 0; i-- )
      {
         ss << ( ( x & ( 1LL << i ) ) ? 1 : 0 );
         if ( i > 0 && i % 8 == 0 )
            ss << " ";
      }
      return ( ss.str() );
   }
   inline std::string hexString( int64_t x )
   {
      return ( hexString( static_cast<uint64_t>( x ) ) );
   }
   inline std::string hexString( int32_t x )
   {
      return ( hexString( static_cast<uint32_t>( x ) ) );
   }
   inline std::string hexString( int16_t x )
   {
      return ( hexString( static_cast<uint16_t>( x ) ) );
   }
   inline std::string hexString( int8_t x )
   {
      return ( hexString( static_cast<uint8_t>( x ) ) );
   }
   inline std::string binaryString( int64_t x )
   {
      return ( binaryString( static_cast<uint64_t>( x ) ) );
   }
   inline std::string binaryString( int32_t x )
   {
      return ( binaryString( static_cast<uint32_t>( x ) ) );
   }
   inline std::string binaryString( int16_t x )
   {
      return ( binaryString( static_cast<uint16_t>( x ) ) );
   }
   inline std::string binaryString( int8_t x )
   {
      return ( binaryString( static_cast<uint8_t>( x ) ) );
   }

   using ImageFileImplSharedPtr = std::shared_ptr<class ImageFileImpl>;
   using ImageFileImplWeakPtr = std::weak_ptr<class ImageFileImpl>;
   using NodeImplSharedPtr = std::shared_ptr<class NodeImpl>;
   using NodeImplWeakPtr = std::weak_ptr<class NodeImpl>;

   using StringList = std::vector<std::string>;
   using StringSet = std::set<std::string>;

   //! generates a new random GUID
   std::string generateRandomGUID();
}
