// Copyright (c) Microsoft Corporation
// All rights reserved. 
//
// 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 
//
// THIS CODE IS PROVIDED ON AN  *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. 
//
// See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.

#define NTDDI_VERSION 0x06010000

#include <v8.h>
#include <string>
#include <node_object_wrap.h>
#include <ppltasks.h>
#include "CollectionsConverter.h"
#include "CollectionsWrap.h"
#include "node-async.h"
#include "NodeRtUtils.h"
#include "OpaqueWrapper.h"
#include "WrapperBase.h"

#using <Windows.WinMD>

// this undefs fixes the issues of compiling Windows.Data.Json, Windows.Storag.FileProperties, and Windows.Stroage.Search
// Some of the node header files brings windows definitions with the same names as some of the WinRT methods
#undef DocumentProperties
#undef GetObject
#undef CreateEvent
#undef FindText
#undef SendMessage

const char* REGISTRATION_TOKEN_MAP_PROPERTY_NAME = "__registrationTokenMap__";

using namespace v8;
using namespace node;
using namespace concurrency;

namespace NodeRT { namespace Windows { namespace Devices { namespace Enumeration { namespace Pnp { 
  v8::Handle<v8::Value> WrapPnpObjectUpdate(::Windows::Devices::Enumeration::Pnp::PnpObjectUpdate^ wintRtInstance);
  ::Windows::Devices::Enumeration::Pnp::PnpObjectUpdate^ UnwrapPnpObjectUpdate(Handle<Value> value);
  
  v8::Handle<v8::Value> WrapPnpObjectCollection(::Windows::Devices::Enumeration::Pnp::PnpObjectCollection^ wintRtInstance);
  ::Windows::Devices::Enumeration::Pnp::PnpObjectCollection^ UnwrapPnpObjectCollection(Handle<Value> value);
  
  v8::Handle<v8::Value> WrapPnpObjectWatcher(::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^ wintRtInstance);
  ::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^ UnwrapPnpObjectWatcher(Handle<Value> value);
  
  v8::Handle<v8::Value> WrapPnpObject(::Windows::Devices::Enumeration::Pnp::PnpObject^ wintRtInstance);
  ::Windows::Devices::Enumeration::Pnp::PnpObject^ UnwrapPnpObject(Handle<Value> value);
  


  static v8::Handle<v8::Value> InitPnpObjectTypeEnum(const Handle<Object> exports)
  {
    HandleScope scope;
    
    Handle<Object> enumObject = Object::New();
    exports->Set(String::NewSymbol("PnpObjectType"), enumObject);

    enumObject->Set(String::NewSymbol("unknown"), Integer::New(0));
    enumObject->Set(String::NewSymbol("deviceInterface"), Integer::New(1));
    enumObject->Set(String::NewSymbol("deviceContainer"), Integer::New(2));
    enumObject->Set(String::NewSymbol("device"), Integer::New(3));
    enumObject->Set(String::NewSymbol("deviceInterfaceClass"), Integer::New(4));

    return scope.Close(Undefined());
  }



  
  class PnpObjectUpdate : public WrapperBase
  {
  public:    
    static v8::Handle<v8::Value> Init(const Handle<Object> exports)
    {
      HandleScope scope;
      
      s_constructorTemplate = Persistent<FunctionTemplate>::New(FunctionTemplate::New(New));
      s_constructorTemplate->SetClassName(String::NewSymbol("PnpObjectUpdate"));
      s_constructorTemplate->InstanceTemplate()->SetInternalFieldCount(1);
      
                              
      s_constructorTemplate->PrototypeTemplate()->SetAccessor(String::NewSymbol("id"), IdGetter);
      s_constructorTemplate->PrototypeTemplate()->SetAccessor(String::NewSymbol("properties"), PropertiesGetter);
      s_constructorTemplate->PrototypeTemplate()->SetAccessor(String::NewSymbol("type"), TypeGetter);
      
      Local<Function> constructor = s_constructorTemplate->GetFunction();


      exports->Set(String::NewSymbol("PnpObjectUpdate"), constructor);
      return scope.Close(Undefined());
    }


    virtual ::Platform::Object^ GetObjectInstance() const override
    {
      return _instance;
    }

  private:
    
    PnpObjectUpdate(::Windows::Devices::Enumeration::Pnp::PnpObjectUpdate^ instance)
    {
      _instance = instance;
    }
    
    
    static v8::Handle<v8::Value> New(const v8::Arguments& args)
    {
      HandleScope scope;

      // in case the constructor was called without the new operator
      if (!s_constructorTemplate->HasInstance(args.This()))
      {
        if (args.Length() > 0)
        {
          std::unique_ptr<Handle<Value> []> constructorArgs(new Handle<Value>[args.Length()]);

          Handle<Value> *argsPtr = constructorArgs.get();
          for (int i = 0; i < args.Length(); i++)
          {
            argsPtr[i] = args[i];
          }

          return s_constructorTemplate->GetFunction()->CallAsConstructor(args.Length(), constructorArgs.get());
        }
        else
        {
          return s_constructorTemplate->GetFunction()->CallAsConstructor(args.Length(), nullptr);
        }
      }
      
      ::Windows::Devices::Enumeration::Pnp::PnpObjectUpdate^ winRtInstance;


      if (args.Length() == 1 && OpaqueWrapper::IsOpaqueWrapper(args[0]) &&
        NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObjectUpdate^>(args[0]))
      {
        try 
        {
          winRtInstance = (::Windows::Devices::Enumeration::Pnp::PnpObjectUpdate^) NodeRT::Utils::GetObjectInstance(args[0]);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return scope.Close(Undefined());
        }
      }
      else
      {
        ThrowException(Exception::Error(NodeRT::Utils::NewString(L"Invalid arguments, no suitable constructor found")));
        return scope.Close(Undefined());
      }

      args.This()->SetHiddenValue(String::NewSymbol("__winRtInstance__"), True());

      PnpObjectUpdate *wrapperInstance = new PnpObjectUpdate(winRtInstance);
      wrapperInstance->Wrap(args.This());

      return args.This();
    }


  



    static Handle<Value> IdGetter(Local<String> property, const AccessorInfo &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObjectUpdate^>(info.This()))
      {
        return scope.Close(Undefined());
      }

      PnpObjectUpdate *wrapper = PnpObjectUpdate::Unwrap<PnpObjectUpdate>(info.This());

      try 
      {
        Platform::String^ result = wrapper->_instance->Id;
        return scope.Close(NodeRT::Utils::NewString(result->Data()));
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return scope.Close(Undefined());
      }
    }
    
    static Handle<Value> PropertiesGetter(Local<String> property, const AccessorInfo &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObjectUpdate^>(info.This()))
      {
        return scope.Close(Undefined());
      }

      PnpObjectUpdate *wrapper = PnpObjectUpdate::Unwrap<PnpObjectUpdate>(info.This());

      try 
      {
        ::Windows::Foundation::Collections::IMapView<::Platform::String^, ::Platform::Object^>^ result = wrapper->_instance->Properties;
        return scope.Close(NodeRT::Collections::MapViewWrapper<::Platform::String^,::Platform::Object^>::CreateMapViewWrapper(result, 
            [](::Platform::String^ val) -> Handle<Value> {
              return NodeRT::Utils::NewString(val->Data());
            },
            [](Handle<Value> value) -> bool {
              return value->IsString();
            },
            [](Handle<Value> value) -> ::Platform::String^ {
              return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
            },
            [](::Platform::Object^ val) -> Handle<Value> {
              return CreateOpaqueWrapper(val);
            }
          ));
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return scope.Close(Undefined());
      }
    }
    
