/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed 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.
 */

#ifndef ANDROID_USB_API_ADB_OBJECT_HANDLE_H__
#define ANDROID_USB_API_ADB_OBJECT_HANDLE_H__
/** \file
  This file consists of declaration of a class AdbObjectHandle that
  encapsulates an internal API object that is visible to the outside
  of the API through a handle.
*/

#include "adb_api.h"
#include "adb_api_private_defines.h"

/** \brief Defines types of internal API objects
*/
enum AdbObjectType {
  /// Object is AdbInterfaceEnumObject.
  AdbObjectTypeInterfaceEnumerator,

  /// Object is AdbInterfaceObject.
  AdbObjectTypeInterface,

  /// Object is AdbEndpointObject.
  AdbObjectTypeEndpoint,

  /// Object is AdbIOCompletion.
  AdbObjectTypeIoCompletion,

  AdbObjectTypeMax
};

/** \brief Encapsulates an internal API basic object that is visible to the
  outside of the API through a handle.
  
  In order to prevent crashes when API client tries to access an object through
  an invalid or already closed handle, we keep track of all opened handles in
  AdbObjectHandleMap that maps association between valid ADBAPIHANDLE and
  an object that this handle represents. All objects that are exposed to the
  outside of API via ADBAPIHANDLE are self-destructing referenced objects.
  The reference model for these objects is as such:
  1. When CreateHandle() method is called on an object, a handle (ADBAPIHANDLE
     that is) is assigned to it, a pair <handle, object> is added to the global
     AdbObjectHandleMap instance, object is referenced and then handle is
     returned to the API client.
  2. Every time API is called with a handle, a lookup is performed in 
     AdbObjectHandleMap to find an object that is associated with the handle.
     If object is not found then ERROR_INVALID_HANDLE is immediatelly returned
     (via SetLastError() call). If object is found then it is referenced and
     API call is dispatched to appropriate method of the found object. Upon
     return from this method, just before returning from the API call, object
     is dereferenced back to match lookup reference.
  3. When object handle gets closed, assuming object is found in the map, that
     <handle, object> pair is deleted from the map and object's refcount is
     decremented to match refcount increment performed when object has been
     added to the map.
  4. When object's refcount drops to zero, the object commits suicide by
     calling "delete this".
  All API objects that have handles that are sent back to API client must be
  derived from this class.
*/
class ADBWIN_API_CLASS AdbObjectHandle {
 public:
  /** \brief Constructs the object

    Refernce counter is set to 1 in the constructor.
    @param[in] obj_type Object type from AdbObjectType enum
  */
  explicit AdbObjectHandle(AdbObjectType obj_type);

 protected:
  /** \brief Destructs the object.

   We hide destructor in order to prevent ourseves from accidentaly allocating
   instances on the stack. If such attempt occurs, compiler will error.
  */
  virtual ~AdbObjectHandle();

 public:
  /** \brief References the object.

    @return Value of the reference counter after object is referenced in this
            method.
  */
  virtual LONG AddRef();

  /** \brief Releases the object.

    If refcount drops to zero as the result of this release, the object is
    destroyed in this method. As a general rule, objects must not be touched
    after this method returns even if returned value is not zero.
    @return Value of the reference counter after object is released in this
            method.
  */
  virtual LONG Release();

  /** \brief Creates handle to this object.

    In this call a handle for this object is generated and object is added
    to the AdbObjectHandleMap.
    @return A handle to this object on success or NULL on an error.
            If NULL is returned GetLastError() provides extended error
            information. ERROR_GEN_FAILURE is set if an attempt was
            made to create already opened object.
  */
  virtual ADBAPIHANDLE CreateHandle();

  /** \brief This method is called when handle to this object gets closed.

    In this call object is deleted from the AdbObjectHandleMap.
    @return true on success or false if object is already closed. If
            false is returned GetLastError() provides extended error
            information.
  */
  virtual bool CloseHandle();

  /** \brief Checks if this object is of the given type.

    @param[in] obj_type One of the AdbObjectType types to check
    @return true is this object type matches obj_type, or false otherwise.
  */
  virtual bool IsObjectOfType(AdbObjectType obj_type) const;

  /** \brief Looks up AdbObjectHandle instance associated with the given handle
    in the AdbObjectHandleMap.

    This method increments reference counter for the returned found object.
    @param[in] adb_handle ADB handle to the object
    @return API object associated with the handle or NULL if object is not
            found. If NULL is returned GetLastError() provides extended error
            information.
  */
  static AdbObjectHandle* Lookup(ADBAPIHANDLE adb_handle);

 protected:
  /** \brief Called when last reference to this object is released.

    Derived object should override this method to perform cleanup that is not
    suitable for destructors.
  */
  virtual void LastReferenceReleased();

 public:
  /// Gets ADB handle associated with this object
  ADBAPIHANDLE adb_handle() const {
    return adb_handle_;
  }

  /// Gets type of this object
  AdbObjectType object_type() const {
    return object_type_;
  }

  /// Checks if object is still opened. Note that it is not guaranteed that
  /// object remains opened when this method returns.
  bool IsOpened() const {
    return (NULL != adb_handle());
  }

 protected:
  /// API handle associated with this object
  ADBAPIHANDLE  adb_handle_;

  /// Type of this object
  AdbObjectType object_type_;

  /// This object's reference counter
  LONG          ref_count_;
};

/// Maps ADBAPIHANDLE to associated AdbObjectHandle object
typedef std::map< ADBAPIHANDLE, AdbObjectHandle* > AdbObjectHandleMap;

/** \brief Template routine that unifies extracting of objects of different
  types from the AdbObjectHandleMap

  @param[in] adb_handle API handle for the object
  @return Object associated with the handle or NULL on error. If NULL is
          returned GetLastError() provides extended error information.
*/
template<class obj_class>
obj_class* LookupObject(ADBAPIHANDLE adb_handle) {
  // Lookup object for the handle in the map
  AdbObjectHandle* adb_object = AdbObjectHandle::Lookup(adb_handle);
  if (NULL != adb_object) {
    // Make sure it's of the correct type
    if (!adb_object->IsObjectOfType(obj_class::Type())) {
      adb_object->Release();
      adb_object = NULL;
      SetLastError(ERROR_INVALID_HANDLE);
    }
  } else {
    SetLastError(ERROR_INVALID_HANDLE);
  }
  return (adb_object != NULL) ? reinterpret_cast<obj_class*>(adb_object) :
                                NULL;
}

#endif  // ANDROID_USB_API_ADB_OBJECT_HANDLE_H__
