//
// LDASTools frameCPP - A library implementing the LIGO/Virgo frame specification
//
// Copyright (C) 2018 California Institute of Technology
//
// LDASTools frameCPP is free software; you may 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 (GPLv2) of the
// License or at your discretion, any later version.
//
// LDASTools frameCPP is distributed in the hope that it will be useful, but
// without any warranty or even the implied warranty of merchantability
// or fitness for a particular purpose. See the GNU General Public
// License (GPLv2) for more details.
//
// Neither the names of the California Institute of Technology (Caltech),
// The Massachusetts Institute of Technology (M.I.T), The Laser
// Interferometer Gravitational-Wave Observatory (LIGO), nor the names
// of its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// You should have received a copy of the licensing terms for this
// software included in the file LICENSE located in the top-level
// directory of this package. If you did not, you can view a copy at
// http://dcc.ligo.org/M1500244/LICENSE
//

template< class T >
std::vector< T >
vect( void* Data, INT_4U Size )
{
  T* data( reinterpret_cast< T* >( Data ) );
  std::vector< T > ret( Size );
  std::copy( &data[0], &data[ Size ], ret.begin( ) );
  return ret;
}

class dump_label
{
public:
  dump_label(	std::ostream& Stream,
		const std::string& Padding,
		const int Width,
		const bool Scientific )
    : m_stream( Stream ),
      m_padding( Padding ),
      m_scientific( Scientific ),
      m_width( Width )
	
  {
  }

  template< class T >
  void operator()( const std::string& Label , const T& Data )
  {
    int	size( m_width - Label.length( ) );

    m_stream << m_padding
	     << Label << ":";
    while ( size-- > 0 )
    {
      m_stream << " ";
    }
    m_stream << Data << std::endl;
  }

  template< class T >
  void operator()( const std::string& Label , const std::vector< T >& Data )
  {
    int	size( m_width - Label.length( ) );

    m_stream << m_padding
	     << Label << ":";
    while ( size-- > 0 )
    {
      m_stream << " ";
    }
    print_vect( Data );
    m_stream << std::endl;
  }

private:
  std::ostream&	m_stream;
  const std::string&	m_padding;
  const bool		m_scientific;
  const int		m_width;

  template< class T >
  void print_vect( const std::vector< T >& Data );
};

template<>
void dump_label::operator()( const std::string& Label,
			     const FrameCPP::Universal::PTR_STRUCT& Data )
{
  int size( m_width - Label.length( ) );

  m_stream << m_padding
	   << Label << ":";
  while ( size-- > 0 )
  {
    m_stream << " ";
  }
  m_stream << Data.ClassRaw( ) << "-" << Data.InstanceRaw( ) << std::endl;
}

template<>
void dump_label::operator()( const std::string& Label,
			     const FrameCPP::Universal::STRING& Data )
{
  int size( m_width - Label.length( ) );

  m_stream << m_padding
	   << Label << ":";
  while ( size-- > 0 )
  {
    m_stream << " ";
  }
  m_stream << Data.c_str( ) << std::endl;
}


template<>
void dump_label::print_vect( const std::vector< FrameCPP::Universal::STRING >& Data )
{
  for( std::vector< FrameCPP::Universal::STRING >::const_iterator
	 d( Data.begin( ) );
       d != Data.end( );
       ++d )
  {
    if ( d != Data.begin( ) )
    {
      m_stream << " ";
    }
    m_stream << ( const std::string )( *d );
  }
}


template< >
void dump_label::
print_vect( const std::vector< CHAR >& Data )
{
  for ( std::vector< CHAR >::const_iterator d( Data.begin( ) );
	d != Data.end( );
	d++ )
  {
    if ( d != Data.begin( ) )
    {
      m_stream << " ";
    }
    m_stream << ( INT_2S)( *d );
  }

  return;
}

template< >
void dump_label::
print_vect( const std::vector< CHAR_U >& Data )
{
  for ( std::vector< CHAR_U >::const_iterator d( Data.begin( ) );
	d != Data.end( );
	d++ )
  {
    if ( d != Data.begin( ) )
    {
      m_stream << " ";
    }
    m_stream << ( INT_2U)( *d );
  }
}

template< >
void dump_label::
print_vect( const std::vector< REAL_4 >& Data )
{
  std::ios_base::fmtflags
    f( m_stream.flags( ) );	// Save flags
  static const std::streamsize
    prec = REAL_4_DIGITS + 1;

  if ( m_scientific )
  {
    m_stream.precision( prec );
    m_stream.setf(std::ios::scientific, std::ios::floatfield);
  }
  for ( std::vector< REAL_4 >::const_iterator d( Data.begin( ) );
	d != Data.end( );
	d++ )
  {
    if ( d != Data.begin( ) )
    {
      m_stream << " ";
    }
    m_stream << *d;
  }
  m_stream.flags( f ); // Restore flags
}

template< >
void dump_label::
print_vect( const std::vector< REAL_8 >& Data )
{
  std::ios_base::fmtflags
    f( m_stream.flags( ) );	// Save flags
  static const std::streamsize
    prec = REAL_8_DIGITS + 1;

  if ( m_scientific )
  {
    m_stream.precision( prec );
    m_stream.setf(std::ios::scientific, std::ios::floatfield);
  }
  for ( std::vector< REAL_8 >::const_iterator d( Data.begin( ) );
	d != Data.end( );
	d++ )
  {
    if ( d != Data.begin( ) )
    {
      m_stream << " ";
    }
    m_stream << *d;
  }
  m_stream.flags( f ); // Restore flags
}

