/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once


#include "geode_defs.hpp"
#include "IGeodeCache.hpp"
#include "native_shared_ptr.hpp"

#include "begin_native.hpp"
#include <geode/Cache.hpp>
#include "end_native.hpp"

using namespace System;

namespace Apache
{
  namespace Geode
  {
    namespace Client
    {
      namespace Internal {
        ref class PdxTypeRegistry;
      } // namespace Internal

      namespace native = apache::geode::client;

      ref class QueryService;

      ref class RegionFactory;
      
      enum class ExpirationAction;

      enum class RegionShortcut;
            
      ref class CacheTransactionManager2PC;
      
      generic<class TPropKey, class TPropValue>
      ref class Properties;

      ref class PoolFactory;

      ref class PoolManager;

      ref class DataInput;

      ref class DataOutput;

      ref class TypeRegistry;

      /// <summary>
      /// Provides a distributed cache.
      /// </summary>
      /// <remarks>
      /// Caches are obtained from Create methods on the
      /// <see cref="CacheFactory.Create"/> class.
      /// <para>
      /// When a cache will no longer be used, call <see cref="Cache.Close" />.
      /// Once it <see cref="Cache.IsClosed" /> any attempt to use it
      /// will cause a <c>CacheClosedException</c> to be thrown.
      /// </para><para>
      /// A cache can have multiple root regions, each with a different name.
      /// </para>
      /// </remarks>
      public ref class Cache sealed
        : public IGeodeCache
      {
      public:
        
        ~Cache();

        /// <summary>
        /// Initializes the cache from an XML file.
        /// </summary>
        /// <param name="cacheXml">pathname of a <c>cache.xml</c> file</param>
        virtual void InitializeDeclarativeCache(String^ cacheXml);

        /// <summary>
        /// Returns the name of this cache.
        /// </summary>
        /// <remarks>
        /// This method does not throw
        /// <c>CacheClosedException</c> if the cache is closed.
        /// </remarks>
        /// <returns>the string name of this cache</returns>
        virtual property String^ Name
        {
          String^ get();
        }

        /// <summary>
        /// True if this cache has been closed.
        /// </summary>
        /// <remarks>
        /// After a new cache object is created, this method returns false.
        /// After <see cref="Close" /> is called on this cache object, this method
        /// returns true.
        /// </remarks>
        /// <returns>true if this cache is closed, otherwise false</returns>
        virtual property bool IsClosed
        {
          bool get();
        }

        virtual property SystemProperties^ SystemProperties
        {
          Apache::Geode::Client::SystemProperties^ get();
        }

        /// <summary>
        /// Returns the cache transaction manager of
        /// <see cref="CacheFactory.Create" /> this cache.
        /// </summary>
        virtual property Apache::Geode::Client::CacheTransactionManager^ CacheTransactionManager
        {
          Apache::Geode::Client::CacheTransactionManager^ get();
        }

        /// <summary>
        /// Terminates this object cache and releases all the local resources.
        /// </summary>
        /// <remarks>
        /// After this cache is closed, any further
        /// method call on this cache or any region object will throw
        /// <c>CacheClosedException</c>, unless otherwise noted.
        /// </remarks>
        /// <exception cref="CacheClosedException">
        /// if the cache is already closed.
        /// </exception>
        virtual void Close();

        /// <summary>
        /// Terminates this object cache and releases all the local resources.
        /// </summary>
        /// <remarks>
        /// After this cache is closed, any further
        /// method call on this cache or any region object will throw
        /// <c>CacheClosedException</c>, unless otherwise noted.
        /// </remarks>
        /// <param name="keepalive">whether to keep a durable client's queue alive</param>
        /// <exception cref="CacheClosedException">
        /// if the cache is already closed.
        /// </exception>
        virtual void Close(bool keepalive);

        /// <summary>
        /// Send the client-ready message to the server for a durable client.        
        /// </summary>
        /// <remarks>
        /// This method should only be called for durable clients and
        /// with a cache server version 5.5 onwards.
        /// </remarks>
        /// <exception cref="IllegalStateException">
        /// if there was a problem sending the message to the server.
        /// </exception>
        virtual void ReadyForEvents();

        /// <summary>
        /// Returns an existing region given the full path from root, or null 
        /// if no such region exists.
        /// </summary>
        /// <remarks>
        /// If Pool attached with Region is in multiusersecure mode then don't use return instance of region as no credential are attached with this instance.
        /// Get region from RegionService instance of Cache.<see cref="Cache.CreateAuthenticatedView(std::shared_ptr<PropertiesPtr>)" />.
        /// </remarks>
        /// <param name="path">the pathname of the region</param>
        /// <returns>the region</returns>
        generic<class TKey, class TValue>
        virtual IRegion<TKey, TValue>^ GetRegion(String^ path);

        /// <summary>
        /// Returns an array of root regions in the cache. This set is a
        /// snapshot and is not backed by the cache.
        /// </summary>
        /// <remarks>
        /// It is not supported when Cache is created from Pool.
        /// </remarks>
        /// <returns>array of regions</returns>
        generic<class TKey, class TValue>
        virtual array<IRegion<TKey, TValue>^>^ RootRegions();

        /// <summary>
        /// Get a query service object to be able to query the cache.
        /// Supported only when cache is created from Pool(pool is in multiuserSecure mode)
        /// </summary>
        /// <remarks>
        /// Currently only works against the java server in native mode, and
        /// at least some endpoints must have been defined in some regions
        /// before actually firing a query.
        /// </remarks>
        virtual Client::QueryService^ GetQueryService();

        /// <summary>
        /// Get a query service object to be able to query the cache.
        /// Use only when Cache has more than one Pool.
        /// </summary>
        /// <remarks>
        /// Currently only works against the java server in native mode, and
        /// at least some endpoints must have been defined in some regions
        /// before actually firing a query.
        /// </remarks>
        virtual Client::QueryService^ GetQueryService(String^ poolName);

        /// <summary>
        /// Returns the instance of <see cref="RegionFactory" /> to create the region
        /// </summary>
        /// <remarks>
        /// Pass the <see cref="RegionShortcut" /> to set the default region attributes
        /// </remarks>
        /// <param name="regionShortcut">the regionShortcut to set the default region attributes</param>
        /// <returns>Instance of RegionFactory</returns>
        RegionFactory^ CreateRegionFactory(RegionShortcut regionShortcut);

        /// <summary>
        /// Returns the instance of <see cref="IRegionService" /> to do the operation on Cache with different Credential.
        /// </summary>
        /// <remarks>
        /// Default pool should be in multiuser mode <see cref="CacheFactory.SetMultiuserAuthentication" />
        /// </remarks>
        /// <param name="credentials">the user Credentials.</param>
        /// <returns>Instance of IRegionService</returns>
        IRegionService^ CreateAuthenticatedView(Properties<String^, Object^>^ credentials);

        /// <summary>
        /// Returns the instance of <see cref="IRegionService" /> to do the operation on Cache with different Credential.
        /// </summary>
        /// <remarks>
        /// Default pool should be in multiuser mode <see cref="CacheFactory.SetMultiuserAuthentication" />
        /// </remarks>
        /// <param name="credentials">the user Credentials.</param>
        /// <param name="poolName">Pool, which is in multiuser mode.</param>
        /// <returns>Instance of IRegionService</returns>
        IRegionService^ CreateAuthenticatedView(Properties<String^, Object^>^ credentials, String^ poolName);

        ///<summary>
        /// Returns whether Cache saves unread fields for Pdx types.
        ///</summary>
        virtual bool GetPdxIgnoreUnreadFields();

        ///<summary>
        /// Returns whether { @link PdxInstance} is preferred for PDX types instead of .NET object.
        ///</summary>
        virtual bool GetPdxReadSerialized();

        /// <summary>
        /// Returns a factory that can create a {@link PdxInstance}.
        /// @param className the fully qualified class name that the PdxInstance will become
        ///   when it is fully deserialized.
        /// @return the factory
        /// </summary>
        virtual IPdxInstanceFactory^ CreatePdxInstanceFactory(String^ className);

        virtual DataInput^ CreateDataInput(array<Byte>^ buffer, System::Int32 len);
        virtual DataInput^ CreateDataInput(array<Byte>^ buffer);
        
        virtual DataOutput^ CreateDataOutput();

        /// <summary>
        /// Returns a PoolFactory that can be used to create a Pool and that provides
        /// access to all Pool attributes.
        /// @return the PoolFactory
        /// </summary>
        virtual PoolFactory^ GetPoolFactory();

        /// <summary>
        /// Returns a PoolManager that provides for the configuration and creation
        /// of instances of PoolFactory.
        /// @return the PoolManager
        /// </summary>
        virtual PoolManager^ GetPoolManager();

        property Apache::Geode::Client::TypeRegistry^ TypeRegistry
        {
          Apache::Geode::Client::TypeRegistry^ get() { return m_typeRegistry; }
        }

      internal:

        /// <summary>
        /// Internal factory function to wrap a native object pointer inside
        /// this managed class with null pointer check.
        /// </summary>
        /// <param name="nativeptr">The native object pointer</param>
        /// <returns>
        /// The managed wrapper object; null if the native pointer is null.
        /// </returns>
        inline static Cache^ Create(std::shared_ptr<native::Cache> nativeptr)
        {
          return __nullptr == nativeptr ? nullptr :
            gcnew Cache( nativeptr );
        }

        std::shared_ptr<native::Cache> GetNative()
        {
          return m_nativeptr->get_shared_ptr();
        }

        Apache::Geode::Client::Internal::PdxTypeRegistry^ GetPdxTypeRegistry()
        {
          return m_pdxTypeRegistry;
        }
		        
		Apache::Geode::Client::TypeRegistry^ GetTypeRegistry()
        {
          return m_typeRegistry;
        }
      private:

        /// <summary>
        /// Private constructor to wrap a native object pointer
        /// </summary>
        /// <param name="nativeptr">The native object pointer</param>
        Cache(std::shared_ptr<native::Cache> nativeptr);

        native_shared_ptr<native::Cache>^ m_nativeptr;

        Apache::Geode::Client::Internal::PdxTypeRegistry^ m_pdxTypeRegistry;
        Apache::Geode::Client::TypeRegistry^ m_typeRegistry;
      };
    }  // namespace Client
  }  // namespace Geode
}  // namespace Apache

