/*
  PolymorphicValue(Test)  -  verify handling of opaque polymorphic values

  Copyright (C)         Lumiera.org
    2011,               Hermann Vosseler <Ichthyostega@web.de>

  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., 675 Mass Ave, Cambridge, MA 02139, USA.

* *****************************************************/



#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "lib/util-foreach.hpp"

#include "lib/polymorphic-value.hpp"

#include <vector>


namespace lib {
namespace test{
  
  using ::Test;
  using util::for_each;
  using util::unConst;
  using util::isSameObject;
  using lumiera::error::LUMIERA_ERROR_ASSERTION;
  
  
  namespace { // test dummy hierarchy
             //  Note: largely varying space requirements
            //         correct function depending on concrete class
    
    
    struct Interface
      {
        virtual ~Interface() {}
        virtual long apiFunc()   =0;
        
        virtual long& localSum() =0;
        
        bool
        operator== (Interface const& o)  const
          {
            return unConst(this)->localSum()
                == unConst(o).localSum();
          }
      };
    
    
    const uint MAX_RAND = 1000;
    const uint MAX_ELM  = 111;
    const uint MAX_SIZ  = sizeof(long[MAX_ELM]);
    
    /* Checksums to verify proper ctor-dtor calls and copy operations */
    long _checkSum = 0;
    long _callSum  = 0;
    uint _created  = 0;
    
    
    /**
     * Template to generate concrete implementation classes.
     * @note the generated classes vary largely in size, and
     *       moreover the actual place to store the checksum
     *       also depends on that size parameter.
     */
    template<uint ii, class BASE=Interface>
    struct Imp : BASE
      {
        long localData_[ii];
        
       ~Imp()
          {
            mark (-1 * localSum());
            CHECK (0 == localSum());
          }
        
        Imp()
          {
            REQUIRE (0 < ii);
            localSum() = 0;
            mark(ii);
            ++ _created;
          }
        
        Imp (Imp const& o)
          {
            ++ _created;
            copyData (o);
            _checkSum  += localSum();
          }// adjust, because we've gotten two identical instances
        
        Imp&
        operator= (Imp const& o)
          {
            _checkSum  -= localSum();
            copyData (o);
            _checkSum  += localSum();
            return *this;
          }
        
      private:
        virtual long
        apiFunc()
          {
            long rr = ii * (1 + rand() % MAX_RAND);
            mark (rr);
            _callSum += rr;
            return rr;
          }
        
        long&
        localSum()
          {
            return localData_[ii-1];
          }
        
        void
        mark (long markerValue)
          {
            localSum() += markerValue;
            _checkSum  += markerValue;
          }
        
        void
        copyData (Imp const& o)
          {
            for (uint i=0; i<ii; ++i)
              localData_[i] = o.localData_[i];
          }
      };
    
    
    /** maximum additional storage maybe wasted
     *  due to alignment of the contained object
     *  within the embedded Holder/Buffer
     */
    const size_t _ALIGN_ = sizeof(size_t);
    
  }
  
  typedef PolymorphicValue<Interface, MAX_SIZ> PolyVal;
  typedef std::vector<PolyVal> TestList;
  
  
  
  
  
  
  /******************************************************************************//**
   *  @test build a bunch of PolymorphicValue objects. Handle them like copyable
   *        value objects, without knowing the exact implementation type; moreover
   *        execute implementation internals only disclosed to the concrete subtype.
   *        Verify correctness through checksums.
   */
  class PolymorphicValue_test : public Test
    {
      
