/*
  SessionServices  -  accessing Proc-Layer internal session implementation services

  Copyright (C)         Lumiera.org
    2008,               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 "proc/mobject/session/session-service-fetch.hpp"
#include "proc/mobject/session/session-service-mutate.hpp"
#include "proc/mobject/session/session-service-explore-scope.hpp"
#include "proc/mobject/session/session-service-mock-index.hpp"
#include "proc/mobject/session/session-service-defaults.hpp"

#include "proc/mobject/session/session-services.hpp"
#include "proc/mobject/session/session-impl.hpp"
#include "proc/mobject/session/sess-manager-impl.hpp"

#include "proc/mobject/session/mobjectfactory.hpp"
#include "lib/symbol.hpp"

using lib::Symbol;

namespace proc {
namespace mobject {
namespace session {
  
  /** is the element-fetch service usable?
   *  Effectively this means: is the session up?
   */
  bool
  SessionServiceFetch::isAccessible ()
  {
    return Session::initFlag
        && Session::current.isUp();
  }
  
  /** verify the given placement-ID (hash) is valid,
   *  by checking if it refers to a Placement instance
   *  currently registered with the PlacementIndex of the
   *  active Session. */
  bool
  SessionServiceFetch::isRegisteredID (PlacementMO::ID const& placementID)
  {
    return SessionImplAPI::current->isRegisteredID (placementID);
  }
  
  
  /** actually retrieve a Placement tracked by the index.
   *  @param placementID hash-ID, typically from a PlacementRef
   *  @throw error::Invalid if the ID isn't resolvable
   *  @note the returned ref is guaranteed to be valid and usable
   *        only \em now, which means, by virtue of the ProcDispatcher
   *        and command processing, during this operation. It can be
   *        used to invoke an operation, but should never be stored;
   *        rather, client code should create an MObjectRef, if 
   *        bound to store an reference for later.
   */
  PlacementMO&
  SessionServiceFetch::resolveID (PlacementMO::ID const& placementID)
  {
    return SessionImplAPI::current->resolveID (placementID);
  }
  
  
  /** attach an object by placement onto the session.
   *  Implemented by registering a copy of the Placement into the
   *  PlacementIndex in the session. This copy establishes a new kind of
   *  "object instance", represented by a new placement-ID, which is returned
   *  and can be used to refer to this "instance" within the session from now on.
   *  @param scope the (existing) parent scope where to attach the new element
   */
  PlacementMO::ID const&
  SessionServiceMutate::attach_toModel(PMO newPlacement, PID scope)
  {
    return SessionImplAPI::current->insertCopy (newPlacement,scope);
  }
  
  
  /** detach the denoted element from the model <i>including all children.</i>
   *  @return true if actually erased something
   *  @note when specifying model root, all sub-elements will be cleared,
   *        but model root itself will be retained. 
   */
  bool
  SessionServiceMutate::detach_and_clear (PID scope)
  {
    return SessionImplAPI::current->purgeScopeRecursively (scope);
  }
  
  
  /** detach the denoted leaf element from the model.
   *  @return true if actually erased something
   *  @throw error::Fatal when attempting to remove the model root
   *  @throw error::State when the given element contains sub elements
   */
  bool
  SessionServiceMutate::detach (PID leafElement)
  {
    return SessionImplAPI::current->detachElement (leafElement);
  }

  
  
  
  namespace { // deleter function to clean up test/mock PlacementIndex
    void
    remove_testIndex (PlacementIndex* testIdx)
    {
      REQUIRE (testIdx);
      SessionImplAPI::current->reset_PlacementIndex();  // restore default Index from Session
      
      testIdx->clear();
      ASSERT (0 == testIdx->size());
      delete testIdx;
    }
  }
  
  /** Re-define the implicit PlacementIndex temporarily, e.g. for unit tests. */
  PPIdx
  SessionServiceMockIndex:: install ()
  {
    Symbol typeID ("dummyRoot");
    PMO dummyRoot (MObject::create (typeID));
    PPIdx mockIndex (new PlacementIndex(dummyRoot), &remove_testIndex); // manage instance lifecycle
    ENSURE (mockIndex);
    ENSURE (mockIndex->isValid());
    ENSURE (1 == mockIndex.use_count());
    
    SessionImplAPI::current->reset_PlacementIndex (mockIndex.get());
    return mockIndex;
  }
  
  
  /** @return resolver for DiscoveryQuery instances, actually backed by PlacementIndex */
  QueryResolver const&
  SessionServiceExploreScope::getResolver()
  {
    return SessionImplAPI::current->getScopeQueryResolver();
  }
  
  
  /** @return root scope of the current model (session datastructure) */
  PlacementMO& 
  SessionServiceExploreScope::getScope (PlacementMO const& placementToLocate)
  {
    return SessionImplAPI::current->getScope(placementToLocate);
  }


  PlacementMO& 
  SessionServiceExploreScope::getScope (PlacementMO::ID const& placementToLocate)
  {
    return SessionImplAPI::current->getScope(placementToLocate);
  }


  /** @return root scope of the current model (session datastructure) */
  PlacementMO&
  SessionServiceExploreScope::getScopeRoot()
  {
    return SessionImplAPI::current->getScopeRoot();
  }
  
  
  
}}} // namespace proc::mobject::session
