#! /bin/bash

function usage() {
   echo $@ >&2
   echo <<EOF >&2
Usage: genclass [path/]stem
EOF
   exit 1
}

test -z "$1" && usage "Missing name!"

TOPSRCDIR=${XTOPSRCDIR:-$(cd /home/abuild/rpmbuild/BUILD/libzypp-17.25.10 && pwd)}
test -z "$TOPSRCDIR" && {
   echo "Dir does not exist '$TOPSRCDIR'" >&2
   exit 1
}

OUTDIR=$(dirname $1)
STEM=$(basename $1)
STEMDIR=$( cd $OUTDIR && pwd )
test -z "$STEMDIR" && {
   echo "Dir does not exist '$(dirname $1)'" >&2
   exit 1
}
STEMDIR=${STEMDIR#$TOPSRCDIR/}

CLASS=$STEM
CLASS_H=$STEMDIR/$STEM.h
CLASS_CC=$STEMDIR/$STEM.cc

OUT_CLASS_H=$OUTDIR/$STEM.h
OUT_CLASS_CC=$OUTDIR/$STEM.cc
test -e $OUT_CLASS_H -o -e $OUT_CLASS_CC && {
   test -e $OUT_CLASS_H && echo "File exists '$OUT_CLASS_H' using '$OUT_CLASS_H.new'" >&2
   test -e $OUT_CLASS_CC && echo "File exists '$OUT_CLASS_CC' using '$OUT_CLASS_CC.new'" >&2
   OUT_CLASS_H="$OUT_CLASS_H.new"
   OUT_CLASS_CC="$OUT_CLASS_CC.new"
}

INCLUDE_H=$CLASS_H
INCLUDE_DEF=$(echo $INCLUDE_H | sed 's/[./]/_/g' | awk '{print toupper($0)}')
NSLIST=$(echo $(dirname $INCLUDE_H) | awk '{l=tolower($0);gsub("/"," ",l);print l}')
SNLIST=
INDENT=
for N in $NSLIST; do
   SNLIST="$N $SNLIST"
   INDENT="$INDENT  "
done

######################################################################
function intro() {
######################################################################
   local FILE=$1
cat <<EOF
/*---------------------------------------------------------------------\\
|                          ____ _   __ __ ___                          |
|                         |__  / \ / / . \ . \                         |
|                           / / \ V /|  _/  _/                         |
|                          / /__ | | | | | |                           |
|                         /_____||_| |_| |_|                           |
|                                                                      |
\---------------------------------------------------------------------*/
/** \file	${FILE}
 */
EOF
}

######################################################################
function nsopen() {
######################################################################
   local I=
   for N in $NSLIST; do
      echo "${I}///////////////////////////////////////////////////////////////////"
      echo "${I}namespace $N"
      echo "${I}{"
      I="$I  "
   done
}

######################################################################
function nsclose() {
######################################################################
   local I=${INDENT}
   for N in $SNLIST; do
      I=${I#  }
      echo "${I}} // namespace $N"
      echo "${I}///////////////////////////////////////////////////////////////////"
   done
}

######################################################################
function genH() {
######################################################################
cat <<EOF
$(intro $CLASS_H)
#ifndef $INCLUDE_DEF
#define $INCLUDE_DEF

#include <iosfwd>

#include "zypp/base/PtrTypes.h"

$(nsopen)
${INDENT}///////////////////////////////////////////////////////////////////
${INDENT}/// \class ${CLASS}
${INDENT}/// \brief
${INDENT}///////////////////////////////////////////////////////////////////
${INDENT}class ${CLASS}
${INDENT}{
${INDENT}  friend std::ostream & operator<<( std::ostream & str, const ${CLASS} & obj );
${INDENT}  friend std::ostream & dumpOn( std::ostream & str, const ${CLASS} & obj );
${INDENT}  friend bool operator==( const ${CLASS} & lhs, const ${CLASS} & rhs );

${INDENT}  public:
${INDENT}    /** Default ctor */
${INDENT}    ${CLASS}();

${INDENT}    /** Dtor */
${INDENT}    ~${CLASS}();

${INDENT}  public:
${INDENT}    /**  Validate object in a boolean context. */
${INDENT}    explicit operator bool() const
${INDENT}    {
${INDENT}      /* !!! Perform Boolean logic here AND check implememtation of operator==!!!
${INDENT}       * NOTE: SafeBool requires operator== otherwise equality is reduced to
${INDENT}       *       ( bool(${CLASS}) == bool(${CLASS}) ).
${INDENT}       */
${INDENT}    }
${INDENT}  public:
${INDENT}    class Impl;                 ///< Implementation class.
${INDENT}  private:
${INDENT}    RWCOW_pointer<Impl> _pimpl; ///< Pointer to implementation.
${INDENT}};

${INDENT}/** \relates ${CLASS} Stream output */
${INDENT}std::ostream & operator<<( std::ostream & str, const ${CLASS} & obj );

${INDENT}/** \relates ${CLASS} Verbose stream output */
${INDENT}std::ostream & dumOn( std::ostream & str, const ${CLASS} & obj );

${INDENT}/** \relates ${CLASS} */
${INDENT}bool operator==( const ${CLASS} & lhs, const ${CLASS} & rhs );

${INDENT}/** \relates ${CLASS} */
${INDENT}inline bool operator!=( const ${CLASS} & lhs, const ${CLASS} & rhs )
${INDENT}{ return !( lhs == rhs ); }

$(nsclose)
#endif // $INCLUDE_DEF
EOF
}

######################################################################
function genCC() {
######################################################################
cat <<EOF
$(intro $CLASS_CC)
#include <iostream>
//#include "zypp/base/LogTools.h"
#include "zypp/base/NonCopyable.h"

#include "${INCLUDE_H}"

using std::endl;

$(nsopen)

${INDENT}///////////////////////////////////////////////////////////////////
${INDENT}/// \class ${CLASS}::Impl
${INDENT}/// \brief ${CLASS} implementation.
${INDENT}///////////////////////////////////////////////////////////////////
${INDENT}class ${CLASS}::Impl : private base::NonCopyable
${INDENT}{
${INDENT}  friend std::ostream & operator<<( std::ostream & str, const Impl & obj );
${INDENT}  friend std::ostream & dumpOn( std::ostream & str, const Impl & obj );

${INDENT}  public:

${INDENT}  public:
${INDENT}    /** Offer default Impl. */
${INDENT}    static shared_ptr<Impl> nullimpl()
${INDENT}    {
${INDENT}      static shared_ptr<Impl> _nullimpl( new Impl );
${INDENT}      return _nullimpl;
${INDENT}    }
${INDENT}  private:
${INDENT}    friend Impl * rwcowClone<Impl>( const Impl * rhs );
${INDENT}    /** clone for RWCOW_pointer */
${INDENT}    Impl * clone() const
${INDENT}    { return new Impl( *this ); }
${INDENT}};

${INDENT}/** \relates ${CLASS}::Impl Stream output */
${INDENT}inline std::ostream & operator<<( std::ostream & str, const ${CLASS}::Impl & obj )
${INDENT}{ return str << "${CLASS}::Impl"; }

${INDENT}/** \relates ${CLASS}::Impl Verbose stream output */
${INDENT}inline std::ostream & dumpOn( std::ostream & str, const ${CLASS}::Impl & obj )
${INDENT}{ return str << obj; }

${INDENT}///////////////////////////////////////////////////////////////////
${INDENT}//
${INDENT}//	CLASS NAME : ${CLASS}
${INDENT}//
${INDENT}///////////////////////////////////////////////////////////////////

${INDENT}${CLASS}::${CLASS}()
${INDENT}  : _pimpl( Impl::nullimpl() )
${INDENT}{}

${INDENT}${CLASS}::~${CLASS}()
${INDENT}{}

${INDENT}std::ostream & operator<<( std::ostream & str, const ${CLASS} & obj )
${INDENT}{ return str << *obj._pimpl; }

${INDENT}std::ostream & dumpOn( std::ostream & str, const ${CLASS} & obj )
${INDENT}{ return dumpOn( str, *obj._pimpl ); }

${INDENT}bool operator==( const ${CLASS} & lhs, const ${CLASS} & rhs )
${INDENT}{ return lhs._pimpl == rhs._pimpl || lhs._pimpl && rhs._pimpl && *lhs._pimpl == *rhs._pimpl; }

$(nsclose)
EOF
}

######################################################################
######################################################################
######################################################################

genH >$OUT_CLASS_H
genCC >$OUT_CLASS_CC