template< >
void dump_label::
print_vect( const std::vector< COMPLEX_8 >& Data )
{
  std::ios_base::fmtflags
    f( m_stream.flags( ) );	// Save flags
  static const std::streamsize
    prec = REAL_4_DIGITS + 1;

  if ( m_scientific )
  {
    m_stream.precision( prec );
    m_stream.setf(std::ios::scientific, std::ios::floatfield);
  }
  for ( std::vector< COMPLEX_8 >::const_iterator d( Data.begin( ) );
	d != Data.end( );
	d++ )
  {
    if ( d != Data.begin( ) )
    {
      m_stream << " ";
    }
    m_stream << ( *d ).real( ) << "+i" << (*d).imag( );
  }
  m_stream.flags( f ); // Restore flags
}

template< >
void dump_label::
print_vect( const std::vector< COMPLEX_16 >& Data )
{
  std::ios_base::fmtflags
    f( m_stream.flags( ) );	// Save flags
  static const std::streamsize
    prec = REAL_8_DIGITS + 1;

  if ( m_scientific )
  {
    m_stream.precision( prec );
    m_stream.setf(std::ios::scientific, std::ios::floatfield);
  }
  for ( std::vector< COMPLEX_16 >::const_iterator d( Data.begin( ) );
	d != Data.end( );
	d++ )
  {
    if ( d != Data.begin( ) )
    {
      m_stream << " ";
    }
    m_stream << ( *d ).real( ) << "+i" << (*d).imag( );
  }
  m_stream.flags( f ); // Restore flags
}

template< class T >
void dump_label::
print_vect( const std::vector< T >& Data )
{
  for ( typename std::vector< T >::const_iterator d( Data.begin( ) );
	d != Data.end( );
	d++ )
  {
    if ( d != Data.begin( ) )
    {
      m_stream << " ";
    }
    m_stream << *d;
  }
}

class DumpFrStruct
{
public:

  enum OutputMode_type {
    OUTPUT_MODE_DUMP_OBJECTS,
    OUTPUT_MODE_DUMP
  };
      
  DumpFrStruct( bool VerboseDataMode,
		bool ScientificDataMode,
		OutputMode_type OutputMode )
    : m_output_mode( OutputMode ),
      m_scientific_data_mode( ScientificDataMode ),
      m_verbose_data_mode( VerboseDataMode )
  {
  }

  DumpFrStruct( const DumpFrStruct& Source )
    : m_output_mode( Source.m_output_mode ),
      m_pad( Source.m_pad ),
      m_scientific_data_mode( Source.m_scientific_data_mode ),
      m_verbose_data_mode( Source.m_verbose_data_mode )
  {
  }

  virtual ~DumpFrStruct( );

  virtual void Dump( const Object& Base ) = 0;

  void SetVerbosity( bool Verbosity )
  {
    m_verbose_data_mode = Verbosity;
  }

  void SetScientific( bool Scientific )
  {
    m_scientific_data_mode = Scientific;
  }

protected:
  static const std::string	m_spaces;

  OutputMode_type		m_output_mode;
  std::string			m_pad;
  bool				m_scientific_data_mode;
  bool				m_verbose_data_mode;

  void header( const std::string& tag );

  void trailer( );

  inline void pad_inc( )
  {
    m_pad += m_spaces;
  }

  inline void pad_dec( )
  {
    m_pad.resize( m_pad.length( ) - m_spaces.length( ) );
  }

  template< class T >
  void dump_container( const std::string& Name, const T& Container );
      
};

DumpFrStruct::
~DumpFrStruct( )
{
}

template< >
void DumpFrStruct::
dump_container( const std::string& Name, const std::vector< int >& Container )
{
  std::cout << m_pad << Name << ":" << std::endl;
  std::cout << m_pad << "{" << std::endl;
  std::cout << m_pad << "}" << std::endl;
}

template< class T >
void DumpFrStruct::
dump_container( const std::string& Name, const T& Container )
{
  if ( Container.size( ) == 0 )
  {
    return;
  }
  std::cout << m_pad << Name << ":" << std::endl;
  std::cout << m_pad << "{" << std::endl;
  pad_inc( );
  for ( INT_4U x = 0; x < Container.size( ); x++ )
  {
    Dump( *( Container[ x ] ) );
  }
  pad_dec( );
  std::cout << m_pad << "}" << std::endl;
}

void DumpFrStruct::
header( const std::string& tag )
{
  switch( m_output_mode )
  {
  case OUTPUT_MODE_DUMP:
    std::cout << m_pad << tag << std::endl;
    std::cout << m_pad << "{" << std::endl;
    pad_inc( );
    break;
  case OUTPUT_MODE_DUMP_OBJECTS:
    pad_inc( );
    break;
  }
}

void DumpFrStruct::
trailer( )
{
  switch( m_output_mode )
  {
  case OUTPUT_MODE_DUMP:
    pad_dec( );
    std::cout << m_pad << "}" << std::endl;
    break;
  case OUTPUT_MODE_DUMP_OBJECTS:
    pad_dec( );
    break;
  }
}
const std::string DumpFrStruct::m_spaces( "  " );
