/*
  GenNode  -  generic node element for tree like data representation

  Copyright (C)         Lumiera.org
    2015,               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.

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


/** @file gen-node.cpp
 ** Generic node element (implementation parts).
 ** Some of the more technical details regarding value access and
 ** comparisons has been moved down within this compilation unit,
 ** to cut down compilation time.
 ** 
 ** \par comparison and match
 ** 
 ** The DataCap element provides a set of functions to check for \em equivalence
 ** or match. These are used to build a recursive containment check. To implement
 ** such predicates, we need to build a one-way off visitor for use with lib::Variant.
 ** These specifically tailored functors only define \c handle(TY) functions for the
 ** cases actually of interest. All other cases invoke the default handling, which
 ** returns \c false.
 ** 
 ** @see gen-node-test.cpp
 ** 
 */


#include "lib/error.hpp"
#include "lib/diff/diff-language.hpp"
#include "lib/diff/gen-node.hpp"
#include "lib/util-quant.hpp"
#include "lib/variant.hpp"

#include <boost/lexical_cast.hpp>


using boost::lexical_cast;
using lib::time::TimeValue;
using util::almostEqual;
using lib::hash::LuidH;


namespace lib {
namespace diff{
  
  /* symbolic marker ID references
   * used within the tree diff language
   * to mark specific scopes and situations
   */
  const Ref Ref::I      ("_I_");
  const Ref Ref::NO     ("_NO_");
  const Ref Ref::END    ("_END_");
  const Ref Ref::THIS   ("_THIS_");     ////////TICKET #996 : Feature of questionable usefulness. Maybe dispensable?
  const Ref Ref::CHILD  ("_CHILD_");
  const Ref Ref::ATTRIBS("_ATTRIBS_");
  
  
  
  
  /** Implementation of content equality test, delgating to content
   * @throws error::Logic when the given other DataCap
   *         does not hold a value of the same type than
   *         this DataCap.
   * @remarks since the GenNode::ID is generated including a type hash,
   *         the equality operator of GenNode ensures this content test
   *         is only called on a compatible DataCap.
   */
  bool
  DataCap::matchData (DataCap const& o)  const
  {
    class EqualityTest
      : public Variant<DataValues>::Predicate
      {
        DataCap const& o_;

#define DERIVE_EQUALITY(_TY_) \
        virtual bool handle  (_TY_ const& val) override { return (o_.get<_TY_>() == val); }
        
        DERIVE_EQUALITY (int)
        DERIVE_EQUALITY (int64_t)
        DERIVE_EQUALITY (short)
        DERIVE_EQUALITY (char)
        DERIVE_EQUALITY (bool)
        DERIVE_EQUALITY (double)
        DERIVE_EQUALITY (string)
        DERIVE_EQUALITY (time::Time)
        DERIVE_EQUALITY (time::Offset)
        DERIVE_EQUALITY (time::Duration)
        DERIVE_EQUALITY (time::TimeSpan)
        DERIVE_EQUALITY (hash::LuidH)
        DERIVE_EQUALITY (Rec)
        
        /** special treatment to allow matching a RecRef
         * with an Record or RecRef on the other side */
        virtual bool
        handle  (RecRef const& val) override
          {
            return o_.matchRec(val);
          }
        
      public:
        EqualityTest(DataCap const& o)
          : o_(o)
          { }
      };
    
    EqualityTest visitor(o);
    return accept(visitor);
  }
  
  
  bool
  DataCap::matchNum (int64_t num)  const
  {
    class MatchNumber
      : public Variant<DataValues>::Predicate
      {
        int64_t num_;

#define MATCH_NUMBER(_TY_) \
        virtual bool handle  (_TY_ const& val) override { return val == num_; }
        
        MATCH_NUMBER (int)
        MATCH_NUMBER (int64_t)
        MATCH_NUMBER (short)
        MATCH_NUMBER (char)
        MATCH_NUMBER (double)
        
      public:
        MatchNumber(int64_t num)
          : num_(num)
          { }
      };
    
    MatchNumber visitor(num);
    return accept(visitor);
  }
  
  
  bool
  DataCap::matchDbl (double d)  const
  {
    class MatchDouble
      : public Variant<DataValues>::Predicate
      {
        double num_;

#define MATCH_DOUBLE(_TY_) \
        virtual bool handle  (_TY_ const& val) override { return almostEqual (double(val), num_); }
        
        MATCH_DOUBLE (int)
        MATCH_DOUBLE (int64_t)
        MATCH_DOUBLE (short)
        MATCH_DOUBLE (char)
        MATCH_DOUBLE (double)
        
      public:
        MatchDouble(double d)
          : num_(d)
          { }
      };
    
    MatchDouble visitor(d);
    return accept(visitor);
  }
  
  
  bool
  DataCap::matchTxt (string const& text)  const
  {
    class MatchString
      : public Variant<DataValues>::Predicate
      {
        string const& txt_;

#define MATCH_STRING(_TY_) \
        virtual bool handle  (_TY_ const& val) override { return lexical_cast<string>(val) == txt_; }
        
        MATCH_STRING (int)
        MATCH_STRING (int64_t)
        MATCH_STRING (short)
        MATCH_STRING (double)
        MATCH_STRING (bool)
        
        virtual bool handle  (string const& str) override { return str == txt_; }
        virtual bool handle  (char   const& c  ) override { return 1 == txt_.length() && txt_.front() == c; }
        
      public:
        MatchString(string const& text)
          : txt_(text)
          { }
      };
    
    MatchString visitor(text);
    return accept(visitor);
  }
  
  
  bool
  DataCap::matchTime (TimeValue time)  const
  {
    class MatchTime
      : public Variant<DataValues>::Predicate
      {
        TimeValue& t_;

#define MATCH_TIME(_TY_) \
        virtual bool handle  (_TY_ const& val) override { return val == t_; }
        
        MATCH_TIME (time::Time)
        MATCH_TIME (time::Offset)
        MATCH_TIME (time::Duration)
        MATCH_TIME (time::TimeSpan)
        MATCH_TIME (hash::LuidH)
        
      public:
        MatchTime(TimeValue& t)
          : t_(t)
          { }
      };
    
    MatchTime visitor(time);
    return accept(visitor);
  }
  
  
  bool
  DataCap::matchBool (bool b)  const
  {
    bool* val = unConst(this)->maybeGet<bool>();
    return val && (b == *val);
  }
  
  
  bool
  DataCap::matchLuid (LuidH hash)  const
  {
    LuidH* val = unConst(this)->maybeGet<LuidH>();
    return val && (hash == *val);
  }
  
  
  bool
  DataCap::matchRec (RecRef const& ref)  const
  {
    if (ref)
      return matchRec (*ref.get());
    else
      {
        RecRef* val = unConst(this)->maybeGet<RecRef>();
        return val && val->empty();
      }
  }
  
  
  bool
  DataCap::matchRec (Rec const& rec)  const
  {
    Rec* val = unConst(this)->maybeGet<Rec>();
    if (!val)
      {
        RecRef* r = unConst(this)->maybeGet<RecRef>();
        if (r) val = r->get();
      }
    return val && (rec == *val);
  }
  
  
  
  
  DataCap::operator string()  const
  {
    return "DataCap|"+string(this->buffer());
  }
  
  
}} // namespace lib::diff