    static Handle<Value> TypeGetter(Local<String> property, const AccessorInfo &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObjectUpdate^>(info.This()))
      {
        return scope.Close(Undefined());
      }

      PnpObjectUpdate *wrapper = PnpObjectUpdate::Unwrap<PnpObjectUpdate>(info.This());

      try 
      {
        ::Windows::Devices::Enumeration::Pnp::PnpObjectType result = wrapper->_instance->Type;
        return scope.Close(Integer::New(static_cast<int>(result)));
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return scope.Close(Undefined());
      }
    }
    


  private:
    ::Windows::Devices::Enumeration::Pnp::PnpObjectUpdate^ _instance;
    static Persistent<FunctionTemplate> s_constructorTemplate;

    friend v8::Handle<v8::Value> WrapPnpObjectUpdate(::Windows::Devices::Enumeration::Pnp::PnpObjectUpdate^ wintRtInstance);
    friend ::Windows::Devices::Enumeration::Pnp::PnpObjectUpdate^ UnwrapPnpObjectUpdate(Handle<Value> value);
    friend bool IsPnpObjectUpdateWrapper(Handle<Value> value);
  };
  Persistent<FunctionTemplate> PnpObjectUpdate::s_constructorTemplate;

  v8::Handle<v8::Value> WrapPnpObjectUpdate(::Windows::Devices::Enumeration::Pnp::PnpObjectUpdate^ winRtInstance)
  {
    HandleScope scope;

    if (winRtInstance == nullptr)
    {
      return scope.Close(Undefined());
    }

    Handle<Object> opaqueWrapper = CreateOpaqueWrapper(winRtInstance);
    Handle<Value> args[] = {opaqueWrapper};
    return scope.Close(PnpObjectUpdate::s_constructorTemplate->GetFunction()->NewInstance(_countof(args), args));
  }

  ::Windows::Devices::Enumeration::Pnp::PnpObjectUpdate^ UnwrapPnpObjectUpdate(Handle<Value> value)
  {
     return PnpObjectUpdate::Unwrap<PnpObjectUpdate>(value.As<Object>())->_instance;
  }

  void InitPnpObjectUpdate(Handle<Object> exports)
  {
    PnpObjectUpdate::Init(exports);
  }

  class PnpObjectCollection : public WrapperBase
  {
  public:    
    static v8::Handle<v8::Value> Init(const Handle<Object> exports)
    {
      HandleScope scope;
      
      s_constructorTemplate = Persistent<FunctionTemplate>::New(FunctionTemplate::New(New));
      s_constructorTemplate->SetClassName(String::NewSymbol("PnpObjectCollection"));
      s_constructorTemplate->InstanceTemplate()->SetInternalFieldCount(1);
      
            
      s_constructorTemplate->PrototypeTemplate()->Set(String::NewSymbol("getAt"), FunctionTemplate::New(GetAt)->GetFunction());
      s_constructorTemplate->PrototypeTemplate()->Set(String::NewSymbol("indexOf"), FunctionTemplate::New(IndexOf)->GetFunction());
      s_constructorTemplate->PrototypeTemplate()->Set(String::NewSymbol("getMany"), FunctionTemplate::New(GetMany)->GetFunction());
      s_constructorTemplate->PrototypeTemplate()->Set(String::NewSymbol("first"), FunctionTemplate::New(First)->GetFunction());
      
                        
      Local<Function> constructor = s_constructorTemplate->GetFunction();


      exports->Set(String::NewSymbol("PnpObjectCollection"), constructor);
      return scope.Close(Undefined());
    }


    virtual ::Platform::Object^ GetObjectInstance() const override
    {
      return _instance;
    }

  private:
    
    PnpObjectCollection(::Windows::Devices::Enumeration::Pnp::PnpObjectCollection^ instance)
    {
      _instance = instance;
    }
    
    
    static v8::Handle<v8::Value> New(const v8::Arguments& args)
    {
      HandleScope scope;

      // in case the constructor was called without the new operator
      if (!s_constructorTemplate->HasInstance(args.This()))
      {
        if (args.Length() > 0)
        {
          std::unique_ptr<Handle<Value> []> constructorArgs(new Handle<Value>[args.Length()]);

          Handle<Value> *argsPtr = constructorArgs.get();
          for (int i = 0; i < args.Length(); i++)
          {
            argsPtr[i] = args[i];
          }

          return s_constructorTemplate->GetFunction()->CallAsConstructor(args.Length(), constructorArgs.get());
        }
        else
        {
          return s_constructorTemplate->GetFunction()->CallAsConstructor(args.Length(), nullptr);
        }
      }
      
      ::Windows::Devices::Enumeration::Pnp::PnpObjectCollection^ winRtInstance;


      if (args.Length() == 1 && OpaqueWrapper::IsOpaqueWrapper(args[0]) &&
        NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObjectCollection^>(args[0]))
      {
        try 
        {
          winRtInstance = (::Windows::Devices::Enumeration::Pnp::PnpObjectCollection^) NodeRT::Utils::GetObjectInstance(args[0]);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return scope.Close(Undefined());
        }
      }
      else
      {
        ThrowException(Exception::Error(NodeRT::Utils::NewString(L"Invalid arguments, no suitable constructor found")));
        return scope.Close(Undefined());
      }

      args.This()->SetHiddenValue(String::NewSymbol("__winRtInstance__"), True());

      PnpObjectCollection *wrapperInstance = new PnpObjectCollection(winRtInstance);
      wrapperInstance->Wrap(args.This());

      return args.This();
    }


  
    static Handle<Value> GetAt(const v8::Arguments& args)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObjectCollection^>(args.This()))
      {
        return scope.Close(Undefined());
      }

      PnpObjectCollection *wrapper = PnpObjectCollection::Unwrap<PnpObjectCollection>(args.This());

      if (args.Length() == 1
        && args[0]->IsUint32())
      {
        try
        {
          unsigned int arg0 = static_cast<unsigned int>(args[0]->IntegerValue());
          
          ::Windows::Devices::Enumeration::Pnp::PnpObject^ result;
          result = wrapper->_instance->GetAt(arg0);
          return scope.Close(WrapPnpObject(result));
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return scope.Close(Undefined());
        }
      }
      else 
      {
        ThrowException(Exception::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return scope.Close(Undefined());
      }

      return scope.Close(Undefined());
    }
    static Handle<Value> IndexOf(const v8::Arguments& args)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObjectCollection^>(args.This()))
      {
        return scope.Close(Undefined());
      }

      PnpObjectCollection *wrapper = PnpObjectCollection::Unwrap<PnpObjectCollection>(args.This());