      virtual void
      run (Arg)
        {
          _checkSum = 0;
          _callSum  = 0;
          _created  = 0;
          
          verifyBasics();
          
          {
            TestList objs = createOpaqueValues ();
            for_each (objs, operate);
          }
          CHECK (0 == _checkSum); // all dead
          
          verifyOverrunProtection();
          verifyCopySupportDetectionMetafunctions();
        }
      
      
      TestList
      createOpaqueValues ()
        {
          TestList list;
          list.push_back (PolyVal::build<Imp<1> >  () );
          list.push_back (PolyVal::build<Imp<11> > () );
          list.push_back (PolyVal::build<Imp<111> >() );
          list.push_back (PolyVal::build<Imp<23> > () );
          list.push_back (PolyVal::build<Imp<5> >  () );
          return list;
        } //note: copy
      
      
      static void
      operate (PolyVal& elm)
        {
          PolyVal myLocalVal(elm);
          CHECK (elm == myLocalVal);
          
          long prevSum = _callSum;
          long randVal = myLocalVal->apiFunc();
          CHECK (prevSum + randVal == _callSum);
          CHECK (elm != myLocalVal);
          
          elm = myLocalVal;
          CHECK (elm == myLocalVal);
          CHECK (!isSameObject (elm, myLocalVal));
          
          CHECK (sizeof(myLocalVal) <= MAX_SIZ + polyvalue::Trait<Interface>::ADMIN_OVERHEAD + _ALIGN_);
        }
      
      
      void
      verifyBasics()
        {
          typedef Imp<MAX_ELM> MaximumSizedImp;
          
          // Standard case: no copy support by client objects
          verifyCreation_and_Copy<PolyVal, MaximumSizedImp>();
          
          // Special case: client objects expose extension point for copy support
          typedef polyvalue::CopySupport<Interface> CopySupportAPI;                    // Copy support API declared as sub-interface
          typedef Imp<MAX_ELM,CopySupportAPI> CopySupportingImp;                       // insert this sub-interface between public API and Implementation
          typedef PolymorphicValue<Interface, MAX_SIZ, CopySupportAPI> OptimalPolyVal; // Make the Holder use this special attachment point
          CHECK (sizeof(OptimalPolyVal) < sizeof(PolyVal));                            // results in smaller Holder and less implementation overhead
          
          verifyCreation_and_Copy<OptimalPolyVal, CopySupportingImp>();
        }
      
      
      template<class PV,class IMP>
      void
      verifyCreation_and_Copy()
        {
          typedef PV Holder;
          typedef IMP ImpType;
          typedef typename PV::Interface Api;
          
          long prevSum = _checkSum;
          uint prevCnt = _created;
          
          Holder val = Holder::template build<ImpType>();
          CHECK (prevSum+111 == _checkSum);            // We got one primary ctor call
          CHECK (prevCnt+1   <= _created);             // Note: usually, the compiler optimises
          CHECK (prevCnt+2   >= _created);             //       and skips the spurious copy-operation
          CHECK (sizeof(Holder) >= sizeof(ImpType));
          Api& embedded = val;
          CHECK (isSameObject(embedded,val));
          CHECK (INSTANCEOF(ImpType, &embedded));
          
          prevCnt = _created;
          Holder val2(val);       // invoke copy ctor without knowing the implementation type
          embedded.apiFunc();
          CHECK (val != val2);    // invoking the API function had an sideeffect on the state
          val = val2;             // assignment of copy back to the original...
          CHECK (val == val2);    // cancels the side effect
          
          CHECK (prevCnt+1 == _created); // one new embedded instance was created by copy ctor
        }
      
      
      void
      verifyOverrunProtection()
        {
          typedef Imp<MAX_ELM+1> OversizedImp;
          CHECK (MAX_SIZ < sizeof(OversizedImp));
#if false ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #537 : restore throwing ASSERT
          VERIFY_ERROR (ASSERTION, PolyVal::build<OversizedImp>() );
#endif    ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #537 : restore throwing ASSERT
        }
      
      
      /** @Test internally, PolymorphicValue uses some metafunctions
       * to pick a suitable code path, based on the presence of helper functions
       * on the API of the embedded objects. Default is no support by these objects,
       * which then requires to use a more expensive implementation. Sometimes it's
       * desirable to support \em cloning only (copy ctor), but no assignment after
       * the fact. In this special case, a support API with only a \cloneInto member
       * can be implemented, causing the PolymorphicValue container to raise an 
       * exception in case the copy operator is invoked.
       */
      void
      verifyCopySupportDetectionMetafunctions()
        {
          typedef polyvalue::CopySupport<Interface> CopySupportAPI;
          typedef polyvalue::CloneValueSupport<Interface> CloneOnlyAPI;
          
          CHECK ( !polyvalue::exposes_CloneFunction<Interface>::value );
          CHECK (  polyvalue::exposes_CloneFunction<CopySupportAPI>::value );
          CHECK (  polyvalue::exposes_CloneFunction<CloneOnlyAPI>::value );
          
          CHECK (  polyvalue::allow_Clone_but_no_Copy<CloneOnlyAPI>::value );
          CHECK ( !polyvalue::allow_Clone_but_no_Copy<CopySupportAPI>::value );
       }
    };
  
  
  LAUNCHER (PolymorphicValue_test, "unit common");
  
  
}} // namespace lib::test