      if (args.Length() == 1
        && NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObject^>(args[0]))
      {
        try
        {
          ::Windows::Devices::Enumeration::Pnp::PnpObject^ arg0 = UnwrapPnpObject(args[0]);
          unsigned int arg1;
          
          bool result;
          result = wrapper->_instance->IndexOf(arg0, &arg1);
          Handle<Object> resObj = Object::New();
          resObj->Set(String::NewSymbol("boolean"), Boolean::New(result));
          resObj->Set(String::NewSymbol("index"), Integer::New(arg1));
          return scope.Close(resObj);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return scope.Close(Undefined());
        }
      }
      else 
      {
        ThrowException(Exception::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return scope.Close(Undefined());
      }

      return scope.Close(Undefined());
    }
    static Handle<Value> GetMany(const v8::Arguments& args)
    {
      HandleScope scope;
      ThrowException(Exception::Error(NodeRT::Utils::NewString(L"Not implemented")));
      return scope.Close(Undefined());
    }
    static Handle<Value> First(const v8::Arguments& args)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObjectCollection^>(args.This()))
      {
        return scope.Close(Undefined());
      }

      PnpObjectCollection *wrapper = PnpObjectCollection::Unwrap<PnpObjectCollection>(args.This());

      if (args.Length() == 0)
      {
        try
        {
          ::Windows::Foundation::Collections::IIterator<::Windows::Devices::Enumeration::Pnp::PnpObject^>^ result;
          result = wrapper->_instance->First();
          return scope.Close(NodeRT::Collections::IteratorWrapper<::Windows::Devices::Enumeration::Pnp::PnpObject^>::CreateIteratorWrapper(result, 
            [](::Windows::Devices::Enumeration::Pnp::PnpObject^ val) -> Handle<Value> {
              return WrapPnpObject(val);
            }
          ));
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return scope.Close(Undefined());
        }
      }
      else 
      {
        ThrowException(Exception::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return scope.Close(Undefined());
      }

      return scope.Close(Undefined());
    }





  private:
    ::Windows::Devices::Enumeration::Pnp::PnpObjectCollection^ _instance;
    static Persistent<FunctionTemplate> s_constructorTemplate;

    friend v8::Handle<v8::Value> WrapPnpObjectCollection(::Windows::Devices::Enumeration::Pnp::PnpObjectCollection^ wintRtInstance);
    friend ::Windows::Devices::Enumeration::Pnp::PnpObjectCollection^ UnwrapPnpObjectCollection(Handle<Value> value);
    friend bool IsPnpObjectCollectionWrapper(Handle<Value> value);
  };
  Persistent<FunctionTemplate> PnpObjectCollection::s_constructorTemplate;

  v8::Handle<v8::Value> WrapPnpObjectCollection(::Windows::Devices::Enumeration::Pnp::PnpObjectCollection^ winRtInstance)
  {
    HandleScope scope;

    if (winRtInstance == nullptr)
    {
      return scope.Close(Undefined());
    }

    Handle<Object> opaqueWrapper = CreateOpaqueWrapper(winRtInstance);
    Handle<Value> args[] = {opaqueWrapper};
    return scope.Close(PnpObjectCollection::s_constructorTemplate->GetFunction()->NewInstance(_countof(args), args));
  }

  ::Windows::Devices::Enumeration::Pnp::PnpObjectCollection^ UnwrapPnpObjectCollection(Handle<Value> value)
  {
     return PnpObjectCollection::Unwrap<PnpObjectCollection>(value.As<Object>())->_instance;
  }

  void InitPnpObjectCollection(Handle<Object> exports)
  {
    PnpObjectCollection::Init(exports);
  }

  class PnpObjectWatcher : public WrapperBase
  {
  public:    
    static v8::Handle<v8::Value> Init(const Handle<Object> exports)
    {
      HandleScope scope;
      
      s_constructorTemplate = Persistent<FunctionTemplate>::New(FunctionTemplate::New(New));
      s_constructorTemplate->SetClassName(String::NewSymbol("PnpObjectWatcher"));
      s_constructorTemplate->InstanceTemplate()->SetInternalFieldCount(1);
      
            
      s_constructorTemplate->PrototypeTemplate()->Set(String::NewSymbol("start"), FunctionTemplate::New(Start)->GetFunction());
      s_constructorTemplate->PrototypeTemplate()->Set(String::NewSymbol("stop"), FunctionTemplate::New(Stop)->GetFunction());
      
                  
      Local<Function> addListenerFunc = FunctionTemplate::New(AddListener)->GetFunction();
      s_constructorTemplate->PrototypeTemplate()->Set(String::NewSymbol("addListener"), addListenerFunc);
      s_constructorTemplate->PrototypeTemplate()->Set(String::NewSymbol("on"), addListenerFunc);
      Local<Function> removeListenerFunc = FunctionTemplate::New(RemoveListener)->GetFunction();
      s_constructorTemplate->PrototypeTemplate()->Set(String::NewSymbol("removeListener"), removeListenerFunc);
      s_constructorTemplate->PrototypeTemplate()->Set(String::NewSymbol("off"), removeListenerFunc);
            
      s_constructorTemplate->PrototypeTemplate()->SetAccessor(String::NewSymbol("status"), StatusGetter);
      
      Local<Function> constructor = s_constructorTemplate->GetFunction();


      exports->Set(String::NewSymbol("PnpObjectWatcher"), constructor);
      return scope.Close(Undefined());
    }


    virtual ::Platform::Object^ GetObjectInstance() const override
    {
      return _instance;
    }

  private:
    
    PnpObjectWatcher(::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^ instance)
    {
      _instance = instance;
    }
    
    
    static v8::Handle<v8::Value> New(const v8::Arguments& args)
    {
      HandleScope scope;

      // in case the constructor was called without the new operator
      if (!s_constructorTemplate->HasInstance(args.This()))
      {
        if (args.Length() > 0)
        {
          std::unique_ptr<Handle<Value> []> constructorArgs(new Handle<Value>[args.Length()]);

          Handle<Value> *argsPtr = constructorArgs.get();
          for (int i = 0; i < args.Length(); i++)
          {
            argsPtr[i] = args[i];
          }

          return s_constructorTemplate->GetFunction()->CallAsConstructor(args.Length(), constructorArgs.get());
        }
        else
        {
          return s_constructorTemplate->GetFunction()->CallAsConstructor(args.Length(), nullptr);
        }
      }
      
      ::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^ winRtInstance;


      if (args.Length() == 1 && OpaqueWrapper::IsOpaqueWrapper(args[0]) &&
        NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^>(args[0]))
      {
        try 
        {
          winRtInstance = (::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^) NodeRT::Utils::GetObjectInstance(args[0]);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return scope.Close(Undefined());
        }
      }
      else
      {
        ThrowException(Exception::Error(NodeRT::Utils::NewString(L"Invalid arguments, no suitable constructor found")));
        return scope.Close(Undefined());
      }

      args.This()->SetHiddenValue(String::NewSymbol("__winRtInstance__"), True());

      PnpObjectWatcher *wrapperInstance = new PnpObjectWatcher(winRtInstance);
      wrapperInstance->Wrap(args.This());

      return args.This();
    }


  
    static Handle<Value> Start(const v8::Arguments& args)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^>(args.This()))
      {
        return scope.Close(Undefined());
      }

      PnpObjectWatcher *wrapper = PnpObjectWatcher::Unwrap<PnpObjectWatcher>(args.This());

      if (args.Length() == 0)
      {
        try
        {
          wrapper->_instance->Start();
          return scope.Close(Undefined());   
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return scope.Close(Undefined());
        }
      }
      else 
      {
        ThrowException(Exception::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return scope.Close(Undefined());
      }

      return scope.Close(Undefined());
    }
    static Handle<Value> Stop(const v8::Arguments& args)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^>(args.This()))
      {
        return scope.Close(Undefined());
      }

      PnpObjectWatcher *wrapper = PnpObjectWatcher::Unwrap<PnpObjectWatcher>(args.This());

      if (args.Length() == 0)
      {
        try
        {
          wrapper->_instance->Stop();
          return scope.Close(Undefined());   
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return scope.Close(Undefined());
        }
      }
      else 
      {
        ThrowException(Exception::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return scope.Close(Undefined());
      }

      return scope.Close(Undefined());
    }



    static Handle<Value> StatusGetter(Local<String> property, const AccessorInfo &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^>(info.This()))
      {
        return scope.Close(Undefined());
      }

      PnpObjectWatcher *wrapper = PnpObjectWatcher::Unwrap<PnpObjectWatcher>(info.This());

      try 
      {
        ::Windows::Devices::Enumeration::DeviceWatcherStatus result = wrapper->_instance->Status;
        return scope.Close(Integer::New(static_cast<int>(result)));
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return scope.Close(Undefined());
      }
    }
    


    static v8::Handle<v8::Value> AddListener(const v8::Arguments& args)
    {
      HandleScope scope;

      if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsFunction())
      {
        ThrowException(Exception::Error(NodeRT::Utils::NewString(L"wrong arguments, expected arguments are eventName(string),callback(function)")));
        return scope.Close(Undefined());
      }

      String::Value eventName(args[0]);
      auto str = *eventName;
      
      Local<Function> callback = args[1].As<Function>();
      
      ::Windows::Foundation::EventRegistrationToken registrationToken;
      if (NodeRT::Utils::CaseInsenstiveEquals(L"added", str))
      {
        if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^>(args.This()))
        {
          ThrowException(Exception::Error(NodeRT::Utils::NewString(L"The caller of this method isn't of the expected type or internal WinRt object was disposed")));
          return scope.Close(Undefined());
        }
        PnpObjectWatcher *wrapper = PnpObjectWatcher::Unwrap<PnpObjectWatcher>(args.This());
      
        try
        {
          std::shared_ptr<Persistent<Object>> callbackObjPtr(new Persistent<Object>(Persistent<Object>::New(NodeRT::Utils::CreateCallbackObjectInDomain(callback))), 
            [] (Persistent<Object> *ptr ) {
              NodeUtils::Async::RunOnMain([ptr]() {
                ptr->Dispose();
                delete ptr;
            });
          });

          registrationToken = wrapper->_instance->Added::add(
            ref new ::Windows::Foundation::TypedEventHandler<::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^, ::Windows::Devices::Enumeration::Pnp::PnpObject^>(
            [callbackObjPtr](::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^ arg0, ::Windows::Devices::Enumeration::Pnp::PnpObject^ arg1) {
              NodeUtils::Async::RunOnMain([callbackObjPtr , arg0, arg1]() {
                TryCatch tryCatch;
              
                Handle<Value> error;

                Handle<Value> wrappedArg0 = WrapPnpObjectWatcher(arg0);
                Handle<Value> wrappedArg1 = WrapPnpObject(arg1);

                if (tryCatch.HasCaught())
                {
                  error = tryCatch.Exception()->ToObject();
                }
                else 
                {
                  error = Undefined();
                }


                if (wrappedArg0.IsEmpty()) wrappedArg0 = Undefined();
                if (wrappedArg1.IsEmpty()) wrappedArg1 = Undefined();

                Handle<Value> args[] = { wrappedArg0, wrappedArg1 };
                NodeRT::Utils::CallCallbackInDomain(*callbackObjPtr, _countof(args), args);
              });
            })
          );
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return scope.Close(Undefined());
        }

      }
      else if (NodeRT::Utils::CaseInsenstiveEquals(L"enumerationCompleted", str))
      {
        if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^>(args.This()))
        {
          ThrowException(Exception::Error(NodeRT::Utils::NewString(L"The caller of this method isn't of the expected type or internal WinRt object was disposed")));
          return scope.Close(Undefined());
        }
        PnpObjectWatcher *wrapper = PnpObjectWatcher::Unwrap<PnpObjectWatcher>(args.This());
      
        try
        {
          std::shared_ptr<Persistent<Object>> callbackObjPtr(new Persistent<Object>(Persistent<Object>::New(NodeRT::Utils::CreateCallbackObjectInDomain(callback))), 
            [] (Persistent<Object> *ptr ) {
              NodeUtils::Async::RunOnMain([ptr]() {
                ptr->Dispose();
                delete ptr;
            });
          });

          registrationToken = wrapper->_instance->EnumerationCompleted::add(
            ref new ::Windows::Foundation::TypedEventHandler<::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^, ::Platform::Object^>(
            [callbackObjPtr](::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^ arg0, ::Platform::Object^ arg1) {
              NodeUtils::Async::RunOnMain([callbackObjPtr , arg0, arg1]() {
                TryCatch tryCatch;
              
                Handle<Value> error;

                Handle<Value> wrappedArg0 = WrapPnpObjectWatcher(arg0);
                Handle<Value> wrappedArg1 = CreateOpaqueWrapper(arg1);

                if (tryCatch.HasCaught())
                {
                  error = tryCatch.Exception()->ToObject();
                }
                else 
                {
                  error = Undefined();
                }


                if (wrappedArg0.IsEmpty()) wrappedArg0 = Undefined();
                if (wrappedArg1.IsEmpty()) wrappedArg1 = Undefined();

                Handle<Value> args[] = { wrappedArg0, wrappedArg1 };
                NodeRT::Utils::CallCallbackInDomain(*callbackObjPtr, _countof(args), args);
              });
            })
          );
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return scope.Close(Undefined());
        }

      }
      else if (NodeRT::Utils::CaseInsenstiveEquals(L"removed", str))
      {
        if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^>(args.This()))
        {
          ThrowException(Exception::Error(NodeRT::Utils::NewString(L"The caller of this method isn't of the expected type or internal WinRt object was disposed")));
          return scope.Close(Undefined());
        }
        PnpObjectWatcher *wrapper = PnpObjectWatcher::Unwrap<PnpObjectWatcher>(args.This());
      
        try
        {
          std::shared_ptr<Persistent<Object>> callbackObjPtr(new Persistent<Object>(Persistent<Object>::New(NodeRT::Utils::CreateCallbackObjectInDomain(callback))), 
            [] (Persistent<Object> *ptr ) {
              NodeUtils::Async::RunOnMain([ptr]() {
                ptr->Dispose();
                delete ptr;
            });
          });

          registrationToken = wrapper->_instance->Removed::add(
            ref new ::Windows::Foundation::TypedEventHandler<::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^, ::Windows::Devices::Enumeration::Pnp::PnpObjectUpdate^>(
            [callbackObjPtr](::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^ arg0, ::Windows::Devices::Enumeration::Pnp::PnpObjectUpdate^ arg1) {
              NodeUtils::Async::RunOnMain([callbackObjPtr , arg0, arg1]() {
                TryCatch tryCatch;
              
                Handle<Value> error;

                Handle<Value> wrappedArg0 = WrapPnpObjectWatcher(arg0);
                Handle<Value> wrappedArg1 = WrapPnpObjectUpdate(arg1);

                if (tryCatch.HasCaught())
                {
                  error = tryCatch.Exception()->ToObject();
                }
                else 
                {
                  error = Undefined();
                }


                if (wrappedArg0.IsEmpty()) wrappedArg0 = Undefined();
                if (wrappedArg1.IsEmpty()) wrappedArg1 = Undefined();

                Handle<Value> args[] = { wrappedArg0, wrappedArg1 };
                NodeRT::Utils::CallCallbackInDomain(*callbackObjPtr, _countof(args), args);
              });
            })
          );
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return scope.Close(Undefined());
        }

      }
      else if (NodeRT::Utils::CaseInsenstiveEquals(L"stopped", str))
      {
        if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^>(args.This()))
        {
          ThrowException(Exception::Error(NodeRT::Utils::NewString(L"The caller of this method isn't of the expected type or internal WinRt object was disposed")));
          return scope.Close(Undefined());
        }
        PnpObjectWatcher *wrapper = PnpObjectWatcher::Unwrap<PnpObjectWatcher>(args.This());
      
        try
        {
          std::shared_ptr<Persistent<Object>> callbackObjPtr(new Persistent<Object>(Persistent<Object>::New(NodeRT::Utils::CreateCallbackObjectInDomain(callback))), 
            [] (Persistent<Object> *ptr ) {
              NodeUtils::Async::RunOnMain([ptr]() {
                ptr->Dispose();
                delete ptr;
            });
          });

          registrationToken = wrapper->_instance->Stopped::add(
            ref new ::Windows::Foundation::TypedEventHandler<::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^, ::Platform::Object^>(
            [callbackObjPtr](::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^ arg0, ::Platform::Object^ arg1) {
              NodeUtils::Async::RunOnMain([callbackObjPtr , arg0, arg1]() {
                TryCatch tryCatch;
              
                Handle<Value> error;

                Handle<Value> wrappedArg0 = WrapPnpObjectWatcher(arg0);
                Handle<Value> wrappedArg1 = CreateOpaqueWrapper(arg1);

                if (tryCatch.HasCaught())
                {
                  error = tryCatch.Exception()->ToObject();
                }
                else 
                {
                  error = Undefined();
                }


                if (wrappedArg0.IsEmpty()) wrappedArg0 = Undefined();
                if (wrappedArg1.IsEmpty()) wrappedArg1 = Undefined();

                Handle<Value> args[] = { wrappedArg0, wrappedArg1 };
                NodeRT::Utils::CallCallbackInDomain(*callbackObjPtr, _countof(args), args);
              });
            })
          );
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return scope.Close(Undefined());
        }

      }
      else if (NodeRT::Utils::CaseInsenstiveEquals(L"updated", str))
      {
        if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^>(args.This()))
        {
          ThrowException(Exception::Error(NodeRT::Utils::NewString(L"The caller of this method isn't of the expected type or internal WinRt object was disposed")));
          return scope.Close(Undefined());
        }
        PnpObjectWatcher *wrapper = PnpObjectWatcher::Unwrap<PnpObjectWatcher>(args.This());
      
        try
        {
          std::shared_ptr<Persistent<Object>> callbackObjPtr(new Persistent<Object>(Persistent<Object>::New(NodeRT::Utils::CreateCallbackObjectInDomain(callback))), 
            [] (Persistent<Object> *ptr ) {
              NodeUtils::Async::RunOnMain([ptr]() {
                ptr->Dispose();
                delete ptr;
            });
          });

          registrationToken = wrapper->_instance->Updated::add(
            ref new ::Windows::Foundation::TypedEventHandler<::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^, ::Windows::Devices::Enumeration::Pnp::PnpObjectUpdate^>(
            [callbackObjPtr](::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^ arg0, ::Windows::Devices::Enumeration::Pnp::PnpObjectUpdate^ arg1) {
              NodeUtils::Async::RunOnMain([callbackObjPtr , arg0, arg1]() {
                TryCatch tryCatch;
              
                Handle<Value> error;

                Handle<Value> wrappedArg0 = WrapPnpObjectWatcher(arg0);
                Handle<Value> wrappedArg1 = WrapPnpObjectUpdate(arg1);

                if (tryCatch.HasCaught())
                {
                  error = tryCatch.Exception()->ToObject();
                }
                else 
                {
                  error = Undefined();
                }


                if (wrappedArg0.IsEmpty()) wrappedArg0 = Undefined();
                if (wrappedArg1.IsEmpty()) wrappedArg1 = Undefined();

                Handle<Value> args[] = { wrappedArg0, wrappedArg1 };
                NodeRT::Utils::CallCallbackInDomain(*callbackObjPtr, _countof(args), args);
              });
            })
          );
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return scope.Close(Undefined());
        }

      }
      else 
      {
        ThrowException(Exception::Error(String::Concat(NodeRT::Utils::NewString(L"given event name isn't supported: "), args[0].As<String>())));
        return scope.Close(Undefined());
      }

      Local<Value> tokenMap = callback->GetHiddenValue(String::NewSymbol(REGISTRATION_TOKEN_MAP_PROPERTY_NAME));
                
      if (tokenMap.IsEmpty() || tokenMap->Equals(Undefined()))
      {
          tokenMap = Object::New();
          callback->SetHiddenValue(String::NewSymbol(REGISTRATION_TOKEN_MAP_PROPERTY_NAME), tokenMap);
      }

      tokenMap.As<Object>()->Set(args[1], CreateOpaqueWrapper(::Windows::Foundation::PropertyValue::CreateInt64(registrationToken.Value)));
                
      return scope.Close(Undefined());
    }

    static v8::Handle<v8::Value> RemoveListener(const v8::Arguments& args)
    {
      HandleScope scope;

      if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsFunction())
      {
        ThrowException(Exception::Error(NodeRT::Utils::NewString(L"wrong arguments, expected a string and a callback")));
        return scope.Close(Undefined());
      }

      String::Value eventName(args[0]);
      auto str = *eventName;

      if ((NodeRT::Utils::CaseInsenstiveEquals(L"added", str)) &&(NodeRT::Utils::CaseInsenstiveEquals(L"enumerationCompleted", str)) &&(NodeRT::Utils::CaseInsenstiveEquals(L"removed", str)) &&(NodeRT::Utils::CaseInsenstiveEquals(L"stopped", str)) &&(NodeRT::Utils::CaseInsenstiveEquals(L"updated", str)))
      {
        ThrowException(Exception::Error(String::Concat(NodeRT::Utils::NewString(L"given event name isn't supported: "), args[0].As<String>())));
        return scope.Close(Undefined());
      }

      Local<Function> callback = args[1].As<Function>();
      Handle<Value> tokenMap = callback->GetHiddenValue(String::NewSymbol(REGISTRATION_TOKEN_MAP_PROPERTY_NAME));
                
      if (tokenMap.IsEmpty() || tokenMap->Equals(Undefined()))
      {
        return scope.Close(Undefined());
      }

      Handle<Value> opaqueWrapperObj =  tokenMap.As<Object>()->Get(args[1]);

      if (opaqueWrapperObj.IsEmpty() || opaqueWrapperObj->Equals(Undefined()))
      {
        return scope.Close(Undefined());
      }

      OpaqueWrapper *opaqueWrapper = OpaqueWrapper::Unwrap<OpaqueWrapper>(opaqueWrapperObj.As<Object>());
            
      long long tokenValue = (long long) opaqueWrapper->GetObjectInstance();
      ::Windows::Foundation::EventRegistrationToken registrationToken;
      registrationToken.Value = tokenValue;
        
      try 
      {
        if (NodeRT::Utils::CaseInsenstiveEquals(L"added", str))
        {
          if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^>(args.This()))
          {
            ThrowException(Exception::Error(NodeRT::Utils::NewString(L"The caller of this method isn't of the expected type or internal WinRt object was disposed")));
            return scope.Close(Undefined());
          }
          PnpObjectWatcher *wrapper = PnpObjectWatcher::Unwrap<PnpObjectWatcher>(args.This());
          wrapper->_instance->Added::remove(registrationToken);
        }
        else if (NodeRT::Utils::CaseInsenstiveEquals(L"enumerationCompleted", str))
        {
          if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^>(args.This()))
          {
            ThrowException(Exception::Error(NodeRT::Utils::NewString(L"The caller of this method isn't of the expected type or internal WinRt object was disposed")));
            return scope.Close(Undefined());
          }
          PnpObjectWatcher *wrapper = PnpObjectWatcher::Unwrap<PnpObjectWatcher>(args.This());
          wrapper->_instance->EnumerationCompleted::remove(registrationToken);
        }
        else if (NodeRT::Utils::CaseInsenstiveEquals(L"removed", str))
        {
          if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^>(args.This()))
          {
            ThrowException(Exception::Error(NodeRT::Utils::NewString(L"The caller of this method isn't of the expected type or internal WinRt object was disposed")));
            return scope.Close(Undefined());
          }
          PnpObjectWatcher *wrapper = PnpObjectWatcher::Unwrap<PnpObjectWatcher>(args.This());
          wrapper->_instance->Removed::remove(registrationToken);
        }
        else if (NodeRT::Utils::CaseInsenstiveEquals(L"stopped", str))
        {
          if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^>(args.This()))
          {
            ThrowException(Exception::Error(NodeRT::Utils::NewString(L"The caller of this method isn't of the expected type or internal WinRt object was disposed")));
            return scope.Close(Undefined());
          }
          PnpObjectWatcher *wrapper = PnpObjectWatcher::Unwrap<PnpObjectWatcher>(args.This());
          wrapper->_instance->Stopped::remove(registrationToken);
        }
        else if (NodeRT::Utils::CaseInsenstiveEquals(L"updated", str))
        {
          if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^>(args.This()))
          {
            ThrowException(Exception::Error(NodeRT::Utils::NewString(L"The caller of this method isn't of the expected type or internal WinRt object was disposed")));
            return scope.Close(Undefined());
          }
          PnpObjectWatcher *wrapper = PnpObjectWatcher::Unwrap<PnpObjectWatcher>(args.This());
          wrapper->_instance->Updated::remove(registrationToken);
        }
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
      }

      tokenMap.As<Object>()->Delete(args[0].As<String>());

      return scope.Close(Undefined());
    }
  private:
    ::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^ _instance;
    static Persistent<FunctionTemplate> s_constructorTemplate;

    friend v8::Handle<v8::Value> WrapPnpObjectWatcher(::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^ wintRtInstance);
    friend ::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^ UnwrapPnpObjectWatcher(Handle<Value> value);
    friend bool IsPnpObjectWatcherWrapper(Handle<Value> value);
  };
  Persistent<FunctionTemplate> PnpObjectWatcher::s_constructorTemplate;

  v8::Handle<v8::Value> WrapPnpObjectWatcher(::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^ winRtInstance)
  {
    HandleScope scope;

    if (winRtInstance == nullptr)
    {
      return scope.Close(Undefined());
    }

    Handle<Object> opaqueWrapper = CreateOpaqueWrapper(winRtInstance);
    Handle<Value> args[] = {opaqueWrapper};
    return scope.Close(PnpObjectWatcher::s_constructorTemplate->GetFunction()->NewInstance(_countof(args), args));
  }

  ::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^ UnwrapPnpObjectWatcher(Handle<Value> value)
  {
     return PnpObjectWatcher::Unwrap<PnpObjectWatcher>(value.As<Object>())->_instance;
  }

  void InitPnpObjectWatcher(Handle<Object> exports)
  {
    PnpObjectWatcher::Init(exports);
  }

  class PnpObject : public WrapperBase
  {
  public:    
    static v8::Handle<v8::Value> Init(const Handle<Object> exports)
    {
      HandleScope scope;
      
      s_constructorTemplate = Persistent<FunctionTemplate>::New(FunctionTemplate::New(New));
      s_constructorTemplate->SetClassName(String::NewSymbol("PnpObject"));
      s_constructorTemplate->InstanceTemplate()->SetInternalFieldCount(1);
      
      Handle<Value> asyncSymbol = String::NewSymbol("__winRtAsync__");
      Handle<Function> func;
            
      s_constructorTemplate->PrototypeTemplate()->Set(String::NewSymbol("update"), FunctionTemplate::New(Update)->GetFunction());
      
                        
      s_constructorTemplate->PrototypeTemplate()->SetAccessor(String::NewSymbol("id"), IdGetter);
      s_constructorTemplate->PrototypeTemplate()->SetAccessor(String::NewSymbol("properties"), PropertiesGetter);
      s_constructorTemplate->PrototypeTemplate()->SetAccessor(String::NewSymbol("type"), TypeGetter);
      
      Local<Function> constructor = s_constructorTemplate->GetFunction();

      constructor->Set(String::NewSymbol("createWatcher"), FunctionTemplate::New(CreateWatcher)->GetFunction());
      func = FunctionTemplate::New(CreateFromIdAsync)->GetFunction();
      func->Set(asyncSymbol, True(), PropertyAttribute::DontEnum);
      constructor->Set(String::NewSymbol("createFromIdAsync"), func);
      func = FunctionTemplate::New(FindAllAsync)->GetFunction();
      func->Set(asyncSymbol, True(), PropertyAttribute::DontEnum);
      constructor->Set(String::NewSymbol("findAllAsync"), func);

      exports->Set(String::NewSymbol("PnpObject"), constructor);
      return scope.Close(Undefined());
    }


    virtual ::Platform::Object^ GetObjectInstance() const override
    {
      return _instance;
    }

  private:
    
    PnpObject(::Windows::Devices::Enumeration::Pnp::PnpObject^ instance)
    {
      _instance = instance;
    }
    
    
    static v8::Handle<v8::Value> New(const v8::Arguments& args)
    {
      HandleScope scope;

      // in case the constructor was called without the new operator
      if (!s_constructorTemplate->HasInstance(args.This()))
      {
        if (args.Length() > 0)
        {
          std::unique_ptr<Handle<Value> []> constructorArgs(new Handle<Value>[args.Length()]);

          Handle<Value> *argsPtr = constructorArgs.get();
          for (int i = 0; i < args.Length(); i++)
          {
            argsPtr[i] = args[i];
          }

          return s_constructorTemplate->GetFunction()->CallAsConstructor(args.Length(), constructorArgs.get());
        }
        else
        {
          return s_constructorTemplate->GetFunction()->CallAsConstructor(args.Length(), nullptr);
        }
      }
      
      ::Windows::Devices::Enumeration::Pnp::PnpObject^ winRtInstance;


      if (args.Length() == 1 && OpaqueWrapper::IsOpaqueWrapper(args[0]) &&
        NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObject^>(args[0]))
      {
        try 
        {
          winRtInstance = (::Windows::Devices::Enumeration::Pnp::PnpObject^) NodeRT::Utils::GetObjectInstance(args[0]);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return scope.Close(Undefined());
        }
      }
      else
      {
        ThrowException(Exception::Error(NodeRT::Utils::NewString(L"Invalid arguments, no suitable constructor found")));
        return scope.Close(Undefined());
      }

      args.This()->SetHiddenValue(String::NewSymbol("__winRtInstance__"), True());

      PnpObject *wrapperInstance = new PnpObject(winRtInstance);
      wrapperInstance->Wrap(args.This());

      return args.This();
    }


  
    static Handle<Value> Update(const v8::Arguments& args)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObject^>(args.This()))
      {
        return scope.Close(Undefined());
      }

      PnpObject *wrapper = PnpObject::Unwrap<PnpObject>(args.This());

      if (args.Length() == 1
        && NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObjectUpdate^>(args[0]))
      {
        try
        {
          ::Windows::Devices::Enumeration::Pnp::PnpObjectUpdate^ arg0 = UnwrapPnpObjectUpdate(args[0]);
          
          wrapper->_instance->Update(arg0);
          return scope.Close(Undefined());   
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return scope.Close(Undefined());
        }
      }
      else 
      {
        ThrowException(Exception::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return scope.Close(Undefined());
      }

      return scope.Close(Undefined());
    }

    static Handle<Value> CreateFromIdAsync(const v8::Arguments& args)
    {
      HandleScope scope;

      if (args.Length() == 0 || !args[args.Length() -1]->IsFunction())
      {
          ThrowException(Exception::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return scope.Close(Undefined());
      }

      ::Windows::Foundation::IAsyncOperation<::Windows::Devices::Enumeration::Pnp::PnpObject^>^ op;
      

      if (args.Length() == 4
        && args[0]->IsInt32()
        && args[1]->IsString()
        && (NodeRT::Utils::IsWinRtWrapperOf<::Windows::Foundation::Collections::IIterable<::Platform::String^>^>(args[2]) || args[2]->IsArray()))
      {
        try
        {
          ::Windows::Devices::Enumeration::Pnp::PnpObjectType arg0 = static_cast<::Windows::Devices::Enumeration::Pnp::PnpObjectType>(args[0]->Int32Value());
          Platform::String^ arg1 = ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(args[1])));
          ::Windows::Foundation::Collections::IIterable<::Platform::String^>^ arg2 = 
            [] (v8::Handle<v8::Value> value) -> ::Windows::Foundation::Collections::IIterable<::Platform::String^>^
            {
              if (value->IsArray())
              {
                return NodeRT::Collections::JsArrayToWinrtVector<::Platform::String^>(value.As<Array>(), 
                 [](Handle<Value> value) -> bool {
                   return (!NodeRT::Utils::IsWinRtWrapper(value));
                 },
                 [](Handle<Value> value) -> ::Platform::String^ {
                   return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
                 }
                );
              }
              else
              {
                return dynamic_cast<::Windows::Foundation::Collections::IIterable<::Platform::String^>^>(NodeRT::Utils::GetObjectInstance(value));
              }
            } (args[2]);
          
          op = ::Windows::Devices::Enumeration::Pnp::PnpObject::CreateFromIdAsync(arg0,arg1,arg2);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return scope.Close(Undefined());
        }
      }
      else 
      {
        ThrowException(Exception::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return scope.Close(Undefined());
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(args[args.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<::Windows::Devices::Enumeration::Pnp::PnpObject^> t) 
      {	
        try
        {
          auto result = t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [result](NodeUtils::InvokeCallbackDelegate invokeCallback) {

            
            TryCatch tryCatch;
            Handle<Value> error; 
            Handle<Value> arg1 = WrapPnpObject(result);
            if (tryCatch.HasCaught())
            {
              error = tryCatch.Exception()->ToObject();
            }
            else 
            {
              error = Undefined();
            }
            if (arg1.IsEmpty()) arg1 = Undefined();
            Handle<Value> args[] = {error, arg1};

            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
          
            Handle<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Handle<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });

      return scope.Close(Undefined());
    }
    static Handle<Value> FindAllAsync(const v8::Arguments& args)
    {
      HandleScope scope;

      if (args.Length() == 0 || !args[args.Length() -1]->IsFunction())
      {
          ThrowException(Exception::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return scope.Close(Undefined());
      }

      ::Windows::Foundation::IAsyncOperation<::Windows::Devices::Enumeration::Pnp::PnpObjectCollection^>^ op;
      

      if (args.Length() == 3
        && args[0]->IsInt32()
        && (NodeRT::Utils::IsWinRtWrapperOf<::Windows::Foundation::Collections::IIterable<::Platform::String^>^>(args[1]) || args[1]->IsArray()))
      {
        try
        {
          ::Windows::Devices::Enumeration::Pnp::PnpObjectType arg0 = static_cast<::Windows::Devices::Enumeration::Pnp::PnpObjectType>(args[0]->Int32Value());
          ::Windows::Foundation::Collections::IIterable<::Platform::String^>^ arg1 = 
            [] (v8::Handle<v8::Value> value) -> ::Windows::Foundation::Collections::IIterable<::Platform::String^>^
            {
              if (value->IsArray())
              {
                return NodeRT::Collections::JsArrayToWinrtVector<::Platform::String^>(value.As<Array>(), 
                 [](Handle<Value> value) -> bool {
                   return (!NodeRT::Utils::IsWinRtWrapper(value));
                 },
                 [](Handle<Value> value) -> ::Platform::String^ {
                   return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
                 }
                );
              }
              else
              {
                return dynamic_cast<::Windows::Foundation::Collections::IIterable<::Platform::String^>^>(NodeRT::Utils::GetObjectInstance(value));
              }
            } (args[1]);
          
          op = ::Windows::Devices::Enumeration::Pnp::PnpObject::FindAllAsync(arg0,arg1);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return scope.Close(Undefined());
        }
      }
      else if (args.Length() == 4
        && args[0]->IsInt32()
        && (NodeRT::Utils::IsWinRtWrapperOf<::Windows::Foundation::Collections::IIterable<::Platform::String^>^>(args[1]) || args[1]->IsArray())
        && args[2]->IsString())
      {
        try
        {
          ::Windows::Devices::Enumeration::Pnp::PnpObjectType arg0 = static_cast<::Windows::Devices::Enumeration::Pnp::PnpObjectType>(args[0]->Int32Value());
          ::Windows::Foundation::Collections::IIterable<::Platform::String^>^ arg1 = 
            [] (v8::Handle<v8::Value> value) -> ::Windows::Foundation::Collections::IIterable<::Platform::String^>^
            {
              if (value->IsArray())
              {
                return NodeRT::Collections::JsArrayToWinrtVector<::Platform::String^>(value.As<Array>(), 
                 [](Handle<Value> value) -> bool {
                   return (!NodeRT::Utils::IsWinRtWrapper(value));
                 },
                 [](Handle<Value> value) -> ::Platform::String^ {
                   return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
                 }
                );
              }
              else
              {
                return dynamic_cast<::Windows::Foundation::Collections::IIterable<::Platform::String^>^>(NodeRT::Utils::GetObjectInstance(value));
              }
            } (args[1]);
          Platform::String^ arg2 = ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(args[2])));
          
          op = ::Windows::Devices::Enumeration::Pnp::PnpObject::FindAllAsync(arg0,arg1,arg2);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return scope.Close(Undefined());
        }
      }
      else 
      {
        ThrowException(Exception::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return scope.Close(Undefined());
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(args[args.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<::Windows::Devices::Enumeration::Pnp::PnpObjectCollection^> t) 
      {	
        try
        {
          auto result = t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [result](NodeUtils::InvokeCallbackDelegate invokeCallback) {

            
            TryCatch tryCatch;
            Handle<Value> error; 
            Handle<Value> arg1 = WrapPnpObjectCollection(result);
            if (tryCatch.HasCaught())
            {
              error = tryCatch.Exception()->ToObject();
            }
            else 
            {
              error = Undefined();
            }
            if (arg1.IsEmpty()) arg1 = Undefined();
            Handle<Value> args[] = {error, arg1};

            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
          
            Handle<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Handle<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });

      return scope.Close(Undefined());
    }

    static Handle<Value> CreateWatcher(const v8::Arguments& args)
    {
      HandleScope scope;

      if (args.Length() == 2
        && args[0]->IsInt32()
        && (NodeRT::Utils::IsWinRtWrapperOf<::Windows::Foundation::Collections::IIterable<::Platform::String^>^>(args[1]) || args[1]->IsArray()))
      {
        try
        {
          ::Windows::Devices::Enumeration::Pnp::PnpObjectType arg0 = static_cast<::Windows::Devices::Enumeration::Pnp::PnpObjectType>(args[0]->Int32Value());
          ::Windows::Foundation::Collections::IIterable<::Platform::String^>^ arg1 = 
            [] (v8::Handle<v8::Value> value) -> ::Windows::Foundation::Collections::IIterable<::Platform::String^>^
            {
              if (value->IsArray())
              {
                return NodeRT::Collections::JsArrayToWinrtVector<::Platform::String^>(value.As<Array>(), 
                 [](Handle<Value> value) -> bool {
                   return (!NodeRT::Utils::IsWinRtWrapper(value));
                 },
                 [](Handle<Value> value) -> ::Platform::String^ {
                   return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
                 }
                );
              }
              else
              {
                return dynamic_cast<::Windows::Foundation::Collections::IIterable<::Platform::String^>^>(NodeRT::Utils::GetObjectInstance(value));
              }
            } (args[1]);
          
          ::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^ result;
          result = ::Windows::Devices::Enumeration::Pnp::PnpObject::CreateWatcher(arg0, arg1);
          return scope.Close(WrapPnpObjectWatcher(result));
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return scope.Close(Undefined());
        }
      }
      else if (args.Length() == 3
        && args[0]->IsInt32()
        && (NodeRT::Utils::IsWinRtWrapperOf<::Windows::Foundation::Collections::IIterable<::Platform::String^>^>(args[1]) || args[1]->IsArray())
        && args[2]->IsString())
      {
        try
        {
          ::Windows::Devices::Enumeration::Pnp::PnpObjectType arg0 = static_cast<::Windows::Devices::Enumeration::Pnp::PnpObjectType>(args[0]->Int32Value());
          ::Windows::Foundation::Collections::IIterable<::Platform::String^>^ arg1 = 
            [] (v8::Handle<v8::Value> value) -> ::Windows::Foundation::Collections::IIterable<::Platform::String^>^
            {
              if (value->IsArray())
              {
                return NodeRT::Collections::JsArrayToWinrtVector<::Platform::String^>(value.As<Array>(), 
                 [](Handle<Value> value) -> bool {
                   return (!NodeRT::Utils::IsWinRtWrapper(value));
                 },
                 [](Handle<Value> value) -> ::Platform::String^ {
                   return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
                 }
                );
              }
              else
              {
                return dynamic_cast<::Windows::Foundation::Collections::IIterable<::Platform::String^>^>(NodeRT::Utils::GetObjectInstance(value));
              }
            } (args[1]);
          Platform::String^ arg2 = ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(args[2])));
          
          ::Windows::Devices::Enumeration::Pnp::PnpObjectWatcher^ result;
          result = ::Windows::Devices::Enumeration::Pnp::PnpObject::CreateWatcher(arg0, arg1, arg2);
          return scope.Close(WrapPnpObjectWatcher(result));
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return scope.Close(Undefined());
        }
      }
      else 
      {
        ThrowException(Exception::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return scope.Close(Undefined());
      }

      return scope.Close(Undefined());
    }

    static Handle<Value> IdGetter(Local<String> property, const AccessorInfo &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObject^>(info.This()))
      {
        return scope.Close(Undefined());
      }

      PnpObject *wrapper = PnpObject::Unwrap<PnpObject>(info.This());

      try 
      {
        Platform::String^ result = wrapper->_instance->Id;
        return scope.Close(NodeRT::Utils::NewString(result->Data()));
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return scope.Close(Undefined());
      }
    }
    
    static Handle<Value> PropertiesGetter(Local<String> property, const AccessorInfo &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObject^>(info.This()))
      {
        return scope.Close(Undefined());
      }

      PnpObject *wrapper = PnpObject::Unwrap<PnpObject>(info.This());

      try 
      {
        ::Windows::Foundation::Collections::IMapView<::Platform::String^, ::Platform::Object^>^ result = wrapper->_instance->Properties;
        return scope.Close(NodeRT::Collections::MapViewWrapper<::Platform::String^,::Platform::Object^>::CreateMapViewWrapper(result, 
            [](::Platform::String^ val) -> Handle<Value> {
              return NodeRT::Utils::NewString(val->Data());
            },
            [](Handle<Value> value) -> bool {
              return value->IsString();
            },
            [](Handle<Value> value) -> ::Platform::String^ {
              return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
            },
            [](::Platform::Object^ val) -> Handle<Value> {
              return CreateOpaqueWrapper(val);
            }
          ));
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return scope.Close(Undefined());
      }
    }
    
    static Handle<Value> TypeGetter(Local<String> property, const AccessorInfo &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Enumeration::Pnp::PnpObject^>(info.This()))
      {
        return scope.Close(Undefined());
      }

      PnpObject *wrapper = PnpObject::Unwrap<PnpObject>(info.This());

      try 
      {
        ::Windows::Devices::Enumeration::Pnp::PnpObjectType result = wrapper->_instance->Type;
        return scope.Close(Integer::New(static_cast<int>(result)));
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return scope.Close(Undefined());
      }
    }
    


  private:
    ::Windows::Devices::Enumeration::Pnp::PnpObject^ _instance;
    static Persistent<FunctionTemplate> s_constructorTemplate;

    friend v8::Handle<v8::Value> WrapPnpObject(::Windows::Devices::Enumeration::Pnp::PnpObject^ wintRtInstance);
    friend ::Windows::Devices::Enumeration::Pnp::PnpObject^ UnwrapPnpObject(Handle<Value> value);
    friend bool IsPnpObjectWrapper(Handle<Value> value);
  };
  Persistent<FunctionTemplate> PnpObject::s_constructorTemplate;

  v8::Handle<v8::Value> WrapPnpObject(::Windows::Devices::Enumeration::Pnp::PnpObject^ winRtInstance)
  {
    HandleScope scope;

    if (winRtInstance == nullptr)
    {
      return scope.Close(Undefined());
    }

    Handle<Object> opaqueWrapper = CreateOpaqueWrapper(winRtInstance);
    Handle<Value> args[] = {opaqueWrapper};
    return scope.Close(PnpObject::s_constructorTemplate->GetFunction()->NewInstance(_countof(args), args));
  }

  ::Windows::Devices::Enumeration::Pnp::PnpObject^ UnwrapPnpObject(Handle<Value> value)
  {
     return PnpObject::Unwrap<PnpObject>(value.As<Object>())->_instance;
  }

  void InitPnpObject(Handle<Object> exports)
  {
    PnpObject::Init(exports);
  }

} } } } } 

void init(Handle<Object> exports)
{
  if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)))
  {
    ThrowException(v8::Exception::Error(NodeRT::Utils::NewString(L"error in CoInitializeEx()")));
    return;
  }
  
  NodeRT::Windows::Devices::Enumeration::Pnp::InitPnpObjectTypeEnum(exports);
  NodeRT::Windows::Devices::Enumeration::Pnp::InitPnpObjectUpdate(exports);
  NodeRT::Windows::Devices::Enumeration::Pnp::InitPnpObjectCollection(exports);
  NodeRT::Windows::Devices::Enumeration::Pnp::InitPnpObjectWatcher(exports);
  NodeRT::Windows::Devices::Enumeration::Pnp::InitPnpObject(exports);

  NodeRT::Utils::RegisterNameSpace("Windows.Devices.Enumeration.Pnp", exports);
}


NODE_MODULE(binding, init)