// 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.

// TODO: Verify that this is is still needed..
#define NTDDI_VERSION 0x06010000

#include <v8.h>
#include "nan.h"
#include <string>
#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 v8::Array;
using v8::String;
using v8::Handle;
using v8::Value;
using v8::Boolean;
using v8::Integer;
using v8::FunctionTemplate;
using v8::Object;
using v8::Local;
using v8::Function;
using v8::Date;
using v8::Number;
using v8::PropertyAttribute;
using v8::Primitive;
using Nan::HandleScope;
using Nan::Persistent;
using Nan::Undefined;
using Nan::True;
using Nan::False;
using Nan::Null;
using Nan::MaybeLocal;
using Nan::EscapableHandleScope;
using Nan::HandleScope;
using Nan::TryCatch;
using namespace concurrency;

namespace NodeRT { namespace Windows { namespace Storage { namespace FileProperties { 

  v8::Local<v8::Value> WrapGeotagHelper(::Windows::Storage::FileProperties::GeotagHelper^ wintRtInstance);
  ::Windows::Storage::FileProperties::GeotagHelper^ UnwrapGeotagHelper(Local<Value> value);
  
  v8::Local<v8::Value> WrapStorageItemThumbnail(::Windows::Storage::FileProperties::StorageItemThumbnail^ wintRtInstance);
  ::Windows::Storage::FileProperties::StorageItemThumbnail^ UnwrapStorageItemThumbnail(Local<Value> value);
  
  v8::Local<v8::Value> WrapIStorageItemExtraProperties(::Windows::Storage::FileProperties::IStorageItemExtraProperties^ wintRtInstance);
  ::Windows::Storage::FileProperties::IStorageItemExtraProperties^ UnwrapIStorageItemExtraProperties(Local<Value> value);
  
  v8::Local<v8::Value> WrapMusicProperties(::Windows::Storage::FileProperties::MusicProperties^ wintRtInstance);
  ::Windows::Storage::FileProperties::MusicProperties^ UnwrapMusicProperties(Local<Value> value);
  
  v8::Local<v8::Value> WrapVideoProperties(::Windows::Storage::FileProperties::VideoProperties^ wintRtInstance);
  ::Windows::Storage::FileProperties::VideoProperties^ UnwrapVideoProperties(Local<Value> value);
  
  v8::Local<v8::Value> WrapImageProperties(::Windows::Storage::FileProperties::ImageProperties^ wintRtInstance);
  ::Windows::Storage::FileProperties::ImageProperties^ UnwrapImageProperties(Local<Value> value);
  
  v8::Local<v8::Value> WrapDocumentProperties(::Windows::Storage::FileProperties::DocumentProperties^ wintRtInstance);
  ::Windows::Storage::FileProperties::DocumentProperties^ UnwrapDocumentProperties(Local<Value> value);
  
  v8::Local<v8::Value> WrapStorageItemContentProperties(::Windows::Storage::FileProperties::StorageItemContentProperties^ wintRtInstance);
  ::Windows::Storage::FileProperties::StorageItemContentProperties^ UnwrapStorageItemContentProperties(Local<Value> value);
  
  v8::Local<v8::Value> WrapBasicProperties(::Windows::Storage::FileProperties::BasicProperties^ wintRtInstance);
  ::Windows::Storage::FileProperties::BasicProperties^ UnwrapBasicProperties(Local<Value> value);
  


  static void InitPropertyPrefetchOptionsEnum(const Local<Object> exports)
  {
    HandleScope scope;
    
	Local<Object> enumObject = Nan::New<Object>();
    Nan::Set(exports, Nan::New<String>("PropertyPrefetchOptions").ToLocalChecked(), enumObject);
	Nan::Set(enumObject, Nan::New<String>("none").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::PropertyPrefetchOptions::None)));
	Nan::Set(enumObject, Nan::New<String>("musicProperties").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::PropertyPrefetchOptions::MusicProperties)));
	Nan::Set(enumObject, Nan::New<String>("videoProperties").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::PropertyPrefetchOptions::VideoProperties)));
	Nan::Set(enumObject, Nan::New<String>("imageProperties").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::PropertyPrefetchOptions::ImageProperties)));
	Nan::Set(enumObject, Nan::New<String>("documentProperties").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::PropertyPrefetchOptions::DocumentProperties)));
	Nan::Set(enumObject, Nan::New<String>("basicProperties").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::PropertyPrefetchOptions::BasicProperties)));
  }


  static void InitThumbnailTypeEnum(const Local<Object> exports)
  {
    HandleScope scope;
    
	Local<Object> enumObject = Nan::New<Object>();
    Nan::Set(exports, Nan::New<String>("ThumbnailType").ToLocalChecked(), enumObject);
	Nan::Set(enumObject, Nan::New<String>("image").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::ThumbnailType::Image)));
	Nan::Set(enumObject, Nan::New<String>("icon").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::ThumbnailType::Icon)));
  }


  static void InitThumbnailModeEnum(const Local<Object> exports)
  {
    HandleScope scope;
    
	Local<Object> enumObject = Nan::New<Object>();
    Nan::Set(exports, Nan::New<String>("ThumbnailMode").ToLocalChecked(), enumObject);
	Nan::Set(enumObject, Nan::New<String>("picturesView").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::ThumbnailMode::PicturesView)));
	Nan::Set(enumObject, Nan::New<String>("videosView").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::ThumbnailMode::VideosView)));
	Nan::Set(enumObject, Nan::New<String>("musicView").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::ThumbnailMode::MusicView)));
	Nan::Set(enumObject, Nan::New<String>("documentsView").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::ThumbnailMode::DocumentsView)));
	Nan::Set(enumObject, Nan::New<String>("listView").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::ThumbnailMode::ListView)));
	Nan::Set(enumObject, Nan::New<String>("singleItem").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::ThumbnailMode::SingleItem)));
  }


  static void InitThumbnailOptionsEnum(const Local<Object> exports)
  {
    HandleScope scope;
    
	Local<Object> enumObject = Nan::New<Object>();
    Nan::Set(exports, Nan::New<String>("ThumbnailOptions").ToLocalChecked(), enumObject);
	Nan::Set(enumObject, Nan::New<String>("none").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::ThumbnailOptions::None)));
	Nan::Set(enumObject, Nan::New<String>("returnOnlyIfCached").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::ThumbnailOptions::ReturnOnlyIfCached)));
	Nan::Set(enumObject, Nan::New<String>("resizeThumbnail").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::ThumbnailOptions::ResizeThumbnail)));
	Nan::Set(enumObject, Nan::New<String>("useCurrentScale").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::ThumbnailOptions::UseCurrentScale)));
  }


  static void InitPhotoOrientationEnum(const Local<Object> exports)
  {
    HandleScope scope;
    
	Local<Object> enumObject = Nan::New<Object>();
    Nan::Set(exports, Nan::New<String>("PhotoOrientation").ToLocalChecked(), enumObject);
	Nan::Set(enumObject, Nan::New<String>("unspecified").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::PhotoOrientation::Unspecified)));
	Nan::Set(enumObject, Nan::New<String>("normal").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::PhotoOrientation::Normal)));
	Nan::Set(enumObject, Nan::New<String>("flipHorizontal").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::PhotoOrientation::FlipHorizontal)));
	Nan::Set(enumObject, Nan::New<String>("rotate180").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::PhotoOrientation::Rotate180)));
	Nan::Set(enumObject, Nan::New<String>("flipVertical").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::PhotoOrientation::FlipVertical)));
	Nan::Set(enumObject, Nan::New<String>("transpose").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::PhotoOrientation::Transpose)));
	Nan::Set(enumObject, Nan::New<String>("rotate270").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::PhotoOrientation::Rotate270)));
	Nan::Set(enumObject, Nan::New<String>("transverse").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::PhotoOrientation::Transverse)));
	Nan::Set(enumObject, Nan::New<String>("rotate90").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::PhotoOrientation::Rotate90)));
  }


  static void InitVideoOrientationEnum(const Local<Object> exports)
  {
    HandleScope scope;
    
	Local<Object> enumObject = Nan::New<Object>();
    Nan::Set(exports, Nan::New<String>("VideoOrientation").ToLocalChecked(), enumObject);
	Nan::Set(enumObject, Nan::New<String>("normal").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::VideoOrientation::Normal)));
	Nan::Set(enumObject, Nan::New<String>("rotate90").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::VideoOrientation::Rotate90)));
	Nan::Set(enumObject, Nan::New<String>("rotate180").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::VideoOrientation::Rotate180)));
	Nan::Set(enumObject, Nan::New<String>("rotate270").ToLocalChecked(), Nan::New<Integer>(static_cast<int>(::Windows::Storage::FileProperties::VideoOrientation::Rotate270)));
  }



  
  class GeotagHelper : public WrapperBase
  {
  public:    
    static void Init(const Local<Object> exports)
    {
      HandleScope scope;
      
      Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(New);
      s_constructorTemplate.Reset(localRef);
      localRef->SetClassName(Nan::New<String>("GeotagHelper").ToLocalChecked());
      localRef->InstanceTemplate()->SetInternalFieldCount(1);
      
      Local<Function> func;
      Local<FunctionTemplate> funcTemplate;
                              
      Local<Object> constructor = Nan::To<Object>(Nan::GetFunction(localRef).ToLocalChecked()).ToLocalChecked();
	  Nan::SetMethod(constructor, "castFrom", CastFrom);

      func = Nan::GetFunction(Nan::New<FunctionTemplate>(GetGeotagAsync)).ToLocalChecked();
      Nan::Set(constructor, Nan::New<String>("getGeotagAsync").ToLocalChecked(), func);
      func = Nan::GetFunction(Nan::New<FunctionTemplate>(SetGeotagFromGeolocatorAsync)).ToLocalChecked();
      Nan::Set(constructor, Nan::New<String>("setGeotagFromGeolocatorAsync").ToLocalChecked(), func);
      func = Nan::GetFunction(Nan::New<FunctionTemplate>(SetGeotagAsync)).ToLocalChecked();
      Nan::Set(constructor, Nan::New<String>("setGeotagAsync").ToLocalChecked(), func);

      Nan::Set(exports, Nan::New<String>("GeotagHelper").ToLocalChecked(), constructor);
    }


    virtual ::Platform::Object^ GetObjectInstance() const override
    {
      return _instance;
    }

  private:
    
    GeotagHelper(::Windows::Storage::FileProperties::GeotagHelper^ instance)
    {
      _instance = instance;
    }
    
    
    static void New(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

	    Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(s_constructorTemplate);

      // in case the constructor was called without the new operator
      if (!localRef->HasInstance(info.This()))
      {
        if (info.Length() > 0)
        {
          std::unique_ptr<Local<Value> []> constructorArgs(new Local<Value>[info.Length()]);

          Local<Value> *argsPtr = constructorArgs.get();
          for (int i = 0; i < info.Length(); i++)
          {
            argsPtr[i] = info[i];
          }

		  MaybeLocal<Object> res = Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(), info.Length(), constructorArgs.get());
		  if (res.IsEmpty())
		  {
			  return;
		  }
		  info.GetReturnValue().Set(res.ToLocalChecked());
		  return;
		}
		else
		{
          MaybeLocal<Object> res = Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(), info.Length(), nullptr);
          if (res.IsEmpty())
          {
            return;
          }
          info.GetReturnValue().Set(res.ToLocalChecked());
          return;
        }
      }
      
      ::Windows::Storage::FileProperties::GeotagHelper^ winRtInstance;


      if (info.Length() == 1 && OpaqueWrapper::IsOpaqueWrapper(info[0]) &&
        NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::GeotagHelper^>(info[0]))
      {
        try 
        {
          winRtInstance = (::Windows::Storage::FileProperties::GeotagHelper^) NodeRT::Utils::GetObjectInstance(info[0]);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Invalid arguments, no suitable constructor found")));
	    	return;
      }

      NodeRT::Utils::SetHiddenValue(info.This(), Nan::New<String>("__winRtInstance__").ToLocalChecked(), True());

      GeotagHelper *wrapperInstance = new GeotagHelper(winRtInstance);
      wrapperInstance->Wrap(info.This());

      info.GetReturnValue().Set(info.This());
    }


	
    static void CastFrom(Nan::NAN_METHOD_ARGS_TYPE info)
    {
		HandleScope scope;
		if (info.Length() < 1 || !NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::GeotagHelper^>(info[0]))
		{
			Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Invalid arguments, no object provided, or given object could not be casted to requested type")));
			return;
		}

		::Windows::Storage::FileProperties::GeotagHelper^ winRtInstance;
		try
		{
			winRtInstance = (::Windows::Storage::FileProperties::GeotagHelper^) NodeRT::Utils::GetObjectInstance(info[0]);
		}
		catch (Platform::Exception ^exception)
		{
			NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
			return;
		}

		info.GetReturnValue().Set(WrapGeotagHelper(winRtInstance));
    }


  

    static void GetGeotagAsync(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (info.Length() == 0 || !info[info.Length() -1]->IsFunction())
      {
          Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return;
      }

      ::Windows::Foundation::IAsyncOperation<::Windows::Devices::Geolocation::Geopoint^>^ op;
      

      if (info.Length() == 2
        && NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::IStorageFile^>(info[0]))
      {
        try
        {
          ::Windows::Storage::IStorageFile^ arg0 = dynamic_cast<::Windows::Storage::IStorageFile^>(NodeRT::Utils::GetObjectInstance(info[0]));
          
          op = ::Windows::Storage::FileProperties::GeotagHelper::GetGeotagAsync(arg0);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(info[info.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<::Windows::Devices::Geolocation::Geopoint^> t) 
      {	
        try
        {
          auto result = t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [result](NodeUtils::InvokeCallbackDelegate invokeCallback) {

            
            TryCatch tryCatch;
            Local<Value> error; 
            Local<Value> arg1 = NodeRT::Utils::CreateExternalWinRTObject("Windows.Devices.Geolocation", "Geopoint", result);
            if (tryCatch.HasCaught())
            {
              error = Nan::To<Object>(tryCatch.Exception()).ToLocalChecked();
            }
            else 
            {
              error = Undefined();
            }
            if (arg1.IsEmpty()) arg1 = Undefined();
            Local<Value> args[] = {error, arg1};
			// TODO: this is ugly! Needed due to the possibility of expception occuring inside object convertors
			// can be fixed by wrapping the conversion code in a function and calling it on the fly
			// we must clear the try catch block here so the invoked inner method exception won't get swollen (issue #52) 
			tryCatch.~TryCatch();

	  	    
            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
          
            Local<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Local<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });
    }
    static void SetGeotagFromGeolocatorAsync(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (info.Length() == 0 || !info[info.Length() -1]->IsFunction())
      {
          Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return;
      }

      ::Windows::Foundation::IAsyncAction^ op;
      

      if (info.Length() == 3
        && NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::IStorageFile^>(info[0])
        && NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Geolocation::Geolocator^>(info[1]))
      {
        try
        {
          ::Windows::Storage::IStorageFile^ arg0 = dynamic_cast<::Windows::Storage::IStorageFile^>(NodeRT::Utils::GetObjectInstance(info[0]));
          ::Windows::Devices::Geolocation::Geolocator^ arg1 = dynamic_cast<::Windows::Devices::Geolocation::Geolocator^>(NodeRT::Utils::GetObjectInstance(info[1]));
          
          op = ::Windows::Storage::FileProperties::GeotagHelper::SetGeotagFromGeolocatorAsync(arg0,arg1);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(info[info.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<void> t) 
      {	
        try
        {
          t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [](NodeUtils::InvokeCallbackDelegate invokeCallback) {

            
            Local<Value> args[] = {Undefined()};

	  	    
            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
          
            Local<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Local<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });
    }
    static void SetGeotagAsync(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (info.Length() == 0 || !info[info.Length() -1]->IsFunction())
      {
          Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return;
      }

      ::Windows::Foundation::IAsyncAction^ op;
      

      if (info.Length() == 3
        && NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::IStorageFile^>(info[0])
        && NodeRT::Utils::IsWinRtWrapperOf<::Windows::Devices::Geolocation::Geopoint^>(info[1]))
      {
        try
        {
          ::Windows::Storage::IStorageFile^ arg0 = dynamic_cast<::Windows::Storage::IStorageFile^>(NodeRT::Utils::GetObjectInstance(info[0]));
          ::Windows::Devices::Geolocation::Geopoint^ arg1 = dynamic_cast<::Windows::Devices::Geolocation::Geopoint^>(NodeRT::Utils::GetObjectInstance(info[1]));
          
          op = ::Windows::Storage::FileProperties::GeotagHelper::SetGeotagAsync(arg0,arg1);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(info[info.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<void> t) 
      {	
        try
        {
          t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [](NodeUtils::InvokeCallbackDelegate invokeCallback) {

            
            Local<Value> args[] = {Undefined()};

	  	    
            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
          
            Local<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Local<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });
    }




  private:
    ::Windows::Storage::FileProperties::GeotagHelper^ _instance;
    static Persistent<FunctionTemplate> s_constructorTemplate;

    friend v8::Local<v8::Value> WrapGeotagHelper(::Windows::Storage::FileProperties::GeotagHelper^ wintRtInstance);
    friend ::Windows::Storage::FileProperties::GeotagHelper^ UnwrapGeotagHelper(Local<Value> value);
  };
  Persistent<FunctionTemplate> GeotagHelper::s_constructorTemplate;

  v8::Local<v8::Value> WrapGeotagHelper(::Windows::Storage::FileProperties::GeotagHelper^ winRtInstance)
  {
    EscapableHandleScope scope;

    if (winRtInstance == nullptr)
    {
      return scope.Escape(Undefined());
    }

    Local<Value> opaqueWrapper = CreateOpaqueWrapper(winRtInstance);
    Local<Value> args[] = {opaqueWrapper};
    Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(GeotagHelper::s_constructorTemplate);
    return scope.Escape(Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(),_countof(args), args).ToLocalChecked());
  }

  ::Windows::Storage::FileProperties::GeotagHelper^ UnwrapGeotagHelper(Local<Value> value)
  {
     return GeotagHelper::Unwrap<GeotagHelper>(Nan::To<Object>(value).ToLocalChecked())->_instance;
  }

  void InitGeotagHelper(Local<Object> exports)
  {
    GeotagHelper::Init(exports);
  }

  class StorageItemThumbnail : public WrapperBase
  {
  public:    
    static void Init(const Local<Object> exports)
    {
      HandleScope scope;
      
      Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(New);
      s_constructorTemplate.Reset(localRef);
      localRef->SetClassName(Nan::New<String>("StorageItemThumbnail").ToLocalChecked());
      localRef->InstanceTemplate()->SetInternalFieldCount(1);
      
      Local<Function> func;
      Local<FunctionTemplate> funcTemplate;
            
      Nan::SetPrototypeMethod(localRef, "getInputStreamAt", GetInputStreamAt);
      Nan::SetPrototypeMethod(localRef, "getOutputStreamAt", GetOutputStreamAt);
      Nan::SetPrototypeMethod(localRef, "seek", Seek);
      Nan::SetPrototypeMethod(localRef, "cloneStream", CloneStream);
      Nan::SetPrototypeMethod(localRef, "close", Close);
      
            
      Nan::SetPrototypeMethod(localRef, "readAsync", ReadAsync);
      Nan::SetPrototypeMethod(localRef, "writeAsync", WriteAsync);
      Nan::SetPrototypeMethod(localRef, "flushAsync", FlushAsync);
      
                  
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("originalHeight").ToLocalChecked(), OriginalHeightGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("originalWidth").ToLocalChecked(), OriginalWidthGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("returnedSmallerCachedSize").ToLocalChecked(), ReturnedSmallerCachedSizeGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("type").ToLocalChecked(), TypeGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("contentType").ToLocalChecked(), ContentTypeGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("size").ToLocalChecked(), SizeGetter, SizeSetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("canRead").ToLocalChecked(), CanReadGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("canWrite").ToLocalChecked(), CanWriteGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("position").ToLocalChecked(), PositionGetter);
      
      Local<Object> constructor = Nan::To<Object>(Nan::GetFunction(localRef).ToLocalChecked()).ToLocalChecked();
	  Nan::SetMethod(constructor, "castFrom", CastFrom);


      Nan::Set(exports, Nan::New<String>("StorageItemThumbnail").ToLocalChecked(), constructor);
    }


    virtual ::Platform::Object^ GetObjectInstance() const override
    {
      return _instance;
    }

  private:
    
    StorageItemThumbnail(::Windows::Storage::FileProperties::StorageItemThumbnail^ instance)
    {
      _instance = instance;
    }
    
    
    static void New(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

	    Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(s_constructorTemplate);

      // in case the constructor was called without the new operator
      if (!localRef->HasInstance(info.This()))
      {
        if (info.Length() > 0)
        {
          std::unique_ptr<Local<Value> []> constructorArgs(new Local<Value>[info.Length()]);

          Local<Value> *argsPtr = constructorArgs.get();
          for (int i = 0; i < info.Length(); i++)
          {
            argsPtr[i] = info[i];
          }

		  MaybeLocal<Object> res = Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(), info.Length(), constructorArgs.get());
		  if (res.IsEmpty())
		  {
			  return;
		  }
		  info.GetReturnValue().Set(res.ToLocalChecked());
		  return;
		}
		else
		{
          MaybeLocal<Object> res = Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(), info.Length(), nullptr);
          if (res.IsEmpty())
          {
            return;
          }
          info.GetReturnValue().Set(res.ToLocalChecked());
          return;
        }
      }
      
      ::Windows::Storage::FileProperties::StorageItemThumbnail^ winRtInstance;


      if (info.Length() == 1 && OpaqueWrapper::IsOpaqueWrapper(info[0]) &&
        NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemThumbnail^>(info[0]))
      {
        try 
        {
          winRtInstance = (::Windows::Storage::FileProperties::StorageItemThumbnail^) NodeRT::Utils::GetObjectInstance(info[0]);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Invalid arguments, no suitable constructor found")));
	    	return;
      }

      NodeRT::Utils::SetHiddenValue(info.This(), Nan::New<String>("__winRtInstance__").ToLocalChecked(), True());

      StorageItemThumbnail *wrapperInstance = new StorageItemThumbnail(winRtInstance);
      wrapperInstance->Wrap(info.This());

      info.GetReturnValue().Set(info.This());
    }


	
    static void CastFrom(Nan::NAN_METHOD_ARGS_TYPE info)
    {
		HandleScope scope;
		if (info.Length() < 1 || !NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemThumbnail^>(info[0]))
		{
			Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Invalid arguments, no object provided, or given object could not be casted to requested type")));
			return;
		}

		::Windows::Storage::FileProperties::StorageItemThumbnail^ winRtInstance;
		try
		{
			winRtInstance = (::Windows::Storage::FileProperties::StorageItemThumbnail^) NodeRT::Utils::GetObjectInstance(info[0]);
		}
		catch (Platform::Exception ^exception)
		{
			NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
			return;
		}

		info.GetReturnValue().Set(WrapStorageItemThumbnail(winRtInstance));
    }


    static void ReadAsync(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemThumbnail^>(info.This()))
      {
        return;
      }

      if (info.Length() == 0 || !info[info.Length() -1]->IsFunction())
      {
          Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return;
      }

      StorageItemThumbnail *wrapper = StorageItemThumbnail::Unwrap<StorageItemThumbnail>(info.This());

      ::Windows::Foundation::IAsyncOperationWithProgress<::Windows::Storage::Streams::IBuffer^, unsigned int>^ op;
    

      if (info.Length() == 4
        && NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::Streams::IBuffer^>(info[0])
        && info[1]->IsUint32()
        && info[2]->IsInt32())
      {
        try
        {
          ::Windows::Storage::Streams::IBuffer^ arg0 = dynamic_cast<::Windows::Storage::Streams::IBuffer^>(NodeRT::Utils::GetObjectInstance(info[0]));
          unsigned int arg1 = static_cast<unsigned int>(Nan::To<uint32_t>(info[1]).FromMaybe(0));
          ::Windows::Storage::Streams::InputStreamOptions arg2 = static_cast<::Windows::Storage::Streams::InputStreamOptions>(Nan::To<int32_t>(info[2]).FromMaybe(0));
          
          op = wrapper->_instance->ReadAsync(arg0,arg1,arg2);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(info[info.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<::Windows::Storage::Streams::IBuffer^> t) 
      {	
        try
        {
          auto result = t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [result](NodeUtils::InvokeCallbackDelegate invokeCallback) {


            TryCatch tryCatch;
            Local<Value> error; 
            Local<Value> arg1 = NodeRT::Utils::CreateExternalWinRTObject("Windows.Storage.Streams", "IBuffer", result);
            if (tryCatch.HasCaught())
            {
              error = Nan::To<Object>(tryCatch.Exception()).ToLocalChecked();
            }
            else 
            {
              error = Undefined();
            }
            if (arg1.IsEmpty()) arg1 = Undefined();
            Local<Value> args[] = {error, arg1};
			// TODO: this is ugly! Needed due to the possibility of expception occuring inside object convertors
			// can be fixed by wrapping the conversion code in a function and calling it on the fly
			// we must clear the try catch block here so the invoked inner method exception won't get swollen (issue #52) 
			tryCatch.~TryCatch();

		    
            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
             
            Local<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Local<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });
    }
    static void WriteAsync(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemThumbnail^>(info.This()))
      {
        return;
      }

      if (info.Length() == 0 || !info[info.Length() -1]->IsFunction())
      {
          Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return;
      }

      StorageItemThumbnail *wrapper = StorageItemThumbnail::Unwrap<StorageItemThumbnail>(info.This());

      ::Windows::Foundation::IAsyncOperationWithProgress<unsigned int, unsigned int>^ op;
    

      if (info.Length() == 2
        && NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::Streams::IBuffer^>(info[0]))
      {
        try
        {
          ::Windows::Storage::Streams::IBuffer^ arg0 = dynamic_cast<::Windows::Storage::Streams::IBuffer^>(NodeRT::Utils::GetObjectInstance(info[0]));
          
          op = wrapper->_instance->WriteAsync(arg0);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(info[info.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<unsigned int> t) 
      {	
        try
        {
          auto result = t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [result](NodeUtils::InvokeCallbackDelegate invokeCallback) {


            TryCatch tryCatch;
            Local<Value> error; 
            Local<Value> arg1 = Nan::New<Integer>(result);
            if (tryCatch.HasCaught())
            {
              error = Nan::To<Object>(tryCatch.Exception()).ToLocalChecked();
            }
            else 
            {
              error = Undefined();
            }
            if (arg1.IsEmpty()) arg1 = Undefined();
            Local<Value> args[] = {error, arg1};
			// TODO: this is ugly! Needed due to the possibility of expception occuring inside object convertors
			// can be fixed by wrapping the conversion code in a function and calling it on the fly
			// we must clear the try catch block here so the invoked inner method exception won't get swollen (issue #52) 
			tryCatch.~TryCatch();

		    
            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
             
            Local<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Local<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });
    }
    static void FlushAsync(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemThumbnail^>(info.This()))
      {
        return;
      }

      if (info.Length() == 0 || !info[info.Length() -1]->IsFunction())
      {
          Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return;
      }

      StorageItemThumbnail *wrapper = StorageItemThumbnail::Unwrap<StorageItemThumbnail>(info.This());

      ::Windows::Foundation::IAsyncOperation<bool>^ op;
    

      if (info.Length() == 1)
      {
        try
        {
          op = wrapper->_instance->FlushAsync();
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(info[info.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<bool> t) 
      {	
        try
        {
          auto result = t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [result](NodeUtils::InvokeCallbackDelegate invokeCallback) {


            TryCatch tryCatch;
            Local<Value> error; 
            Local<Value> arg1 = Nan::New<Boolean>(result);
            if (tryCatch.HasCaught())
            {
              error = Nan::To<Object>(tryCatch.Exception()).ToLocalChecked();
            }
            else 
            {
              error = Undefined();
            }
            if (arg1.IsEmpty()) arg1 = Undefined();
            Local<Value> args[] = {error, arg1};
			// TODO: this is ugly! Needed due to the possibility of expception occuring inside object convertors
			// can be fixed by wrapping the conversion code in a function and calling it on the fly
			// we must clear the try catch block here so the invoked inner method exception won't get swollen (issue #52) 
			tryCatch.~TryCatch();

		    
            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
             
            Local<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Local<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });
    }
  
    static void GetInputStreamAt(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemThumbnail^>(info.This()))
      {
        return;
      }

      StorageItemThumbnail *wrapper = StorageItemThumbnail::Unwrap<StorageItemThumbnail>(info.This());

      if (info.Length() == 1
        && info[0]->IsNumber())
      {
        try
        {
          unsigned __int64 arg0 = static_cast<unsigned __int64>(Nan::To<int64_t>(info[0]).FromMaybe(0));
          
          ::Windows::Storage::Streams::IInputStream^ result;
          result = wrapper->_instance->GetInputStreamAt(arg0);
          info.GetReturnValue().Set(NodeRT::Utils::CreateExternalWinRTObject("Windows.Storage.Streams", "IInputStream", result));
          return;
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    }
    static void GetOutputStreamAt(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemThumbnail^>(info.This()))
      {
        return;
      }

      StorageItemThumbnail *wrapper = StorageItemThumbnail::Unwrap<StorageItemThumbnail>(info.This());

      if (info.Length() == 1
        && info[0]->IsNumber())
      {
        try
        {
          unsigned __int64 arg0 = static_cast<unsigned __int64>(Nan::To<int64_t>(info[0]).FromMaybe(0));
          
          ::Windows::Storage::Streams::IOutputStream^ result;
          result = wrapper->_instance->GetOutputStreamAt(arg0);
          info.GetReturnValue().Set(NodeRT::Utils::CreateExternalWinRTObject("Windows.Storage.Streams", "IOutputStream", result));
          return;
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    }
    static void Seek(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemThumbnail^>(info.This()))
      {
        return;
      }

      StorageItemThumbnail *wrapper = StorageItemThumbnail::Unwrap<StorageItemThumbnail>(info.This());

      if (info.Length() == 1
        && info[0]->IsNumber())
      {
        try
        {
          unsigned __int64 arg0 = static_cast<unsigned __int64>(Nan::To<int64_t>(info[0]).FromMaybe(0));
          
          wrapper->_instance->Seek(arg0);
          return;   
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    }
    static void CloneStream(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemThumbnail^>(info.This()))
      {
        return;
      }

      StorageItemThumbnail *wrapper = StorageItemThumbnail::Unwrap<StorageItemThumbnail>(info.This());

      if (info.Length() == 0)
      {
        try
        {
          ::Windows::Storage::Streams::IRandomAccessStream^ result;
          result = wrapper->_instance->CloneStream();
          info.GetReturnValue().Set(NodeRT::Utils::CreateExternalWinRTObject("Windows.Storage.Streams", "IRandomAccessStream", result));
          return;
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    }
    static void Close(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemThumbnail^>(info.This()))
      {
	    return;
      }

      StorageItemThumbnail *wrapper = StorageItemThumbnail::Unwrap<StorageItemThumbnail>(info.This());

      if (info.Length() == 0)
      {
        try
        {
          delete wrapper->_instance;
          wrapper->_instance = nullptr;
		  return;
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
		  return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
		return;
      }
    }




    static void OriginalHeightGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemThumbnail^>(info.This()))
      {
        return;
      }

      StorageItemThumbnail *wrapper = StorageItemThumbnail::Unwrap<StorageItemThumbnail>(info.This());

      try 
      {
        unsigned int result = wrapper->_instance->OriginalHeight;
        info.GetReturnValue().Set(Nan::New<Integer>(result));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void OriginalWidthGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemThumbnail^>(info.This()))
      {
        return;
      }

      StorageItemThumbnail *wrapper = StorageItemThumbnail::Unwrap<StorageItemThumbnail>(info.This());

      try 
      {
        unsigned int result = wrapper->_instance->OriginalWidth;
        info.GetReturnValue().Set(Nan::New<Integer>(result));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void ReturnedSmallerCachedSizeGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemThumbnail^>(info.This()))
      {
        return;
      }

      StorageItemThumbnail *wrapper = StorageItemThumbnail::Unwrap<StorageItemThumbnail>(info.This());

      try 
      {
        bool result = wrapper->_instance->ReturnedSmallerCachedSize;
        info.GetReturnValue().Set(Nan::New<Boolean>(result));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void TypeGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemThumbnail^>(info.This()))
      {
        return;
      }

      StorageItemThumbnail *wrapper = StorageItemThumbnail::Unwrap<StorageItemThumbnail>(info.This());

      try 
      {
        ::Windows::Storage::FileProperties::ThumbnailType result = wrapper->_instance->Type;
        info.GetReturnValue().Set(Nan::New<Integer>(static_cast<int>(result)));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void ContentTypeGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemThumbnail^>(info.This()))
      {
        return;
      }

      StorageItemThumbnail *wrapper = StorageItemThumbnail::Unwrap<StorageItemThumbnail>(info.This());

      try 
      {
        Platform::String^ result = wrapper->_instance->ContentType;
        info.GetReturnValue().Set(NodeRT::Utils::NewString(result->Data()));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void SizeGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemThumbnail^>(info.This()))
      {
        return;
      }

      StorageItemThumbnail *wrapper = StorageItemThumbnail::Unwrap<StorageItemThumbnail>(info.This());

      try 
      {
        unsigned __int64 result = wrapper->_instance->Size;
        info.GetReturnValue().Set(Nan::New<Number>(static_cast<double>(result)));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void SizeSetter(Local<String> property, Local<Value> value, const Nan::PropertyCallbackInfo<void> &info)
    {
      HandleScope scope;
      
      if (!value->IsNumber())
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Value to set is of unexpected type")));
        return;
      }

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemThumbnail^>(info.This()))
      {
        return;
      }

      StorageItemThumbnail *wrapper = StorageItemThumbnail::Unwrap<StorageItemThumbnail>(info.This());

      try 
      {
        
        unsigned __int64 winRtValue = static_cast<unsigned __int64>(Nan::To<int64_t>(value).FromMaybe(0));

        wrapper->_instance->Size = winRtValue;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
      }
    }
    
    static void CanReadGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemThumbnail^>(info.This()))
      {
        return;
      }

      StorageItemThumbnail *wrapper = StorageItemThumbnail::Unwrap<StorageItemThumbnail>(info.This());

      try 
      {
        bool result = wrapper->_instance->CanRead;
        info.GetReturnValue().Set(Nan::New<Boolean>(result));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void CanWriteGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemThumbnail^>(info.This()))
      {
        return;
      }

      StorageItemThumbnail *wrapper = StorageItemThumbnail::Unwrap<StorageItemThumbnail>(info.This());

      try 
      {
        bool result = wrapper->_instance->CanWrite;
        info.GetReturnValue().Set(Nan::New<Boolean>(result));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void PositionGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemThumbnail^>(info.This()))
      {
        return;
      }

      StorageItemThumbnail *wrapper = StorageItemThumbnail::Unwrap<StorageItemThumbnail>(info.This());

      try 
      {
        unsigned __int64 result = wrapper->_instance->Position;
        info.GetReturnValue().Set(Nan::New<Number>(static_cast<double>(result)));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    


  private:
    ::Windows::Storage::FileProperties::StorageItemThumbnail^ _instance;
    static Persistent<FunctionTemplate> s_constructorTemplate;

    friend v8::Local<v8::Value> WrapStorageItemThumbnail(::Windows::Storage::FileProperties::StorageItemThumbnail^ wintRtInstance);
    friend ::Windows::Storage::FileProperties::StorageItemThumbnail^ UnwrapStorageItemThumbnail(Local<Value> value);
  };
  Persistent<FunctionTemplate> StorageItemThumbnail::s_constructorTemplate;

  v8::Local<v8::Value> WrapStorageItemThumbnail(::Windows::Storage::FileProperties::StorageItemThumbnail^ winRtInstance)
  {
    EscapableHandleScope scope;

    if (winRtInstance == nullptr)
    {
      return scope.Escape(Undefined());
    }

    Local<Value> opaqueWrapper = CreateOpaqueWrapper(winRtInstance);
    Local<Value> args[] = {opaqueWrapper};
    Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(StorageItemThumbnail::s_constructorTemplate);
    return scope.Escape(Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(),_countof(args), args).ToLocalChecked());
  }

  ::Windows::Storage::FileProperties::StorageItemThumbnail^ UnwrapStorageItemThumbnail(Local<Value> value)
  {
     return StorageItemThumbnail::Unwrap<StorageItemThumbnail>(Nan::To<Object>(value).ToLocalChecked())->_instance;
  }

  void InitStorageItemThumbnail(Local<Object> exports)
  {
    StorageItemThumbnail::Init(exports);
  }

  class IStorageItemExtraProperties : public WrapperBase
  {
  public:    
    static void Init(const Local<Object> exports)
    {
      HandleScope scope;
      
      Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(New);
      s_constructorTemplate.Reset(localRef);
      localRef->SetClassName(Nan::New<String>("IStorageItemExtraProperties").ToLocalChecked());
      localRef->InstanceTemplate()->SetInternalFieldCount(1);
      
      Local<Function> func;
      Local<FunctionTemplate> funcTemplate;
                  
      Nan::SetPrototypeMethod(localRef, "retrievePropertiesAsync", RetrievePropertiesAsync);
      Nan::SetPrototypeMethod(localRef, "savePropertiesAsync", SavePropertiesAsync);
      
                  
      Local<Object> constructor = Nan::To<Object>(Nan::GetFunction(localRef).ToLocalChecked()).ToLocalChecked();
	  Nan::SetMethod(constructor, "castFrom", CastFrom);


      Nan::Set(exports, Nan::New<String>("IStorageItemExtraProperties").ToLocalChecked(), constructor);
    }


    virtual ::Platform::Object^ GetObjectInstance() const override
    {
      return _instance;
    }

  private:
    
    IStorageItemExtraProperties(::Windows::Storage::FileProperties::IStorageItemExtraProperties^ instance)
    {
      _instance = instance;
    }
    
    
    static void New(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

	    Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(s_constructorTemplate);

      // in case the constructor was called without the new operator
      if (!localRef->HasInstance(info.This()))
      {
        if (info.Length() > 0)
        {
          std::unique_ptr<Local<Value> []> constructorArgs(new Local<Value>[info.Length()]);

          Local<Value> *argsPtr = constructorArgs.get();
          for (int i = 0; i < info.Length(); i++)
          {
            argsPtr[i] = info[i];
          }

		  MaybeLocal<Object> res = Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(), info.Length(), constructorArgs.get());
		  if (res.IsEmpty())
		  {
			  return;
		  }
		  info.GetReturnValue().Set(res.ToLocalChecked());
		  return;
		}
		else
		{
          MaybeLocal<Object> res = Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(), info.Length(), nullptr);
          if (res.IsEmpty())
          {
            return;
          }
          info.GetReturnValue().Set(res.ToLocalChecked());
          return;
        }
      }
      
      ::Windows::Storage::FileProperties::IStorageItemExtraProperties^ winRtInstance;


      if (info.Length() == 1 && OpaqueWrapper::IsOpaqueWrapper(info[0]) &&
        NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::IStorageItemExtraProperties^>(info[0]))
      {
        try 
        {
          winRtInstance = (::Windows::Storage::FileProperties::IStorageItemExtraProperties^) NodeRT::Utils::GetObjectInstance(info[0]);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Invalid arguments, no suitable constructor found")));
	    	return;
      }

      NodeRT::Utils::SetHiddenValue(info.This(), Nan::New<String>("__winRtInstance__").ToLocalChecked(), True());

      IStorageItemExtraProperties *wrapperInstance = new IStorageItemExtraProperties(winRtInstance);
      wrapperInstance->Wrap(info.This());

      info.GetReturnValue().Set(info.This());
    }


	
    static void CastFrom(Nan::NAN_METHOD_ARGS_TYPE info)
    {
		HandleScope scope;
		if (info.Length() < 1 || !NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::IStorageItemExtraProperties^>(info[0]))
		{
			Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Invalid arguments, no object provided, or given object could not be casted to requested type")));
			return;
		}

		::Windows::Storage::FileProperties::IStorageItemExtraProperties^ winRtInstance;
		try
		{
			winRtInstance = (::Windows::Storage::FileProperties::IStorageItemExtraProperties^) NodeRT::Utils::GetObjectInstance(info[0]);
		}
		catch (Platform::Exception ^exception)
		{
			NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
			return;
		}

		info.GetReturnValue().Set(WrapIStorageItemExtraProperties(winRtInstance));
    }


    static void RetrievePropertiesAsync(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::IStorageItemExtraProperties^>(info.This()))
      {
        return;
      }

      if (info.Length() == 0 || !info[info.Length() -1]->IsFunction())
      {
          Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return;
      }

      IStorageItemExtraProperties *wrapper = IStorageItemExtraProperties::Unwrap<IStorageItemExtraProperties>(info.This());

      ::Windows::Foundation::IAsyncOperation<::Windows::Foundation::Collections::IMap<::Platform::String^, ::Platform::Object^>^>^ op;
    

      if (info.Length() == 2
        && (NodeRT::Utils::IsWinRtWrapperOf<::Windows::Foundation::Collections::IIterable<::Platform::String^>^>(info[0]) || info[0]->IsArray()))
      {
        try
        {
          ::Windows::Foundation::Collections::IIterable<::Platform::String^>^ arg0 = 
            [] (v8::Local<v8::Value> value) -> ::Windows::Foundation::Collections::IIterable<::Platform::String^>^
            {
              if (value->IsArray())
              {
                return NodeRT::Collections::JsArrayToWinrtVector<::Platform::String^>(value.As<Array>(), 
                 [](Local<Value> value) -> bool {
                   return (!NodeRT::Utils::IsWinRtWrapper(value));
                 },
                 [](Local<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));
              }
            } (info[0]);
          
          op = wrapper->_instance->RetrievePropertiesAsync(arg0);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(info[info.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<::Windows::Foundation::Collections::IMap<::Platform::String^, ::Platform::Object^>^> t) 
      {	
        try
        {
          auto result = t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [result](NodeUtils::InvokeCallbackDelegate invokeCallback) {


            TryCatch tryCatch;
            Local<Value> error; 
            Local<Value> arg1 = NodeRT::Collections::MapWrapper<::Platform::String^,::Platform::Object^>::CreateMapWrapper(result, 
            [](::Platform::String^ val) -> Local<Value> {
              return NodeRT::Utils::NewString(val->Data());
            },
            [](Local<Value> value) -> bool {
              return value->IsString();
            },
            [](Local<Value> value) -> ::Platform::String^ {
              return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
            },
            [](::Platform::Object^ val) -> Local<Value> {
              return CreateOpaqueWrapper(val);
            },
            [](Local<Value> value) -> bool {
              return NodeRT::Utils::IsWinRtWrapperOf<::Platform::Object^>(value);
            },
            [](Local<Value> value) -> ::Platform::Object^ {
              return dynamic_cast<::Platform::Object^>(NodeRT::Utils::GetObjectInstance(value));
            }
          );
            if (tryCatch.HasCaught())
            {
              error = Nan::To<Object>(tryCatch.Exception()).ToLocalChecked();
            }
            else 
            {
              error = Undefined();
            }
            if (arg1.IsEmpty()) arg1 = Undefined();
            Local<Value> args[] = {error, arg1};
			// TODO: this is ugly! Needed due to the possibility of expception occuring inside object convertors
			// can be fixed by wrapping the conversion code in a function and calling it on the fly
			// we must clear the try catch block here so the invoked inner method exception won't get swollen (issue #52) 
			tryCatch.~TryCatch();

		    
            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
             
            Local<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Local<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });
    }
    static void SavePropertiesAsync(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::IStorageItemExtraProperties^>(info.This()))
      {
        return;
      }

      if (info.Length() == 0 || !info[info.Length() -1]->IsFunction())
      {
          Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return;
      }

      IStorageItemExtraProperties *wrapper = IStorageItemExtraProperties::Unwrap<IStorageItemExtraProperties>(info.This());

      ::Windows::Foundation::IAsyncAction^ op;
    

      if (info.Length() == 2
        && (NodeRT::Utils::IsWinRtWrapperOf<::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^>(info[0]) || info[0]->IsObject()))
      {
        try
        {
          ::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^ arg0 = 
            [] (v8::Local<v8::Value> value) -> ::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^
            {
              if (value->IsObject())
              {
                return NodeRT::Collections::JsObjectToWinrtMap<::Platform::Object^>(value.As<Object>(), 
                 [](Local<Value> value) -> bool {
                   return NodeRT::Utils::IsWinRtWrapperOf<::Platform::Object^>(value);
                 },
                 [](Local<Value> value) -> ::Platform::Object^ {
                   return dynamic_cast<::Platform::Object^>(NodeRT::Utils::GetObjectInstance(value));
                 }
                );
              }
              else
              {
                return dynamic_cast<::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^>(NodeRT::Utils::GetObjectInstance(value));
              }
            } (info[0]);
          
          op = wrapper->_instance->SavePropertiesAsync(arg0);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else if (info.Length() == 1)
      {
        try
        {
          op = wrapper->_instance->SavePropertiesAsync();
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(info[info.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<void> t) 
      {	
        try
        {
          t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [](NodeUtils::InvokeCallbackDelegate invokeCallback) {


            Local<Value> args[] = {Undefined()};

		    
            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
             
            Local<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Local<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });
    }
  





  private:
    ::Windows::Storage::FileProperties::IStorageItemExtraProperties^ _instance;
    static Persistent<FunctionTemplate> s_constructorTemplate;

    friend v8::Local<v8::Value> WrapIStorageItemExtraProperties(::Windows::Storage::FileProperties::IStorageItemExtraProperties^ wintRtInstance);
    friend ::Windows::Storage::FileProperties::IStorageItemExtraProperties^ UnwrapIStorageItemExtraProperties(Local<Value> value);
  };
  Persistent<FunctionTemplate> IStorageItemExtraProperties::s_constructorTemplate;

  v8::Local<v8::Value> WrapIStorageItemExtraProperties(::Windows::Storage::FileProperties::IStorageItemExtraProperties^ winRtInstance)
  {
    EscapableHandleScope scope;

    if (winRtInstance == nullptr)
    {
      return scope.Escape(Undefined());
    }

    Local<Value> opaqueWrapper = CreateOpaqueWrapper(winRtInstance);
    Local<Value> args[] = {opaqueWrapper};
    Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(IStorageItemExtraProperties::s_constructorTemplate);
    return scope.Escape(Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(),_countof(args), args).ToLocalChecked());
  }

  ::Windows::Storage::FileProperties::IStorageItemExtraProperties^ UnwrapIStorageItemExtraProperties(Local<Value> value)
  {
     return IStorageItemExtraProperties::Unwrap<IStorageItemExtraProperties>(Nan::To<Object>(value).ToLocalChecked())->_instance;
  }

  void InitIStorageItemExtraProperties(Local<Object> exports)
  {
    IStorageItemExtraProperties::Init(exports);
  }

  class MusicProperties : public WrapperBase
  {
  public:    
    static void Init(const Local<Object> exports)
    {
      HandleScope scope;
      
      Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(New);
      s_constructorTemplate.Reset(localRef);
      localRef->SetClassName(Nan::New<String>("MusicProperties").ToLocalChecked());
      localRef->InstanceTemplate()->SetInternalFieldCount(1);
      
      Local<Function> func;
      Local<FunctionTemplate> funcTemplate;
                  
      Nan::SetPrototypeMethod(localRef, "retrievePropertiesAsync", RetrievePropertiesAsync);
      Nan::SetPrototypeMethod(localRef, "savePropertiesAsync", SavePropertiesAsync);
      
                  
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("rating").ToLocalChecked(), RatingGetter, RatingSetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("albumArtist").ToLocalChecked(), AlbumArtistGetter, AlbumArtistSetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("album").ToLocalChecked(), AlbumGetter, AlbumSetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("artist").ToLocalChecked(), ArtistGetter, ArtistSetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("publisher").ToLocalChecked(), PublisherGetter, PublisherSetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("year").ToLocalChecked(), YearGetter, YearSetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("trackNumber").ToLocalChecked(), TrackNumberGetter, TrackNumberSetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("title").ToLocalChecked(), TitleGetter, TitleSetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("subtitle").ToLocalChecked(), SubtitleGetter, SubtitleSetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("producers").ToLocalChecked(), ProducersGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("composers").ToLocalChecked(), ComposersGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("conductors").ToLocalChecked(), ConductorsGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("duration").ToLocalChecked(), DurationGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("writers").ToLocalChecked(), WritersGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("genre").ToLocalChecked(), GenreGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("bitrate").ToLocalChecked(), BitrateGetter);
      
      Local<Object> constructor = Nan::To<Object>(Nan::GetFunction(localRef).ToLocalChecked()).ToLocalChecked();
	  Nan::SetMethod(constructor, "castFrom", CastFrom);


      Nan::Set(exports, Nan::New<String>("MusicProperties").ToLocalChecked(), constructor);
    }


    virtual ::Platform::Object^ GetObjectInstance() const override
    {
      return _instance;
    }

  private:
    
    MusicProperties(::Windows::Storage::FileProperties::MusicProperties^ instance)
    {
      _instance = instance;
    }
    
    
    static void New(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

	    Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(s_constructorTemplate);

      // in case the constructor was called without the new operator
      if (!localRef->HasInstance(info.This()))
      {
        if (info.Length() > 0)
        {
          std::unique_ptr<Local<Value> []> constructorArgs(new Local<Value>[info.Length()]);

          Local<Value> *argsPtr = constructorArgs.get();
          for (int i = 0; i < info.Length(); i++)
          {
            argsPtr[i] = info[i];
          }

		  MaybeLocal<Object> res = Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(), info.Length(), constructorArgs.get());
		  if (res.IsEmpty())
		  {
			  return;
		  }
		  info.GetReturnValue().Set(res.ToLocalChecked());
		  return;
		}
		else
		{
          MaybeLocal<Object> res = Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(), info.Length(), nullptr);
          if (res.IsEmpty())
          {
            return;
          }
          info.GetReturnValue().Set(res.ToLocalChecked());
          return;
        }
      }
      
      ::Windows::Storage::FileProperties::MusicProperties^ winRtInstance;


      if (info.Length() == 1 && OpaqueWrapper::IsOpaqueWrapper(info[0]) &&
        NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info[0]))
      {
        try 
        {
          winRtInstance = (::Windows::Storage::FileProperties::MusicProperties^) NodeRT::Utils::GetObjectInstance(info[0]);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Invalid arguments, no suitable constructor found")));
	    	return;
      }

      NodeRT::Utils::SetHiddenValue(info.This(), Nan::New<String>("__winRtInstance__").ToLocalChecked(), True());

      MusicProperties *wrapperInstance = new MusicProperties(winRtInstance);
      wrapperInstance->Wrap(info.This());

      info.GetReturnValue().Set(info.This());
    }


	
    static void CastFrom(Nan::NAN_METHOD_ARGS_TYPE info)
    {
		HandleScope scope;
		if (info.Length() < 1 || !NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info[0]))
		{
			Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Invalid arguments, no object provided, or given object could not be casted to requested type")));
			return;
		}

		::Windows::Storage::FileProperties::MusicProperties^ winRtInstance;
		try
		{
			winRtInstance = (::Windows::Storage::FileProperties::MusicProperties^) NodeRT::Utils::GetObjectInstance(info[0]);
		}
		catch (Platform::Exception ^exception)
		{
			NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
			return;
		}

		info.GetReturnValue().Set(WrapMusicProperties(winRtInstance));
    }


    static void RetrievePropertiesAsync(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      if (info.Length() == 0 || !info[info.Length() -1]->IsFunction())
      {
          Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      ::Windows::Foundation::IAsyncOperation<::Windows::Foundation::Collections::IMap<::Platform::String^, ::Platform::Object^>^>^ op;
    

      if (info.Length() == 2
        && (NodeRT::Utils::IsWinRtWrapperOf<::Windows::Foundation::Collections::IIterable<::Platform::String^>^>(info[0]) || info[0]->IsArray()))
      {
        try
        {
          ::Windows::Foundation::Collections::IIterable<::Platform::String^>^ arg0 = 
            [] (v8::Local<v8::Value> value) -> ::Windows::Foundation::Collections::IIterable<::Platform::String^>^
            {
              if (value->IsArray())
              {
                return NodeRT::Collections::JsArrayToWinrtVector<::Platform::String^>(value.As<Array>(), 
                 [](Local<Value> value) -> bool {
                   return (!NodeRT::Utils::IsWinRtWrapper(value));
                 },
                 [](Local<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));
              }
            } (info[0]);
          
          op = wrapper->_instance->RetrievePropertiesAsync(arg0);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(info[info.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<::Windows::Foundation::Collections::IMap<::Platform::String^, ::Platform::Object^>^> t) 
      {	
        try
        {
          auto result = t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [result](NodeUtils::InvokeCallbackDelegate invokeCallback) {


            TryCatch tryCatch;
            Local<Value> error; 
            Local<Value> arg1 = NodeRT::Collections::MapWrapper<::Platform::String^,::Platform::Object^>::CreateMapWrapper(result, 
            [](::Platform::String^ val) -> Local<Value> {
              return NodeRT::Utils::NewString(val->Data());
            },
            [](Local<Value> value) -> bool {
              return value->IsString();
            },
            [](Local<Value> value) -> ::Platform::String^ {
              return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
            },
            [](::Platform::Object^ val) -> Local<Value> {
              return CreateOpaqueWrapper(val);
            },
            [](Local<Value> value) -> bool {
              return NodeRT::Utils::IsWinRtWrapperOf<::Platform::Object^>(value);
            },
            [](Local<Value> value) -> ::Platform::Object^ {
              return dynamic_cast<::Platform::Object^>(NodeRT::Utils::GetObjectInstance(value));
            }
          );
            if (tryCatch.HasCaught())
            {
              error = Nan::To<Object>(tryCatch.Exception()).ToLocalChecked();
            }
            else 
            {
              error = Undefined();
            }
            if (arg1.IsEmpty()) arg1 = Undefined();
            Local<Value> args[] = {error, arg1};
			// TODO: this is ugly! Needed due to the possibility of expception occuring inside object convertors
			// can be fixed by wrapping the conversion code in a function and calling it on the fly
			// we must clear the try catch block here so the invoked inner method exception won't get swollen (issue #52) 
			tryCatch.~TryCatch();

		    
            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
             
            Local<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Local<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });
    }
    static void SavePropertiesAsync(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      if (info.Length() == 0 || !info[info.Length() -1]->IsFunction())
      {
          Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      ::Windows::Foundation::IAsyncAction^ op;
    

      if (info.Length() == 2
        && (NodeRT::Utils::IsWinRtWrapperOf<::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^>(info[0]) || info[0]->IsObject()))
      {
        try
        {
          ::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^ arg0 = 
            [] (v8::Local<v8::Value> value) -> ::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^
            {
              if (value->IsObject())
              {
                return NodeRT::Collections::JsObjectToWinrtMap<::Platform::Object^>(value.As<Object>(), 
                 [](Local<Value> value) -> bool {
                   return NodeRT::Utils::IsWinRtWrapperOf<::Platform::Object^>(value);
                 },
                 [](Local<Value> value) -> ::Platform::Object^ {
                   return dynamic_cast<::Platform::Object^>(NodeRT::Utils::GetObjectInstance(value));
                 }
                );
              }
              else
              {
                return dynamic_cast<::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^>(NodeRT::Utils::GetObjectInstance(value));
              }
            } (info[0]);
          
          op = wrapper->_instance->SavePropertiesAsync(arg0);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else if (info.Length() == 1)
      {
        try
        {
          op = wrapper->_instance->SavePropertiesAsync();
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(info[info.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<void> t) 
      {	
        try
        {
          t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [](NodeUtils::InvokeCallbackDelegate invokeCallback) {


            Local<Value> args[] = {Undefined()};

		    
            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
             
            Local<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Local<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });
    }
  



    static void RatingGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        unsigned int result = wrapper->_instance->Rating;
        info.GetReturnValue().Set(Nan::New<Integer>(result));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void RatingSetter(Local<String> property, Local<Value> value, const Nan::PropertyCallbackInfo<void> &info)
    {
      HandleScope scope;
      
      if (!value->IsUint32())
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Value to set is of unexpected type")));
        return;
      }

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        
        unsigned int winRtValue = static_cast<unsigned int>(Nan::To<uint32_t>(value).FromMaybe(0));

        wrapper->_instance->Rating = winRtValue;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
      }
    }
    
    static void AlbumArtistGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        Platform::String^ result = wrapper->_instance->AlbumArtist;
        info.GetReturnValue().Set(NodeRT::Utils::NewString(result->Data()));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void AlbumArtistSetter(Local<String> property, Local<Value> value, const Nan::PropertyCallbackInfo<void> &info)
    {
      HandleScope scope;
      
      if (!value->IsString())
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Value to set is of unexpected type")));
        return;
      }

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        
        Platform::String^ winRtValue = ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));

        wrapper->_instance->AlbumArtist = winRtValue;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
      }
    }
    
    static void AlbumGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        Platform::String^ result = wrapper->_instance->Album;
        info.GetReturnValue().Set(NodeRT::Utils::NewString(result->Data()));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void AlbumSetter(Local<String> property, Local<Value> value, const Nan::PropertyCallbackInfo<void> &info)
    {
      HandleScope scope;
      
      if (!value->IsString())
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Value to set is of unexpected type")));
        return;
      }

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        
        Platform::String^ winRtValue = ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));

        wrapper->_instance->Album = winRtValue;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
      }
    }
    
    static void ArtistGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        Platform::String^ result = wrapper->_instance->Artist;
        info.GetReturnValue().Set(NodeRT::Utils::NewString(result->Data()));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void ArtistSetter(Local<String> property, Local<Value> value, const Nan::PropertyCallbackInfo<void> &info)
    {
      HandleScope scope;
      
      if (!value->IsString())
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Value to set is of unexpected type")));
        return;
      }

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        
        Platform::String^ winRtValue = ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));

        wrapper->_instance->Artist = winRtValue;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
      }
    }
    
    static void PublisherGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        Platform::String^ result = wrapper->_instance->Publisher;
        info.GetReturnValue().Set(NodeRT::Utils::NewString(result->Data()));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void PublisherSetter(Local<String> property, Local<Value> value, const Nan::PropertyCallbackInfo<void> &info)
    {
      HandleScope scope;
      
      if (!value->IsString())
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Value to set is of unexpected type")));
        return;
      }

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        
        Platform::String^ winRtValue = ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));

        wrapper->_instance->Publisher = winRtValue;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
      }
    }
    
    static void YearGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        unsigned int result = wrapper->_instance->Year;
        info.GetReturnValue().Set(Nan::New<Integer>(result));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void YearSetter(Local<String> property, Local<Value> value, const Nan::PropertyCallbackInfo<void> &info)
    {
      HandleScope scope;
      
      if (!value->IsUint32())
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Value to set is of unexpected type")));
        return;
      }

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        
        unsigned int winRtValue = static_cast<unsigned int>(Nan::To<uint32_t>(value).FromMaybe(0));

        wrapper->_instance->Year = winRtValue;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
      }
    }
    
    static void TrackNumberGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        unsigned int result = wrapper->_instance->TrackNumber;
        info.GetReturnValue().Set(Nan::New<Integer>(result));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void TrackNumberSetter(Local<String> property, Local<Value> value, const Nan::PropertyCallbackInfo<void> &info)
    {
      HandleScope scope;
      
      if (!value->IsUint32())
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Value to set is of unexpected type")));
        return;
      }

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        
        unsigned int winRtValue = static_cast<unsigned int>(Nan::To<uint32_t>(value).FromMaybe(0));

        wrapper->_instance->TrackNumber = winRtValue;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
      }
    }
    
    static void TitleGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        Platform::String^ result = wrapper->_instance->Title;
        info.GetReturnValue().Set(NodeRT::Utils::NewString(result->Data()));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void TitleSetter(Local<String> property, Local<Value> value, const Nan::PropertyCallbackInfo<void> &info)
    {
      HandleScope scope;
      
      if (!value->IsString())
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Value to set is of unexpected type")));
        return;
      }

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        
        Platform::String^ winRtValue = ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));

        wrapper->_instance->Title = winRtValue;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
      }
    }
    
    static void SubtitleGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        Platform::String^ result = wrapper->_instance->Subtitle;
        info.GetReturnValue().Set(NodeRT::Utils::NewString(result->Data()));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void SubtitleSetter(Local<String> property, Local<Value> value, const Nan::PropertyCallbackInfo<void> &info)
    {
      HandleScope scope;
      
      if (!value->IsString())
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Value to set is of unexpected type")));
        return;
      }

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        
        Platform::String^ winRtValue = ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));

        wrapper->_instance->Subtitle = winRtValue;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
      }
    }
    
    static void ProducersGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        ::Windows::Foundation::Collections::IVector<::Platform::String^>^ result = wrapper->_instance->Producers;
        info.GetReturnValue().Set(NodeRT::Collections::VectorWrapper<::Platform::String^>::CreateVectorWrapper(result, 
            [](::Platform::String^ val) -> Local<Value> {
              return NodeRT::Utils::NewString(val->Data());
            },
            [](Local<Value> value) -> bool {
              return value->IsString();
            },
            [](Local<Value> value) -> ::Platform::String^ {
              return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
            }
          ));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void ComposersGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        ::Windows::Foundation::Collections::IVector<::Platform::String^>^ result = wrapper->_instance->Composers;
        info.GetReturnValue().Set(NodeRT::Collections::VectorWrapper<::Platform::String^>::CreateVectorWrapper(result, 
            [](::Platform::String^ val) -> Local<Value> {
              return NodeRT::Utils::NewString(val->Data());
            },
            [](Local<Value> value) -> bool {
              return value->IsString();
            },
            [](Local<Value> value) -> ::Platform::String^ {
              return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
            }
          ));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void ConductorsGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        ::Windows::Foundation::Collections::IVector<::Platform::String^>^ result = wrapper->_instance->Conductors;
        info.GetReturnValue().Set(NodeRT::Collections::VectorWrapper<::Platform::String^>::CreateVectorWrapper(result, 
            [](::Platform::String^ val) -> Local<Value> {
              return NodeRT::Utils::NewString(val->Data());
            },
            [](Local<Value> value) -> bool {
              return value->IsString();
            },
            [](Local<Value> value) -> ::Platform::String^ {
              return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
            }
          ));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void DurationGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        ::Windows::Foundation::TimeSpan result = wrapper->_instance->Duration;
        info.GetReturnValue().Set(Nan::New<Number>(result.Duration/10000.0));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void WritersGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        ::Windows::Foundation::Collections::IVector<::Platform::String^>^ result = wrapper->_instance->Writers;
        info.GetReturnValue().Set(NodeRT::Collections::VectorWrapper<::Platform::String^>::CreateVectorWrapper(result, 
            [](::Platform::String^ val) -> Local<Value> {
              return NodeRT::Utils::NewString(val->Data());
            },
            [](Local<Value> value) -> bool {
              return value->IsString();
            },
            [](Local<Value> value) -> ::Platform::String^ {
              return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
            }
          ));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void GenreGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        ::Windows::Foundation::Collections::IVector<::Platform::String^>^ result = wrapper->_instance->Genre;
        info.GetReturnValue().Set(NodeRT::Collections::VectorWrapper<::Platform::String^>::CreateVectorWrapper(result, 
            [](::Platform::String^ val) -> Local<Value> {
              return NodeRT::Utils::NewString(val->Data());
            },
            [](Local<Value> value) -> bool {
              return value->IsString();
            },
            [](Local<Value> value) -> ::Platform::String^ {
              return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
            }
          ));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void BitrateGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::MusicProperties^>(info.This()))
      {
        return;
      }

      MusicProperties *wrapper = MusicProperties::Unwrap<MusicProperties>(info.This());

      try 
      {
        unsigned int result = wrapper->_instance->Bitrate;
        info.GetReturnValue().Set(Nan::New<Integer>(result));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    


  private:
    ::Windows::Storage::FileProperties::MusicProperties^ _instance;
    static Persistent<FunctionTemplate> s_constructorTemplate;

    friend v8::Local<v8::Value> WrapMusicProperties(::Windows::Storage::FileProperties::MusicProperties^ wintRtInstance);
    friend ::Windows::Storage::FileProperties::MusicProperties^ UnwrapMusicProperties(Local<Value> value);
  };
  Persistent<FunctionTemplate> MusicProperties::s_constructorTemplate;

  v8::Local<v8::Value> WrapMusicProperties(::Windows::Storage::FileProperties::MusicProperties^ winRtInstance)
  {
    EscapableHandleScope scope;

    if (winRtInstance == nullptr)
    {
      return scope.Escape(Undefined());
    }

    Local<Value> opaqueWrapper = CreateOpaqueWrapper(winRtInstance);
    Local<Value> args[] = {opaqueWrapper};
    Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(MusicProperties::s_constructorTemplate);
    return scope.Escape(Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(),_countof(args), args).ToLocalChecked());
  }

  ::Windows::Storage::FileProperties::MusicProperties^ UnwrapMusicProperties(Local<Value> value)
  {
     return MusicProperties::Unwrap<MusicProperties>(Nan::To<Object>(value).ToLocalChecked())->_instance;
  }

  void InitMusicProperties(Local<Object> exports)
  {
    MusicProperties::Init(exports);
  }

  class VideoProperties : public WrapperBase
  {
  public:    
    static void Init(const Local<Object> exports)
    {
      HandleScope scope;
      
      Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(New);
      s_constructorTemplate.Reset(localRef);
      localRef->SetClassName(Nan::New<String>("VideoProperties").ToLocalChecked());
      localRef->InstanceTemplate()->SetInternalFieldCount(1);
      
      Local<Function> func;
      Local<FunctionTemplate> funcTemplate;
                  
      Nan::SetPrototypeMethod(localRef, "retrievePropertiesAsync", RetrievePropertiesAsync);
      Nan::SetPrototypeMethod(localRef, "savePropertiesAsync", SavePropertiesAsync);
      
                  
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("year").ToLocalChecked(), YearGetter, YearSetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("title").ToLocalChecked(), TitleGetter, TitleSetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("subtitle").ToLocalChecked(), SubtitleGetter, SubtitleSetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("publisher").ToLocalChecked(), PublisherGetter, PublisherSetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("rating").ToLocalChecked(), RatingGetter, RatingSetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("latitude").ToLocalChecked(), LatitudeGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("orientation").ToLocalChecked(), OrientationGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("duration").ToLocalChecked(), DurationGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("bitrate").ToLocalChecked(), BitrateGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("producers").ToLocalChecked(), ProducersGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("directors").ToLocalChecked(), DirectorsGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("height").ToLocalChecked(), HeightGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("width").ToLocalChecked(), WidthGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("longitude").ToLocalChecked(), LongitudeGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("writers").ToLocalChecked(), WritersGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("keywords").ToLocalChecked(), KeywordsGetter);
      
      Local<Object> constructor = Nan::To<Object>(Nan::GetFunction(localRef).ToLocalChecked()).ToLocalChecked();
	  Nan::SetMethod(constructor, "castFrom", CastFrom);


      Nan::Set(exports, Nan::New<String>("VideoProperties").ToLocalChecked(), constructor);
    }


    virtual ::Platform::Object^ GetObjectInstance() const override
    {
      return _instance;
    }

  private:
    
    VideoProperties(::Windows::Storage::FileProperties::VideoProperties^ instance)
    {
      _instance = instance;
    }
    
    
    static void New(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

	    Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(s_constructorTemplate);

      // in case the constructor was called without the new operator
      if (!localRef->HasInstance(info.This()))
      {
        if (info.Length() > 0)
        {
          std::unique_ptr<Local<Value> []> constructorArgs(new Local<Value>[info.Length()]);

          Local<Value> *argsPtr = constructorArgs.get();
          for (int i = 0; i < info.Length(); i++)
          {
            argsPtr[i] = info[i];
          }

		  MaybeLocal<Object> res = Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(), info.Length(), constructorArgs.get());
		  if (res.IsEmpty())
		  {
			  return;
		  }
		  info.GetReturnValue().Set(res.ToLocalChecked());
		  return;
		}
		else
		{
          MaybeLocal<Object> res = Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(), info.Length(), nullptr);
          if (res.IsEmpty())
          {
            return;
          }
          info.GetReturnValue().Set(res.ToLocalChecked());
          return;
        }
      }
      
      ::Windows::Storage::FileProperties::VideoProperties^ winRtInstance;


      if (info.Length() == 1 && OpaqueWrapper::IsOpaqueWrapper(info[0]) &&
        NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info[0]))
      {
        try 
        {
          winRtInstance = (::Windows::Storage::FileProperties::VideoProperties^) NodeRT::Utils::GetObjectInstance(info[0]);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Invalid arguments, no suitable constructor found")));
	    	return;
      }

      NodeRT::Utils::SetHiddenValue(info.This(), Nan::New<String>("__winRtInstance__").ToLocalChecked(), True());

      VideoProperties *wrapperInstance = new VideoProperties(winRtInstance);
      wrapperInstance->Wrap(info.This());

      info.GetReturnValue().Set(info.This());
    }


	
    static void CastFrom(Nan::NAN_METHOD_ARGS_TYPE info)
    {
		HandleScope scope;
		if (info.Length() < 1 || !NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info[0]))
		{
			Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Invalid arguments, no object provided, or given object could not be casted to requested type")));
			return;
		}

		::Windows::Storage::FileProperties::VideoProperties^ winRtInstance;
		try
		{
			winRtInstance = (::Windows::Storage::FileProperties::VideoProperties^) NodeRT::Utils::GetObjectInstance(info[0]);
		}
		catch (Platform::Exception ^exception)
		{
			NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
			return;
		}

		info.GetReturnValue().Set(WrapVideoProperties(winRtInstance));
    }


    static void RetrievePropertiesAsync(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info.This()))
      {
        return;
      }

      if (info.Length() == 0 || !info[info.Length() -1]->IsFunction())
      {
          Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return;
      }

      VideoProperties *wrapper = VideoProperties::Unwrap<VideoProperties>(info.This());

      ::Windows::Foundation::IAsyncOperation<::Windows::Foundation::Collections::IMap<::Platform::String^, ::Platform::Object^>^>^ op;
    

      if (info.Length() == 2
        && (NodeRT::Utils::IsWinRtWrapperOf<::Windows::Foundation::Collections::IIterable<::Platform::String^>^>(info[0]) || info[0]->IsArray()))
      {
        try
        {
          ::Windows::Foundation::Collections::IIterable<::Platform::String^>^ arg0 = 
            [] (v8::Local<v8::Value> value) -> ::Windows::Foundation::Collections::IIterable<::Platform::String^>^
            {
              if (value->IsArray())
              {
                return NodeRT::Collections::JsArrayToWinrtVector<::Platform::String^>(value.As<Array>(), 
                 [](Local<Value> value) -> bool {
                   return (!NodeRT::Utils::IsWinRtWrapper(value));
                 },
                 [](Local<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));
              }
            } (info[0]);
          
          op = wrapper->_instance->RetrievePropertiesAsync(arg0);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(info[info.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<::Windows::Foundation::Collections::IMap<::Platform::String^, ::Platform::Object^>^> t) 
      {	
        try
        {
          auto result = t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [result](NodeUtils::InvokeCallbackDelegate invokeCallback) {


            TryCatch tryCatch;
            Local<Value> error; 
            Local<Value> arg1 = NodeRT::Collections::MapWrapper<::Platform::String^,::Platform::Object^>::CreateMapWrapper(result, 
            [](::Platform::String^ val) -> Local<Value> {
              return NodeRT::Utils::NewString(val->Data());
            },
            [](Local<Value> value) -> bool {
              return value->IsString();
            },
            [](Local<Value> value) -> ::Platform::String^ {
              return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
            },
            [](::Platform::Object^ val) -> Local<Value> {
              return CreateOpaqueWrapper(val);
            },
            [](Local<Value> value) -> bool {
              return NodeRT::Utils::IsWinRtWrapperOf<::Platform::Object^>(value);
            },
            [](Local<Value> value) -> ::Platform::Object^ {
              return dynamic_cast<::Platform::Object^>(NodeRT::Utils::GetObjectInstance(value));
            }
          );
            if (tryCatch.HasCaught())
            {
              error = Nan::To<Object>(tryCatch.Exception()).ToLocalChecked();
            }
            else 
            {
              error = Undefined();
            }
            if (arg1.IsEmpty()) arg1 = Undefined();
            Local<Value> args[] = {error, arg1};
			// TODO: this is ugly! Needed due to the possibility of expception occuring inside object convertors
			// can be fixed by wrapping the conversion code in a function and calling it on the fly
			// we must clear the try catch block here so the invoked inner method exception won't get swollen (issue #52) 
			tryCatch.~TryCatch();

		    
            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
             
            Local<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Local<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });
    }
    static void SavePropertiesAsync(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info.This()))
      {
        return;
      }

      if (info.Length() == 0 || !info[info.Length() -1]->IsFunction())
      {
          Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return;
      }

      VideoProperties *wrapper = VideoProperties::Unwrap<VideoProperties>(info.This());

      ::Windows::Foundation::IAsyncAction^ op;
    

      if (info.Length() == 2
        && (NodeRT::Utils::IsWinRtWrapperOf<::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^>(info[0]) || info[0]->IsObject()))
      {
        try
        {
          ::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^ arg0 = 
            [] (v8::Local<v8::Value> value) -> ::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^
            {
              if (value->IsObject())
              {
                return NodeRT::Collections::JsObjectToWinrtMap<::Platform::Object^>(value.As<Object>(), 
                 [](Local<Value> value) -> bool {
                   return NodeRT::Utils::IsWinRtWrapperOf<::Platform::Object^>(value);
                 },
                 [](Local<Value> value) -> ::Platform::Object^ {
                   return dynamic_cast<::Platform::Object^>(NodeRT::Utils::GetObjectInstance(value));
                 }
                );
              }
              else
              {
                return dynamic_cast<::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^>(NodeRT::Utils::GetObjectInstance(value));
              }
            } (info[0]);
          
          op = wrapper->_instance->SavePropertiesAsync(arg0);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else if (info.Length() == 1)
      {
        try
        {
          op = wrapper->_instance->SavePropertiesAsync();
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(info[info.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<void> t) 
      {	
        try
        {
          t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [](NodeUtils::InvokeCallbackDelegate invokeCallback) {


            Local<Value> args[] = {Undefined()};

		    
            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
             
            Local<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Local<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });
    }
  



    static void YearGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info.This()))
      {
        return;
      }

      VideoProperties *wrapper = VideoProperties::Unwrap<VideoProperties>(info.This());

      try 
      {
        unsigned int result = wrapper->_instance->Year;
        info.GetReturnValue().Set(Nan::New<Integer>(result));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void YearSetter(Local<String> property, Local<Value> value, const Nan::PropertyCallbackInfo<void> &info)
    {
      HandleScope scope;
      
      if (!value->IsUint32())
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Value to set is of unexpected type")));
        return;
      }

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info.This()))
      {
        return;
      }

      VideoProperties *wrapper = VideoProperties::Unwrap<VideoProperties>(info.This());

      try 
      {
        
        unsigned int winRtValue = static_cast<unsigned int>(Nan::To<uint32_t>(value).FromMaybe(0));

        wrapper->_instance->Year = winRtValue;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
      }
    }
    
    static void TitleGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info.This()))
      {
        return;
      }

      VideoProperties *wrapper = VideoProperties::Unwrap<VideoProperties>(info.This());

      try 
      {
        Platform::String^ result = wrapper->_instance->Title;
        info.GetReturnValue().Set(NodeRT::Utils::NewString(result->Data()));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void TitleSetter(Local<String> property, Local<Value> value, const Nan::PropertyCallbackInfo<void> &info)
    {
      HandleScope scope;
      
      if (!value->IsString())
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Value to set is of unexpected type")));
        return;
      }

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info.This()))
      {
        return;
      }

      VideoProperties *wrapper = VideoProperties::Unwrap<VideoProperties>(info.This());

      try 
      {
        
        Platform::String^ winRtValue = ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));

        wrapper->_instance->Title = winRtValue;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
      }
    }
    
    static void SubtitleGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info.This()))
      {
        return;
      }

      VideoProperties *wrapper = VideoProperties::Unwrap<VideoProperties>(info.This());

      try 
      {
        Platform::String^ result = wrapper->_instance->Subtitle;
        info.GetReturnValue().Set(NodeRT::Utils::NewString(result->Data()));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void SubtitleSetter(Local<String> property, Local<Value> value, const Nan::PropertyCallbackInfo<void> &info)
    {
      HandleScope scope;
      
      if (!value->IsString())
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Value to set is of unexpected type")));
        return;
      }

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info.This()))
      {
        return;
      }

      VideoProperties *wrapper = VideoProperties::Unwrap<VideoProperties>(info.This());

      try 
      {
        
        Platform::String^ winRtValue = ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));

        wrapper->_instance->Subtitle = winRtValue;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
      }
    }
    
    static void PublisherGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info.This()))
      {
        return;
      }

      VideoProperties *wrapper = VideoProperties::Unwrap<VideoProperties>(info.This());

      try 
      {
        Platform::String^ result = wrapper->_instance->Publisher;
        info.GetReturnValue().Set(NodeRT::Utils::NewString(result->Data()));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void PublisherSetter(Local<String> property, Local<Value> value, const Nan::PropertyCallbackInfo<void> &info)
    {
      HandleScope scope;
      
      if (!value->IsString())
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Value to set is of unexpected type")));
        return;
      }

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info.This()))
      {
        return;
      }

      VideoProperties *wrapper = VideoProperties::Unwrap<VideoProperties>(info.This());

      try 
      {
        
        Platform::String^ winRtValue = ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));

        wrapper->_instance->Publisher = winRtValue;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
      }
    }
    
    static void RatingGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info.This()))
      {
        return;
      }

      VideoProperties *wrapper = VideoProperties::Unwrap<VideoProperties>(info.This());

      try 
      {
        unsigned int result = wrapper->_instance->Rating;
        info.GetReturnValue().Set(Nan::New<Integer>(result));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void RatingSetter(Local<String> property, Local<Value> value, const Nan::PropertyCallbackInfo<void> &info)
    {
      HandleScope scope;
      
      if (!value->IsUint32())
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Value to set is of unexpected type")));
        return;
      }

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info.This()))
      {
        return;
      }

      VideoProperties *wrapper = VideoProperties::Unwrap<VideoProperties>(info.This());

      try 
      {
        
        unsigned int winRtValue = static_cast<unsigned int>(Nan::To<uint32_t>(value).FromMaybe(0));

        wrapper->_instance->Rating = winRtValue;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
      }
    }
    
    static void LatitudeGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info.This()))
      {
        return;
      }

      VideoProperties *wrapper = VideoProperties::Unwrap<VideoProperties>(info.This());

      try 
      {
        ::Platform::IBox<double>^ result = wrapper->_instance->Latitude;
        info.GetReturnValue().Set(result ? static_cast<Local<Value>>(Nan::New<Number>(static_cast<double>(result->Value))) : Undefined());
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void OrientationGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info.This()))
      {
        return;
      }

      VideoProperties *wrapper = VideoProperties::Unwrap<VideoProperties>(info.This());

      try 
      {
        ::Windows::Storage::FileProperties::VideoOrientation result = wrapper->_instance->Orientation;
        info.GetReturnValue().Set(Nan::New<Integer>(static_cast<int>(result)));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void DurationGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info.This()))
      {
        return;
      }

      VideoProperties *wrapper = VideoProperties::Unwrap<VideoProperties>(info.This());

      try 
      {
        ::Windows::Foundation::TimeSpan result = wrapper->_instance->Duration;
        info.GetReturnValue().Set(Nan::New<Number>(result.Duration/10000.0));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void BitrateGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info.This()))
      {
        return;
      }

      VideoProperties *wrapper = VideoProperties::Unwrap<VideoProperties>(info.This());

      try 
      {
        unsigned int result = wrapper->_instance->Bitrate;
        info.GetReturnValue().Set(Nan::New<Integer>(result));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void ProducersGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info.This()))
      {
        return;
      }

      VideoProperties *wrapper = VideoProperties::Unwrap<VideoProperties>(info.This());

      try 
      {
        ::Windows::Foundation::Collections::IVector<::Platform::String^>^ result = wrapper->_instance->Producers;
        info.GetReturnValue().Set(NodeRT::Collections::VectorWrapper<::Platform::String^>::CreateVectorWrapper(result, 
            [](::Platform::String^ val) -> Local<Value> {
              return NodeRT::Utils::NewString(val->Data());
            },
            [](Local<Value> value) -> bool {
              return value->IsString();
            },
            [](Local<Value> value) -> ::Platform::String^ {
              return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
            }
          ));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void DirectorsGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info.This()))
      {
        return;
      }

      VideoProperties *wrapper = VideoProperties::Unwrap<VideoProperties>(info.This());

      try 
      {
        ::Windows::Foundation::Collections::IVector<::Platform::String^>^ result = wrapper->_instance->Directors;
        info.GetReturnValue().Set(NodeRT::Collections::VectorWrapper<::Platform::String^>::CreateVectorWrapper(result, 
            [](::Platform::String^ val) -> Local<Value> {
              return NodeRT::Utils::NewString(val->Data());
            },
            [](Local<Value> value) -> bool {
              return value->IsString();
            },
            [](Local<Value> value) -> ::Platform::String^ {
              return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
            }
          ));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void HeightGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info.This()))
      {
        return;
      }

      VideoProperties *wrapper = VideoProperties::Unwrap<VideoProperties>(info.This());

      try 
      {
        unsigned int result = wrapper->_instance->Height;
        info.GetReturnValue().Set(Nan::New<Integer>(result));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void WidthGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info.This()))
      {
        return;
      }

      VideoProperties *wrapper = VideoProperties::Unwrap<VideoProperties>(info.This());

      try 
      {
        unsigned int result = wrapper->_instance->Width;
        info.GetReturnValue().Set(Nan::New<Integer>(result));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void LongitudeGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info.This()))
      {
        return;
      }

      VideoProperties *wrapper = VideoProperties::Unwrap<VideoProperties>(info.This());

      try 
      {
        ::Platform::IBox<double>^ result = wrapper->_instance->Longitude;
        info.GetReturnValue().Set(result ? static_cast<Local<Value>>(Nan::New<Number>(static_cast<double>(result->Value))) : Undefined());
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void WritersGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info.This()))
      {
        return;
      }

      VideoProperties *wrapper = VideoProperties::Unwrap<VideoProperties>(info.This());

      try 
      {
        ::Windows::Foundation::Collections::IVector<::Platform::String^>^ result = wrapper->_instance->Writers;
        info.GetReturnValue().Set(NodeRT::Collections::VectorWrapper<::Platform::String^>::CreateVectorWrapper(result, 
            [](::Platform::String^ val) -> Local<Value> {
              return NodeRT::Utils::NewString(val->Data());
            },
            [](Local<Value> value) -> bool {
              return value->IsString();
            },
            [](Local<Value> value) -> ::Platform::String^ {
              return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
            }
          ));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void KeywordsGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::VideoProperties^>(info.This()))
      {
        return;
      }

      VideoProperties *wrapper = VideoProperties::Unwrap<VideoProperties>(info.This());

      try 
      {
        ::Windows::Foundation::Collections::IVector<::Platform::String^>^ result = wrapper->_instance->Keywords;
        info.GetReturnValue().Set(NodeRT::Collections::VectorWrapper<::Platform::String^>::CreateVectorWrapper(result, 
            [](::Platform::String^ val) -> Local<Value> {
              return NodeRT::Utils::NewString(val->Data());
            },
            [](Local<Value> value) -> bool {
              return value->IsString();
            },
            [](Local<Value> value) -> ::Platform::String^ {
              return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
            }
          ));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    


  private:
    ::Windows::Storage::FileProperties::VideoProperties^ _instance;
    static Persistent<FunctionTemplate> s_constructorTemplate;

    friend v8::Local<v8::Value> WrapVideoProperties(::Windows::Storage::FileProperties::VideoProperties^ wintRtInstance);
    friend ::Windows::Storage::FileProperties::VideoProperties^ UnwrapVideoProperties(Local<Value> value);
  };
  Persistent<FunctionTemplate> VideoProperties::s_constructorTemplate;

  v8::Local<v8::Value> WrapVideoProperties(::Windows::Storage::FileProperties::VideoProperties^ winRtInstance)
  {
    EscapableHandleScope scope;

    if (winRtInstance == nullptr)
    {
      return scope.Escape(Undefined());
    }

    Local<Value> opaqueWrapper = CreateOpaqueWrapper(winRtInstance);
    Local<Value> args[] = {opaqueWrapper};
    Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(VideoProperties::s_constructorTemplate);
    return scope.Escape(Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(),_countof(args), args).ToLocalChecked());
  }

  ::Windows::Storage::FileProperties::VideoProperties^ UnwrapVideoProperties(Local<Value> value)
  {
     return VideoProperties::Unwrap<VideoProperties>(Nan::To<Object>(value).ToLocalChecked())->_instance;
  }

  void InitVideoProperties(Local<Object> exports)
  {
    VideoProperties::Init(exports);
  }

  class ImageProperties : public WrapperBase
  {
  public:    
    static void Init(const Local<Object> exports)
    {
      HandleScope scope;
      
      Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(New);
      s_constructorTemplate.Reset(localRef);
      localRef->SetClassName(Nan::New<String>("ImageProperties").ToLocalChecked());
      localRef->InstanceTemplate()->SetInternalFieldCount(1);
      
      Local<Function> func;
      Local<FunctionTemplate> funcTemplate;
                  
      Nan::SetPrototypeMethod(localRef, "retrievePropertiesAsync", RetrievePropertiesAsync);
      Nan::SetPrototypeMethod(localRef, "savePropertiesAsync", SavePropertiesAsync);
      
                  
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("title").ToLocalChecked(), TitleGetter, TitleSetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("rating").ToLocalChecked(), RatingGetter, RatingSetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("dateTaken").ToLocalChecked(), DateTakenGetter, DateTakenSetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("cameraModel").ToLocalChecked(), CameraModelGetter, CameraModelSetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("cameraManufacturer").ToLocalChecked(), CameraManufacturerGetter, CameraManufacturerSetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("latitude").ToLocalChecked(), LatitudeGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("longitude").ToLocalChecked(), LongitudeGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("orientation").ToLocalChecked(), OrientationGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("peopleNames").ToLocalChecked(), PeopleNamesGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("height").ToLocalChecked(), HeightGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("keywords").ToLocalChecked(), KeywordsGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("width").ToLocalChecked(), WidthGetter);
      
      Local<Object> constructor = Nan::To<Object>(Nan::GetFunction(localRef).ToLocalChecked()).ToLocalChecked();
	  Nan::SetMethod(constructor, "castFrom", CastFrom);


      Nan::Set(exports, Nan::New<String>("ImageProperties").ToLocalChecked(), constructor);
    }


    virtual ::Platform::Object^ GetObjectInstance() const override
    {
      return _instance;
    }

  private:
    
    ImageProperties(::Windows::Storage::FileProperties::ImageProperties^ instance)
    {
      _instance = instance;
    }
    
    
    static void New(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

	    Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(s_constructorTemplate);

      // in case the constructor was called without the new operator
      if (!localRef->HasInstance(info.This()))
      {
        if (info.Length() > 0)
        {
          std::unique_ptr<Local<Value> []> constructorArgs(new Local<Value>[info.Length()]);

          Local<Value> *argsPtr = constructorArgs.get();
          for (int i = 0; i < info.Length(); i++)
          {
            argsPtr[i] = info[i];
          }

		  MaybeLocal<Object> res = Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(), info.Length(), constructorArgs.get());
		  if (res.IsEmpty())
		  {
			  return;
		  }
		  info.GetReturnValue().Set(res.ToLocalChecked());
		  return;
		}
		else
		{
          MaybeLocal<Object> res = Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(), info.Length(), nullptr);
          if (res.IsEmpty())
          {
            return;
          }
          info.GetReturnValue().Set(res.ToLocalChecked());
          return;
        }
      }
      
      ::Windows::Storage::FileProperties::ImageProperties^ winRtInstance;


      if (info.Length() == 1 && OpaqueWrapper::IsOpaqueWrapper(info[0]) &&
        NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::ImageProperties^>(info[0]))
      {
        try 
        {
          winRtInstance = (::Windows::Storage::FileProperties::ImageProperties^) NodeRT::Utils::GetObjectInstance(info[0]);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Invalid arguments, no suitable constructor found")));
	    	return;
      }

      NodeRT::Utils::SetHiddenValue(info.This(), Nan::New<String>("__winRtInstance__").ToLocalChecked(), True());

      ImageProperties *wrapperInstance = new ImageProperties(winRtInstance);
      wrapperInstance->Wrap(info.This());

      info.GetReturnValue().Set(info.This());
    }


	
    static void CastFrom(Nan::NAN_METHOD_ARGS_TYPE info)
    {
		HandleScope scope;
		if (info.Length() < 1 || !NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::ImageProperties^>(info[0]))
		{
			Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Invalid arguments, no object provided, or given object could not be casted to requested type")));
			return;
		}

		::Windows::Storage::FileProperties::ImageProperties^ winRtInstance;
		try
		{
			winRtInstance = (::Windows::Storage::FileProperties::ImageProperties^) NodeRT::Utils::GetObjectInstance(info[0]);
		}
		catch (Platform::Exception ^exception)
		{
			NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
			return;
		}

		info.GetReturnValue().Set(WrapImageProperties(winRtInstance));
    }


    static void RetrievePropertiesAsync(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::ImageProperties^>(info.This()))
      {
        return;
      }

      if (info.Length() == 0 || !info[info.Length() -1]->IsFunction())
      {
          Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return;
      }

      ImageProperties *wrapper = ImageProperties::Unwrap<ImageProperties>(info.This());

      ::Windows::Foundation::IAsyncOperation<::Windows::Foundation::Collections::IMap<::Platform::String^, ::Platform::Object^>^>^ op;
    

      if (info.Length() == 2
        && (NodeRT::Utils::IsWinRtWrapperOf<::Windows::Foundation::Collections::IIterable<::Platform::String^>^>(info[0]) || info[0]->IsArray()))
      {
        try
        {
          ::Windows::Foundation::Collections::IIterable<::Platform::String^>^ arg0 = 
            [] (v8::Local<v8::Value> value) -> ::Windows::Foundation::Collections::IIterable<::Platform::String^>^
            {
              if (value->IsArray())
              {
                return NodeRT::Collections::JsArrayToWinrtVector<::Platform::String^>(value.As<Array>(), 
                 [](Local<Value> value) -> bool {
                   return (!NodeRT::Utils::IsWinRtWrapper(value));
                 },
                 [](Local<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));
              }
            } (info[0]);
          
          op = wrapper->_instance->RetrievePropertiesAsync(arg0);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(info[info.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<::Windows::Foundation::Collections::IMap<::Platform::String^, ::Platform::Object^>^> t) 
      {	
        try
        {
          auto result = t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [result](NodeUtils::InvokeCallbackDelegate invokeCallback) {


            TryCatch tryCatch;
            Local<Value> error; 
            Local<Value> arg1 = NodeRT::Collections::MapWrapper<::Platform::String^,::Platform::Object^>::CreateMapWrapper(result, 
            [](::Platform::String^ val) -> Local<Value> {
              return NodeRT::Utils::NewString(val->Data());
            },
            [](Local<Value> value) -> bool {
              return value->IsString();
            },
            [](Local<Value> value) -> ::Platform::String^ {
              return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
            },
            [](::Platform::Object^ val) -> Local<Value> {
              return CreateOpaqueWrapper(val);
            },
            [](Local<Value> value) -> bool {
              return NodeRT::Utils::IsWinRtWrapperOf<::Platform::Object^>(value);
            },
            [](Local<Value> value) -> ::Platform::Object^ {
              return dynamic_cast<::Platform::Object^>(NodeRT::Utils::GetObjectInstance(value));
            }
          );
            if (tryCatch.HasCaught())
            {
              error = Nan::To<Object>(tryCatch.Exception()).ToLocalChecked();
            }
            else 
            {
              error = Undefined();
            }
            if (arg1.IsEmpty()) arg1 = Undefined();
            Local<Value> args[] = {error, arg1};
			// TODO: this is ugly! Needed due to the possibility of expception occuring inside object convertors
			// can be fixed by wrapping the conversion code in a function and calling it on the fly
			// we must clear the try catch block here so the invoked inner method exception won't get swollen (issue #52) 
			tryCatch.~TryCatch();

		    
            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
             
            Local<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Local<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });
    }
    static void SavePropertiesAsync(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::ImageProperties^>(info.This()))
      {
        return;
      }

      if (info.Length() == 0 || !info[info.Length() -1]->IsFunction())
      {
          Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return;
      }

      ImageProperties *wrapper = ImageProperties::Unwrap<ImageProperties>(info.This());

      ::Windows::Foundation::IAsyncAction^ op;
    

      if (info.Length() == 2
        && (NodeRT::Utils::IsWinRtWrapperOf<::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^>(info[0]) || info[0]->IsObject()))
      {
        try
        {
          ::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^ arg0 = 
            [] (v8::Local<v8::Value> value) -> ::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^
            {
              if (value->IsObject())
              {
                return NodeRT::Collections::JsObjectToWinrtMap<::Platform::Object^>(value.As<Object>(), 
                 [](Local<Value> value) -> bool {
                   return NodeRT::Utils::IsWinRtWrapperOf<::Platform::Object^>(value);
                 },
                 [](Local<Value> value) -> ::Platform::Object^ {
                   return dynamic_cast<::Platform::Object^>(NodeRT::Utils::GetObjectInstance(value));
                 }
                );
              }
              else
              {
                return dynamic_cast<::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^>(NodeRT::Utils::GetObjectInstance(value));
              }
            } (info[0]);
          
          op = wrapper->_instance->SavePropertiesAsync(arg0);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else if (info.Length() == 1)
      {
        try
        {
          op = wrapper->_instance->SavePropertiesAsync();
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(info[info.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<void> t) 
      {	
        try
        {
          t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [](NodeUtils::InvokeCallbackDelegate invokeCallback) {


            Local<Value> args[] = {Undefined()};

		    
            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
             
            Local<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Local<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });
    }
  



    static void TitleGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::ImageProperties^>(info.This()))
      {
        return;
      }

      ImageProperties *wrapper = ImageProperties::Unwrap<ImageProperties>(info.This());

      try 
      {
        Platform::String^ result = wrapper->_instance->Title;
        info.GetReturnValue().Set(NodeRT::Utils::NewString(result->Data()));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void TitleSetter(Local<String> property, Local<Value> value, const Nan::PropertyCallbackInfo<void> &info)
    {
      HandleScope scope;
      
      if (!value->IsString())
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Value to set is of unexpected type")));
        return;
      }

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::ImageProperties^>(info.This()))
      {
        return;
      }

      ImageProperties *wrapper = ImageProperties::Unwrap<ImageProperties>(info.This());

      try 
      {
        
        Platform::String^ winRtValue = ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));

        wrapper->_instance->Title = winRtValue;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
      }
    }
    
    static void RatingGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::ImageProperties^>(info.This()))
      {
        return;
      }

      ImageProperties *wrapper = ImageProperties::Unwrap<ImageProperties>(info.This());

      try 
      {
        unsigned int result = wrapper->_instance->Rating;
        info.GetReturnValue().Set(Nan::New<Integer>(result));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void RatingSetter(Local<String> property, Local<Value> value, const Nan::PropertyCallbackInfo<void> &info)
    {
      HandleScope scope;
      
      if (!value->IsUint32())
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Value to set is of unexpected type")));
        return;
      }

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::ImageProperties^>(info.This()))
      {
        return;
      }

      ImageProperties *wrapper = ImageProperties::Unwrap<ImageProperties>(info.This());

      try 
      {
        
        unsigned int winRtValue = static_cast<unsigned int>(Nan::To<uint32_t>(value).FromMaybe(0));

        wrapper->_instance->Rating = winRtValue;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
      }
    }
    
    static void DateTakenGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::ImageProperties^>(info.This()))
      {
        return;
      }

      ImageProperties *wrapper = ImageProperties::Unwrap<ImageProperties>(info.This());

      try 
      {
        ::Windows::Foundation::DateTime result = wrapper->_instance->DateTaken;
        info.GetReturnValue().Set(NodeRT::Utils::DateTimeToJS(result));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void DateTakenSetter(Local<String> property, Local<Value> value, const Nan::PropertyCallbackInfo<void> &info)
    {
      HandleScope scope;
      
      if (!value->IsDate())
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Value to set is of unexpected type")));
        return;
      }

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::ImageProperties^>(info.This()))
      {
        return;
      }

      ImageProperties *wrapper = ImageProperties::Unwrap<ImageProperties>(info.This());

      try 
      {
        
        ::Windows::Foundation::DateTime winRtValue = NodeRT::Utils::DateTimeFromJSDate(value);

        wrapper->_instance->DateTaken = winRtValue;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
      }
    }
    
    static void CameraModelGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::ImageProperties^>(info.This()))
      {
        return;
      }

      ImageProperties *wrapper = ImageProperties::Unwrap<ImageProperties>(info.This());

      try 
      {
        Platform::String^ result = wrapper->_instance->CameraModel;
        info.GetReturnValue().Set(NodeRT::Utils::NewString(result->Data()));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void CameraModelSetter(Local<String> property, Local<Value> value, const Nan::PropertyCallbackInfo<void> &info)
    {
      HandleScope scope;
      
      if (!value->IsString())
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Value to set is of unexpected type")));
        return;
      }

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::ImageProperties^>(info.This()))
      {
        return;
      }

      ImageProperties *wrapper = ImageProperties::Unwrap<ImageProperties>(info.This());

      try 
      {
        
        Platform::String^ winRtValue = ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));

        wrapper->_instance->CameraModel = winRtValue;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
      }
    }
    
    static void CameraManufacturerGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::ImageProperties^>(info.This()))
      {
        return;
      }

      ImageProperties *wrapper = ImageProperties::Unwrap<ImageProperties>(info.This());

      try 
      {
        Platform::String^ result = wrapper->_instance->CameraManufacturer;
        info.GetReturnValue().Set(NodeRT::Utils::NewString(result->Data()));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void CameraManufacturerSetter(Local<String> property, Local<Value> value, const Nan::PropertyCallbackInfo<void> &info)
    {
      HandleScope scope;
      
      if (!value->IsString())
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Value to set is of unexpected type")));
        return;
      }

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::ImageProperties^>(info.This()))
      {
        return;
      }

      ImageProperties *wrapper = ImageProperties::Unwrap<ImageProperties>(info.This());

      try 
      {
        
        Platform::String^ winRtValue = ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));

        wrapper->_instance->CameraManufacturer = winRtValue;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
      }
    }
    
    static void LatitudeGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::ImageProperties^>(info.This()))
      {
        return;
      }

      ImageProperties *wrapper = ImageProperties::Unwrap<ImageProperties>(info.This());

      try 
      {
        ::Platform::IBox<double>^ result = wrapper->_instance->Latitude;
        info.GetReturnValue().Set(result ? static_cast<Local<Value>>(Nan::New<Number>(static_cast<double>(result->Value))) : Undefined());
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void LongitudeGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::ImageProperties^>(info.This()))
      {
        return;
      }

      ImageProperties *wrapper = ImageProperties::Unwrap<ImageProperties>(info.This());

      try 
      {
        ::Platform::IBox<double>^ result = wrapper->_instance->Longitude;
        info.GetReturnValue().Set(result ? static_cast<Local<Value>>(Nan::New<Number>(static_cast<double>(result->Value))) : Undefined());
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void OrientationGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::ImageProperties^>(info.This()))
      {
        return;
      }

      ImageProperties *wrapper = ImageProperties::Unwrap<ImageProperties>(info.This());

      try 
      {
        ::Windows::Storage::FileProperties::PhotoOrientation result = wrapper->_instance->Orientation;
        info.GetReturnValue().Set(Nan::New<Integer>(static_cast<int>(result)));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void PeopleNamesGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::ImageProperties^>(info.This()))
      {
        return;
      }

      ImageProperties *wrapper = ImageProperties::Unwrap<ImageProperties>(info.This());

      try 
      {
        ::Windows::Foundation::Collections::IVectorView<::Platform::String^>^ result = wrapper->_instance->PeopleNames;
        info.GetReturnValue().Set(NodeRT::Collections::VectorViewWrapper<::Platform::String^>::CreateVectorViewWrapper(result, 
            [](::Platform::String^ val) -> Local<Value> {
              return NodeRT::Utils::NewString(val->Data());
            },
            [](Local<Value> value) -> bool {
              return value->IsString();
            },
            [](Local<Value> value) -> ::Platform::String^ {
              return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
            }
          ));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void HeightGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::ImageProperties^>(info.This()))
      {
        return;
      }

      ImageProperties *wrapper = ImageProperties::Unwrap<ImageProperties>(info.This());

      try 
      {
        unsigned int result = wrapper->_instance->Height;
        info.GetReturnValue().Set(Nan::New<Integer>(result));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void KeywordsGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::ImageProperties^>(info.This()))
      {
        return;
      }

      ImageProperties *wrapper = ImageProperties::Unwrap<ImageProperties>(info.This());

      try 
      {
        ::Windows::Foundation::Collections::IVector<::Platform::String^>^ result = wrapper->_instance->Keywords;
        info.GetReturnValue().Set(NodeRT::Collections::VectorWrapper<::Platform::String^>::CreateVectorWrapper(result, 
            [](::Platform::String^ val) -> Local<Value> {
              return NodeRT::Utils::NewString(val->Data());
            },
            [](Local<Value> value) -> bool {
              return value->IsString();
            },
            [](Local<Value> value) -> ::Platform::String^ {
              return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
            }
          ));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void WidthGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::ImageProperties^>(info.This()))
      {
        return;
      }

      ImageProperties *wrapper = ImageProperties::Unwrap<ImageProperties>(info.This());

      try 
      {
        unsigned int result = wrapper->_instance->Width;
        info.GetReturnValue().Set(Nan::New<Integer>(result));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    


  private:
    ::Windows::Storage::FileProperties::ImageProperties^ _instance;
    static Persistent<FunctionTemplate> s_constructorTemplate;

    friend v8::Local<v8::Value> WrapImageProperties(::Windows::Storage::FileProperties::ImageProperties^ wintRtInstance);
    friend ::Windows::Storage::FileProperties::ImageProperties^ UnwrapImageProperties(Local<Value> value);
  };
  Persistent<FunctionTemplate> ImageProperties::s_constructorTemplate;

  v8::Local<v8::Value> WrapImageProperties(::Windows::Storage::FileProperties::ImageProperties^ winRtInstance)
  {
    EscapableHandleScope scope;

    if (winRtInstance == nullptr)
    {
      return scope.Escape(Undefined());
    }

    Local<Value> opaqueWrapper = CreateOpaqueWrapper(winRtInstance);
    Local<Value> args[] = {opaqueWrapper};
    Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(ImageProperties::s_constructorTemplate);
    return scope.Escape(Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(),_countof(args), args).ToLocalChecked());
  }

  ::Windows::Storage::FileProperties::ImageProperties^ UnwrapImageProperties(Local<Value> value)
  {
     return ImageProperties::Unwrap<ImageProperties>(Nan::To<Object>(value).ToLocalChecked())->_instance;
  }

  void InitImageProperties(Local<Object> exports)
  {
    ImageProperties::Init(exports);
  }

  class DocumentProperties : public WrapperBase
  {
  public:    
    static void Init(const Local<Object> exports)
    {
      HandleScope scope;
      
      Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(New);
      s_constructorTemplate.Reset(localRef);
      localRef->SetClassName(Nan::New<String>("DocumentProperties").ToLocalChecked());
      localRef->InstanceTemplate()->SetInternalFieldCount(1);
      
      Local<Function> func;
      Local<FunctionTemplate> funcTemplate;
                  
      Nan::SetPrototypeMethod(localRef, "retrievePropertiesAsync", RetrievePropertiesAsync);
      Nan::SetPrototypeMethod(localRef, "savePropertiesAsync", SavePropertiesAsync);
      
                  
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("title").ToLocalChecked(), TitleGetter, TitleSetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("comment").ToLocalChecked(), CommentGetter, CommentSetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("author").ToLocalChecked(), AuthorGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("keywords").ToLocalChecked(), KeywordsGetter);
      
      Local<Object> constructor = Nan::To<Object>(Nan::GetFunction(localRef).ToLocalChecked()).ToLocalChecked();
	  Nan::SetMethod(constructor, "castFrom", CastFrom);


      Nan::Set(exports, Nan::New<String>("DocumentProperties").ToLocalChecked(), constructor);
    }


    virtual ::Platform::Object^ GetObjectInstance() const override
    {
      return _instance;
    }

  private:
    
    DocumentProperties(::Windows::Storage::FileProperties::DocumentProperties^ instance)
    {
      _instance = instance;
    }
    
    
    static void New(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

	    Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(s_constructorTemplate);

      // in case the constructor was called without the new operator
      if (!localRef->HasInstance(info.This()))
      {
        if (info.Length() > 0)
        {
          std::unique_ptr<Local<Value> []> constructorArgs(new Local<Value>[info.Length()]);

          Local<Value> *argsPtr = constructorArgs.get();
          for (int i = 0; i < info.Length(); i++)
          {
            argsPtr[i] = info[i];
          }

		  MaybeLocal<Object> res = Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(), info.Length(), constructorArgs.get());
		  if (res.IsEmpty())
		  {
			  return;
		  }
		  info.GetReturnValue().Set(res.ToLocalChecked());
		  return;
		}
		else
		{
          MaybeLocal<Object> res = Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(), info.Length(), nullptr);
          if (res.IsEmpty())
          {
            return;
          }
          info.GetReturnValue().Set(res.ToLocalChecked());
          return;
        }
      }
      
      ::Windows::Storage::FileProperties::DocumentProperties^ winRtInstance;


      if (info.Length() == 1 && OpaqueWrapper::IsOpaqueWrapper(info[0]) &&
        NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::DocumentProperties^>(info[0]))
      {
        try 
        {
          winRtInstance = (::Windows::Storage::FileProperties::DocumentProperties^) NodeRT::Utils::GetObjectInstance(info[0]);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Invalid arguments, no suitable constructor found")));
	    	return;
      }

      NodeRT::Utils::SetHiddenValue(info.This(), Nan::New<String>("__winRtInstance__").ToLocalChecked(), True());

      DocumentProperties *wrapperInstance = new DocumentProperties(winRtInstance);
      wrapperInstance->Wrap(info.This());

      info.GetReturnValue().Set(info.This());
    }


	
    static void CastFrom(Nan::NAN_METHOD_ARGS_TYPE info)
    {
		HandleScope scope;
		if (info.Length() < 1 || !NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::DocumentProperties^>(info[0]))
		{
			Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Invalid arguments, no object provided, or given object could not be casted to requested type")));
			return;
		}

		::Windows::Storage::FileProperties::DocumentProperties^ winRtInstance;
		try
		{
			winRtInstance = (::Windows::Storage::FileProperties::DocumentProperties^) NodeRT::Utils::GetObjectInstance(info[0]);
		}
		catch (Platform::Exception ^exception)
		{
			NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
			return;
		}

		info.GetReturnValue().Set(WrapDocumentProperties(winRtInstance));
    }


    static void RetrievePropertiesAsync(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::DocumentProperties^>(info.This()))
      {
        return;
      }

      if (info.Length() == 0 || !info[info.Length() -1]->IsFunction())
      {
          Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return;
      }

      DocumentProperties *wrapper = DocumentProperties::Unwrap<DocumentProperties>(info.This());

      ::Windows::Foundation::IAsyncOperation<::Windows::Foundation::Collections::IMap<::Platform::String^, ::Platform::Object^>^>^ op;
    

      if (info.Length() == 2
        && (NodeRT::Utils::IsWinRtWrapperOf<::Windows::Foundation::Collections::IIterable<::Platform::String^>^>(info[0]) || info[0]->IsArray()))
      {
        try
        {
          ::Windows::Foundation::Collections::IIterable<::Platform::String^>^ arg0 = 
            [] (v8::Local<v8::Value> value) -> ::Windows::Foundation::Collections::IIterable<::Platform::String^>^
            {
              if (value->IsArray())
              {
                return NodeRT::Collections::JsArrayToWinrtVector<::Platform::String^>(value.As<Array>(), 
                 [](Local<Value> value) -> bool {
                   return (!NodeRT::Utils::IsWinRtWrapper(value));
                 },
                 [](Local<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));
              }
            } (info[0]);
          
          op = wrapper->_instance->RetrievePropertiesAsync(arg0);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(info[info.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<::Windows::Foundation::Collections::IMap<::Platform::String^, ::Platform::Object^>^> t) 
      {	
        try
        {
          auto result = t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [result](NodeUtils::InvokeCallbackDelegate invokeCallback) {


            TryCatch tryCatch;
            Local<Value> error; 
            Local<Value> arg1 = NodeRT::Collections::MapWrapper<::Platform::String^,::Platform::Object^>::CreateMapWrapper(result, 
            [](::Platform::String^ val) -> Local<Value> {
              return NodeRT::Utils::NewString(val->Data());
            },
            [](Local<Value> value) -> bool {
              return value->IsString();
            },
            [](Local<Value> value) -> ::Platform::String^ {
              return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
            },
            [](::Platform::Object^ val) -> Local<Value> {
              return CreateOpaqueWrapper(val);
            },
            [](Local<Value> value) -> bool {
              return NodeRT::Utils::IsWinRtWrapperOf<::Platform::Object^>(value);
            },
            [](Local<Value> value) -> ::Platform::Object^ {
              return dynamic_cast<::Platform::Object^>(NodeRT::Utils::GetObjectInstance(value));
            }
          );
            if (tryCatch.HasCaught())
            {
              error = Nan::To<Object>(tryCatch.Exception()).ToLocalChecked();
            }
            else 
            {
              error = Undefined();
            }
            if (arg1.IsEmpty()) arg1 = Undefined();
            Local<Value> args[] = {error, arg1};
			// TODO: this is ugly! Needed due to the possibility of expception occuring inside object convertors
			// can be fixed by wrapping the conversion code in a function and calling it on the fly
			// we must clear the try catch block here so the invoked inner method exception won't get swollen (issue #52) 
			tryCatch.~TryCatch();

		    
            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
             
            Local<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Local<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });
    }
    static void SavePropertiesAsync(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::DocumentProperties^>(info.This()))
      {
        return;
      }

      if (info.Length() == 0 || !info[info.Length() -1]->IsFunction())
      {
          Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return;
      }

      DocumentProperties *wrapper = DocumentProperties::Unwrap<DocumentProperties>(info.This());

      ::Windows::Foundation::IAsyncAction^ op;
    

      if (info.Length() == 2
        && (NodeRT::Utils::IsWinRtWrapperOf<::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^>(info[0]) || info[0]->IsObject()))
      {
        try
        {
          ::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^ arg0 = 
            [] (v8::Local<v8::Value> value) -> ::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^
            {
              if (value->IsObject())
              {
                return NodeRT::Collections::JsObjectToWinrtMap<::Platform::Object^>(value.As<Object>(), 
                 [](Local<Value> value) -> bool {
                   return NodeRT::Utils::IsWinRtWrapperOf<::Platform::Object^>(value);
                 },
                 [](Local<Value> value) -> ::Platform::Object^ {
                   return dynamic_cast<::Platform::Object^>(NodeRT::Utils::GetObjectInstance(value));
                 }
                );
              }
              else
              {
                return dynamic_cast<::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^>(NodeRT::Utils::GetObjectInstance(value));
              }
            } (info[0]);
          
          op = wrapper->_instance->SavePropertiesAsync(arg0);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else if (info.Length() == 1)
      {
        try
        {
          op = wrapper->_instance->SavePropertiesAsync();
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(info[info.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<void> t) 
      {	
        try
        {
          t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [](NodeUtils::InvokeCallbackDelegate invokeCallback) {


            Local<Value> args[] = {Undefined()};

		    
            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
             
            Local<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Local<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });
    }
  



    static void TitleGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::DocumentProperties^>(info.This()))
      {
        return;
      }

      DocumentProperties *wrapper = DocumentProperties::Unwrap<DocumentProperties>(info.This());

      try 
      {
        Platform::String^ result = wrapper->_instance->Title;
        info.GetReturnValue().Set(NodeRT::Utils::NewString(result->Data()));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void TitleSetter(Local<String> property, Local<Value> value, const Nan::PropertyCallbackInfo<void> &info)
    {
      HandleScope scope;
      
      if (!value->IsString())
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Value to set is of unexpected type")));
        return;
      }

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::DocumentProperties^>(info.This()))
      {
        return;
      }

      DocumentProperties *wrapper = DocumentProperties::Unwrap<DocumentProperties>(info.This());

      try 
      {
        
        Platform::String^ winRtValue = ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));

        wrapper->_instance->Title = winRtValue;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
      }
    }
    
    static void CommentGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::DocumentProperties^>(info.This()))
      {
        return;
      }

      DocumentProperties *wrapper = DocumentProperties::Unwrap<DocumentProperties>(info.This());

      try 
      {
        Platform::String^ result = wrapper->_instance->Comment;
        info.GetReturnValue().Set(NodeRT::Utils::NewString(result->Data()));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void CommentSetter(Local<String> property, Local<Value> value, const Nan::PropertyCallbackInfo<void> &info)
    {
      HandleScope scope;
      
      if (!value->IsString())
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Value to set is of unexpected type")));
        return;
      }

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::DocumentProperties^>(info.This()))
      {
        return;
      }

      DocumentProperties *wrapper = DocumentProperties::Unwrap<DocumentProperties>(info.This());

      try 
      {
        
        Platform::String^ winRtValue = ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));

        wrapper->_instance->Comment = winRtValue;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
      }
    }
    
    static void AuthorGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::DocumentProperties^>(info.This()))
      {
        return;
      }

      DocumentProperties *wrapper = DocumentProperties::Unwrap<DocumentProperties>(info.This());

      try 
      {
        ::Windows::Foundation::Collections::IVector<::Platform::String^>^ result = wrapper->_instance->Author;
        info.GetReturnValue().Set(NodeRT::Collections::VectorWrapper<::Platform::String^>::CreateVectorWrapper(result, 
            [](::Platform::String^ val) -> Local<Value> {
              return NodeRT::Utils::NewString(val->Data());
            },
            [](Local<Value> value) -> bool {
              return value->IsString();
            },
            [](Local<Value> value) -> ::Platform::String^ {
              return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
            }
          ));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void KeywordsGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::DocumentProperties^>(info.This()))
      {
        return;
      }

      DocumentProperties *wrapper = DocumentProperties::Unwrap<DocumentProperties>(info.This());

      try 
      {
        ::Windows::Foundation::Collections::IVector<::Platform::String^>^ result = wrapper->_instance->Keywords;
        info.GetReturnValue().Set(NodeRT::Collections::VectorWrapper<::Platform::String^>::CreateVectorWrapper(result, 
            [](::Platform::String^ val) -> Local<Value> {
              return NodeRT::Utils::NewString(val->Data());
            },
            [](Local<Value> value) -> bool {
              return value->IsString();
            },
            [](Local<Value> value) -> ::Platform::String^ {
              return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
            }
          ));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    


  private:
    ::Windows::Storage::FileProperties::DocumentProperties^ _instance;
    static Persistent<FunctionTemplate> s_constructorTemplate;

    friend v8::Local<v8::Value> WrapDocumentProperties(::Windows::Storage::FileProperties::DocumentProperties^ wintRtInstance);
    friend ::Windows::Storage::FileProperties::DocumentProperties^ UnwrapDocumentProperties(Local<Value> value);
  };
  Persistent<FunctionTemplate> DocumentProperties::s_constructorTemplate;

  v8::Local<v8::Value> WrapDocumentProperties(::Windows::Storage::FileProperties::DocumentProperties^ winRtInstance)
  {
    EscapableHandleScope scope;

    if (winRtInstance == nullptr)
    {
      return scope.Escape(Undefined());
    }

    Local<Value> opaqueWrapper = CreateOpaqueWrapper(winRtInstance);
    Local<Value> args[] = {opaqueWrapper};
    Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(DocumentProperties::s_constructorTemplate);
    return scope.Escape(Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(),_countof(args), args).ToLocalChecked());
  }

  ::Windows::Storage::FileProperties::DocumentProperties^ UnwrapDocumentProperties(Local<Value> value)
  {
     return DocumentProperties::Unwrap<DocumentProperties>(Nan::To<Object>(value).ToLocalChecked())->_instance;
  }

  void InitDocumentProperties(Local<Object> exports)
  {
    DocumentProperties::Init(exports);
  }

  class StorageItemContentProperties : public WrapperBase
  {
  public:    
    static void Init(const Local<Object> exports)
    {
      HandleScope scope;
      
      Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(New);
      s_constructorTemplate.Reset(localRef);
      localRef->SetClassName(Nan::New<String>("StorageItemContentProperties").ToLocalChecked());
      localRef->InstanceTemplate()->SetInternalFieldCount(1);
      
      Local<Function> func;
      Local<FunctionTemplate> funcTemplate;
                  
      Nan::SetPrototypeMethod(localRef, "getMusicPropertiesAsync", GetMusicPropertiesAsync);
      Nan::SetPrototypeMethod(localRef, "getVideoPropertiesAsync", GetVideoPropertiesAsync);
      Nan::SetPrototypeMethod(localRef, "getImagePropertiesAsync", GetImagePropertiesAsync);
      Nan::SetPrototypeMethod(localRef, "getDocumentPropertiesAsync", GetDocumentPropertiesAsync);
      Nan::SetPrototypeMethod(localRef, "retrievePropertiesAsync", RetrievePropertiesAsync);
      Nan::SetPrototypeMethod(localRef, "savePropertiesAsync", SavePropertiesAsync);
      
                  
      Local<Object> constructor = Nan::To<Object>(Nan::GetFunction(localRef).ToLocalChecked()).ToLocalChecked();
	  Nan::SetMethod(constructor, "castFrom", CastFrom);


      Nan::Set(exports, Nan::New<String>("StorageItemContentProperties").ToLocalChecked(), constructor);
    }


    virtual ::Platform::Object^ GetObjectInstance() const override
    {
      return _instance;
    }

  private:
    
    StorageItemContentProperties(::Windows::Storage::FileProperties::StorageItemContentProperties^ instance)
    {
      _instance = instance;
    }
    
    
    static void New(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

	    Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(s_constructorTemplate);

      // in case the constructor was called without the new operator
      if (!localRef->HasInstance(info.This()))
      {
        if (info.Length() > 0)
        {
          std::unique_ptr<Local<Value> []> constructorArgs(new Local<Value>[info.Length()]);

          Local<Value> *argsPtr = constructorArgs.get();
          for (int i = 0; i < info.Length(); i++)
          {
            argsPtr[i] = info[i];
          }

		  MaybeLocal<Object> res = Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(), info.Length(), constructorArgs.get());
		  if (res.IsEmpty())
		  {
			  return;
		  }
		  info.GetReturnValue().Set(res.ToLocalChecked());
		  return;
		}
		else
		{
          MaybeLocal<Object> res = Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(), info.Length(), nullptr);
          if (res.IsEmpty())
          {
            return;
          }
          info.GetReturnValue().Set(res.ToLocalChecked());
          return;
        }
      }
      
      ::Windows::Storage::FileProperties::StorageItemContentProperties^ winRtInstance;


      if (info.Length() == 1 && OpaqueWrapper::IsOpaqueWrapper(info[0]) &&
        NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemContentProperties^>(info[0]))
      {
        try 
        {
          winRtInstance = (::Windows::Storage::FileProperties::StorageItemContentProperties^) NodeRT::Utils::GetObjectInstance(info[0]);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Invalid arguments, no suitable constructor found")));
	    	return;
      }

      NodeRT::Utils::SetHiddenValue(info.This(), Nan::New<String>("__winRtInstance__").ToLocalChecked(), True());

      StorageItemContentProperties *wrapperInstance = new StorageItemContentProperties(winRtInstance);
      wrapperInstance->Wrap(info.This());

      info.GetReturnValue().Set(info.This());
    }


	
    static void CastFrom(Nan::NAN_METHOD_ARGS_TYPE info)
    {
		HandleScope scope;
		if (info.Length() < 1 || !NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemContentProperties^>(info[0]))
		{
			Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Invalid arguments, no object provided, or given object could not be casted to requested type")));
			return;
		}

		::Windows::Storage::FileProperties::StorageItemContentProperties^ winRtInstance;
		try
		{
			winRtInstance = (::Windows::Storage::FileProperties::StorageItemContentProperties^) NodeRT::Utils::GetObjectInstance(info[0]);
		}
		catch (Platform::Exception ^exception)
		{
			NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
			return;
		}

		info.GetReturnValue().Set(WrapStorageItemContentProperties(winRtInstance));
    }


    static void GetMusicPropertiesAsync(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemContentProperties^>(info.This()))
      {
        return;
      }

      if (info.Length() == 0 || !info[info.Length() -1]->IsFunction())
      {
          Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return;
      }

      StorageItemContentProperties *wrapper = StorageItemContentProperties::Unwrap<StorageItemContentProperties>(info.This());

      ::Windows::Foundation::IAsyncOperation<::Windows::Storage::FileProperties::MusicProperties^>^ op;
    

      if (info.Length() == 1)
      {
        try
        {
          op = wrapper->_instance->GetMusicPropertiesAsync();
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(info[info.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<::Windows::Storage::FileProperties::MusicProperties^> t) 
      {	
        try
        {
          auto result = t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [result](NodeUtils::InvokeCallbackDelegate invokeCallback) {


            TryCatch tryCatch;
            Local<Value> error; 
            Local<Value> arg1 = WrapMusicProperties(result);
            if (tryCatch.HasCaught())
            {
              error = Nan::To<Object>(tryCatch.Exception()).ToLocalChecked();
            }
            else 
            {
              error = Undefined();
            }
            if (arg1.IsEmpty()) arg1 = Undefined();
            Local<Value> args[] = {error, arg1};
			// TODO: this is ugly! Needed due to the possibility of expception occuring inside object convertors
			// can be fixed by wrapping the conversion code in a function and calling it on the fly
			// we must clear the try catch block here so the invoked inner method exception won't get swollen (issue #52) 
			tryCatch.~TryCatch();

		    
            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
             
            Local<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Local<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });
    }
    static void GetVideoPropertiesAsync(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemContentProperties^>(info.This()))
      {
        return;
      }

      if (info.Length() == 0 || !info[info.Length() -1]->IsFunction())
      {
          Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return;
      }

      StorageItemContentProperties *wrapper = StorageItemContentProperties::Unwrap<StorageItemContentProperties>(info.This());

      ::Windows::Foundation::IAsyncOperation<::Windows::Storage::FileProperties::VideoProperties^>^ op;
    

      if (info.Length() == 1)
      {
        try
        {
          op = wrapper->_instance->GetVideoPropertiesAsync();
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(info[info.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<::Windows::Storage::FileProperties::VideoProperties^> t) 
      {	
        try
        {
          auto result = t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [result](NodeUtils::InvokeCallbackDelegate invokeCallback) {


            TryCatch tryCatch;
            Local<Value> error; 
            Local<Value> arg1 = WrapVideoProperties(result);
            if (tryCatch.HasCaught())
            {
              error = Nan::To<Object>(tryCatch.Exception()).ToLocalChecked();
            }
            else 
            {
              error = Undefined();
            }
            if (arg1.IsEmpty()) arg1 = Undefined();
            Local<Value> args[] = {error, arg1};
			// TODO: this is ugly! Needed due to the possibility of expception occuring inside object convertors
			// can be fixed by wrapping the conversion code in a function and calling it on the fly
			// we must clear the try catch block here so the invoked inner method exception won't get swollen (issue #52) 
			tryCatch.~TryCatch();

		    
            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
             
            Local<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Local<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });
    }
    static void GetImagePropertiesAsync(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemContentProperties^>(info.This()))
      {
        return;
      }

      if (info.Length() == 0 || !info[info.Length() -1]->IsFunction())
      {
          Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return;
      }

      StorageItemContentProperties *wrapper = StorageItemContentProperties::Unwrap<StorageItemContentProperties>(info.This());

      ::Windows::Foundation::IAsyncOperation<::Windows::Storage::FileProperties::ImageProperties^>^ op;
    

      if (info.Length() == 1)
      {
        try
        {
          op = wrapper->_instance->GetImagePropertiesAsync();
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(info[info.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<::Windows::Storage::FileProperties::ImageProperties^> t) 
      {	
        try
        {
          auto result = t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [result](NodeUtils::InvokeCallbackDelegate invokeCallback) {


            TryCatch tryCatch;
            Local<Value> error; 
            Local<Value> arg1 = WrapImageProperties(result);
            if (tryCatch.HasCaught())
            {
              error = Nan::To<Object>(tryCatch.Exception()).ToLocalChecked();
            }
            else 
            {
              error = Undefined();
            }
            if (arg1.IsEmpty()) arg1 = Undefined();
            Local<Value> args[] = {error, arg1};
			// TODO: this is ugly! Needed due to the possibility of expception occuring inside object convertors
			// can be fixed by wrapping the conversion code in a function and calling it on the fly
			// we must clear the try catch block here so the invoked inner method exception won't get swollen (issue #52) 
			tryCatch.~TryCatch();

		    
            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
             
            Local<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Local<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });
    }
    static void GetDocumentPropertiesAsync(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemContentProperties^>(info.This()))
      {
        return;
      }

      if (info.Length() == 0 || !info[info.Length() -1]->IsFunction())
      {
          Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return;
      }

      StorageItemContentProperties *wrapper = StorageItemContentProperties::Unwrap<StorageItemContentProperties>(info.This());

      ::Windows::Foundation::IAsyncOperation<::Windows::Storage::FileProperties::DocumentProperties^>^ op;
    

      if (info.Length() == 1)
      {
        try
        {
          op = wrapper->_instance->GetDocumentPropertiesAsync();
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(info[info.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<::Windows::Storage::FileProperties::DocumentProperties^> t) 
      {	
        try
        {
          auto result = t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [result](NodeUtils::InvokeCallbackDelegate invokeCallback) {


            TryCatch tryCatch;
            Local<Value> error; 
            Local<Value> arg1 = WrapDocumentProperties(result);
            if (tryCatch.HasCaught())
            {
              error = Nan::To<Object>(tryCatch.Exception()).ToLocalChecked();
            }
            else 
            {
              error = Undefined();
            }
            if (arg1.IsEmpty()) arg1 = Undefined();
            Local<Value> args[] = {error, arg1};
			// TODO: this is ugly! Needed due to the possibility of expception occuring inside object convertors
			// can be fixed by wrapping the conversion code in a function and calling it on the fly
			// we must clear the try catch block here so the invoked inner method exception won't get swollen (issue #52) 
			tryCatch.~TryCatch();

		    
            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
             
            Local<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Local<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });
    }
    static void RetrievePropertiesAsync(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemContentProperties^>(info.This()))
      {
        return;
      }

      if (info.Length() == 0 || !info[info.Length() -1]->IsFunction())
      {
          Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return;
      }

      StorageItemContentProperties *wrapper = StorageItemContentProperties::Unwrap<StorageItemContentProperties>(info.This());

      ::Windows::Foundation::IAsyncOperation<::Windows::Foundation::Collections::IMap<::Platform::String^, ::Platform::Object^>^>^ op;
    

      if (info.Length() == 2
        && (NodeRT::Utils::IsWinRtWrapperOf<::Windows::Foundation::Collections::IIterable<::Platform::String^>^>(info[0]) || info[0]->IsArray()))
      {
        try
        {
          ::Windows::Foundation::Collections::IIterable<::Platform::String^>^ arg0 = 
            [] (v8::Local<v8::Value> value) -> ::Windows::Foundation::Collections::IIterable<::Platform::String^>^
            {
              if (value->IsArray())
              {
                return NodeRT::Collections::JsArrayToWinrtVector<::Platform::String^>(value.As<Array>(), 
                 [](Local<Value> value) -> bool {
                   return (!NodeRT::Utils::IsWinRtWrapper(value));
                 },
                 [](Local<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));
              }
            } (info[0]);
          
          op = wrapper->_instance->RetrievePropertiesAsync(arg0);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(info[info.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<::Windows::Foundation::Collections::IMap<::Platform::String^, ::Platform::Object^>^> t) 
      {	
        try
        {
          auto result = t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [result](NodeUtils::InvokeCallbackDelegate invokeCallback) {


            TryCatch tryCatch;
            Local<Value> error; 
            Local<Value> arg1 = NodeRT::Collections::MapWrapper<::Platform::String^,::Platform::Object^>::CreateMapWrapper(result, 
            [](::Platform::String^ val) -> Local<Value> {
              return NodeRT::Utils::NewString(val->Data());
            },
            [](Local<Value> value) -> bool {
              return value->IsString();
            },
            [](Local<Value> value) -> ::Platform::String^ {
              return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
            },
            [](::Platform::Object^ val) -> Local<Value> {
              return CreateOpaqueWrapper(val);
            },
            [](Local<Value> value) -> bool {
              return NodeRT::Utils::IsWinRtWrapperOf<::Platform::Object^>(value);
            },
            [](Local<Value> value) -> ::Platform::Object^ {
              return dynamic_cast<::Platform::Object^>(NodeRT::Utils::GetObjectInstance(value));
            }
          );
            if (tryCatch.HasCaught())
            {
              error = Nan::To<Object>(tryCatch.Exception()).ToLocalChecked();
            }
            else 
            {
              error = Undefined();
            }
            if (arg1.IsEmpty()) arg1 = Undefined();
            Local<Value> args[] = {error, arg1};
			// TODO: this is ugly! Needed due to the possibility of expception occuring inside object convertors
			// can be fixed by wrapping the conversion code in a function and calling it on the fly
			// we must clear the try catch block here so the invoked inner method exception won't get swollen (issue #52) 
			tryCatch.~TryCatch();

		    
            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
             
            Local<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Local<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });
    }
    static void SavePropertiesAsync(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::StorageItemContentProperties^>(info.This()))
      {
        return;
      }

      if (info.Length() == 0 || !info[info.Length() -1]->IsFunction())
      {
          Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return;
      }

      StorageItemContentProperties *wrapper = StorageItemContentProperties::Unwrap<StorageItemContentProperties>(info.This());

      ::Windows::Foundation::IAsyncAction^ op;
    

      if (info.Length() == 2
        && (NodeRT::Utils::IsWinRtWrapperOf<::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^>(info[0]) || info[0]->IsObject()))
      {
        try
        {
          ::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^ arg0 = 
            [] (v8::Local<v8::Value> value) -> ::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^
            {
              if (value->IsObject())
              {
                return NodeRT::Collections::JsObjectToWinrtMap<::Platform::Object^>(value.As<Object>(), 
                 [](Local<Value> value) -> bool {
                   return NodeRT::Utils::IsWinRtWrapperOf<::Platform::Object^>(value);
                 },
                 [](Local<Value> value) -> ::Platform::Object^ {
                   return dynamic_cast<::Platform::Object^>(NodeRT::Utils::GetObjectInstance(value));
                 }
                );
              }
              else
              {
                return dynamic_cast<::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^>(NodeRT::Utils::GetObjectInstance(value));
              }
            } (info[0]);
          
          op = wrapper->_instance->SavePropertiesAsync(arg0);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else if (info.Length() == 1)
      {
        try
        {
          op = wrapper->_instance->SavePropertiesAsync();
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(info[info.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<void> t) 
      {	
        try
        {
          t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [](NodeUtils::InvokeCallbackDelegate invokeCallback) {


            Local<Value> args[] = {Undefined()};

		    
            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
             
            Local<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Local<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });
    }
  





  private:
    ::Windows::Storage::FileProperties::StorageItemContentProperties^ _instance;
    static Persistent<FunctionTemplate> s_constructorTemplate;

    friend v8::Local<v8::Value> WrapStorageItemContentProperties(::Windows::Storage::FileProperties::StorageItemContentProperties^ wintRtInstance);
    friend ::Windows::Storage::FileProperties::StorageItemContentProperties^ UnwrapStorageItemContentProperties(Local<Value> value);
  };
  Persistent<FunctionTemplate> StorageItemContentProperties::s_constructorTemplate;

  v8::Local<v8::Value> WrapStorageItemContentProperties(::Windows::Storage::FileProperties::StorageItemContentProperties^ winRtInstance)
  {
    EscapableHandleScope scope;

    if (winRtInstance == nullptr)
    {
      return scope.Escape(Undefined());
    }

    Local<Value> opaqueWrapper = CreateOpaqueWrapper(winRtInstance);
    Local<Value> args[] = {opaqueWrapper};
    Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(StorageItemContentProperties::s_constructorTemplate);
    return scope.Escape(Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(),_countof(args), args).ToLocalChecked());
  }

  ::Windows::Storage::FileProperties::StorageItemContentProperties^ UnwrapStorageItemContentProperties(Local<Value> value)
  {
     return StorageItemContentProperties::Unwrap<StorageItemContentProperties>(Nan::To<Object>(value).ToLocalChecked())->_instance;
  }

  void InitStorageItemContentProperties(Local<Object> exports)
  {
    StorageItemContentProperties::Init(exports);
  }

  class BasicProperties : public WrapperBase
  {
  public:    
    static void Init(const Local<Object> exports)
    {
      HandleScope scope;
      
      Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(New);
      s_constructorTemplate.Reset(localRef);
      localRef->SetClassName(Nan::New<String>("BasicProperties").ToLocalChecked());
      localRef->InstanceTemplate()->SetInternalFieldCount(1);
      
      Local<Function> func;
      Local<FunctionTemplate> funcTemplate;
                  
      Nan::SetPrototypeMethod(localRef, "retrievePropertiesAsync", RetrievePropertiesAsync);
      Nan::SetPrototypeMethod(localRef, "savePropertiesAsync", SavePropertiesAsync);
      
                  
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("dateModified").ToLocalChecked(), DateModifiedGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("itemDate").ToLocalChecked(), ItemDateGetter);
      Nan::SetAccessor(localRef->PrototypeTemplate(), Nan::New<String>("size").ToLocalChecked(), SizeGetter);
      
      Local<Object> constructor = Nan::To<Object>(Nan::GetFunction(localRef).ToLocalChecked()).ToLocalChecked();
	  Nan::SetMethod(constructor, "castFrom", CastFrom);


      Nan::Set(exports, Nan::New<String>("BasicProperties").ToLocalChecked(), constructor);
    }


    virtual ::Platform::Object^ GetObjectInstance() const override
    {
      return _instance;
    }

  private:
    
    BasicProperties(::Windows::Storage::FileProperties::BasicProperties^ instance)
    {
      _instance = instance;
    }
    
    
    static void New(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

	    Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(s_constructorTemplate);

      // in case the constructor was called without the new operator
      if (!localRef->HasInstance(info.This()))
      {
        if (info.Length() > 0)
        {
          std::unique_ptr<Local<Value> []> constructorArgs(new Local<Value>[info.Length()]);

          Local<Value> *argsPtr = constructorArgs.get();
          for (int i = 0; i < info.Length(); i++)
          {
            argsPtr[i] = info[i];
          }

		  MaybeLocal<Object> res = Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(), info.Length(), constructorArgs.get());
		  if (res.IsEmpty())
		  {
			  return;
		  }
		  info.GetReturnValue().Set(res.ToLocalChecked());
		  return;
		}
		else
		{
          MaybeLocal<Object> res = Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(), info.Length(), nullptr);
          if (res.IsEmpty())
          {
            return;
          }
          info.GetReturnValue().Set(res.ToLocalChecked());
          return;
        }
      }
      
      ::Windows::Storage::FileProperties::BasicProperties^ winRtInstance;


      if (info.Length() == 1 && OpaqueWrapper::IsOpaqueWrapper(info[0]) &&
        NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::BasicProperties^>(info[0]))
      {
        try 
        {
          winRtInstance = (::Windows::Storage::FileProperties::BasicProperties^) NodeRT::Utils::GetObjectInstance(info[0]);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Invalid arguments, no suitable constructor found")));
	    	return;
      }

      NodeRT::Utils::SetHiddenValue(info.This(), Nan::New<String>("__winRtInstance__").ToLocalChecked(), True());

      BasicProperties *wrapperInstance = new BasicProperties(winRtInstance);
      wrapperInstance->Wrap(info.This());

      info.GetReturnValue().Set(info.This());
    }


	
    static void CastFrom(Nan::NAN_METHOD_ARGS_TYPE info)
    {
		HandleScope scope;
		if (info.Length() < 1 || !NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::BasicProperties^>(info[0]))
		{
			Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Invalid arguments, no object provided, or given object could not be casted to requested type")));
			return;
		}

		::Windows::Storage::FileProperties::BasicProperties^ winRtInstance;
		try
		{
			winRtInstance = (::Windows::Storage::FileProperties::BasicProperties^) NodeRT::Utils::GetObjectInstance(info[0]);
		}
		catch (Platform::Exception ^exception)
		{
			NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
			return;
		}

		info.GetReturnValue().Set(WrapBasicProperties(winRtInstance));
    }


    static void RetrievePropertiesAsync(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::BasicProperties^>(info.This()))
      {
        return;
      }

      if (info.Length() == 0 || !info[info.Length() -1]->IsFunction())
      {
          Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return;
      }

      BasicProperties *wrapper = BasicProperties::Unwrap<BasicProperties>(info.This());

      ::Windows::Foundation::IAsyncOperation<::Windows::Foundation::Collections::IMap<::Platform::String^, ::Platform::Object^>^>^ op;
    

      if (info.Length() == 2
        && (NodeRT::Utils::IsWinRtWrapperOf<::Windows::Foundation::Collections::IIterable<::Platform::String^>^>(info[0]) || info[0]->IsArray()))
      {
        try
        {
          ::Windows::Foundation::Collections::IIterable<::Platform::String^>^ arg0 = 
            [] (v8::Local<v8::Value> value) -> ::Windows::Foundation::Collections::IIterable<::Platform::String^>^
            {
              if (value->IsArray())
              {
                return NodeRT::Collections::JsArrayToWinrtVector<::Platform::String^>(value.As<Array>(), 
                 [](Local<Value> value) -> bool {
                   return (!NodeRT::Utils::IsWinRtWrapper(value));
                 },
                 [](Local<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));
              }
            } (info[0]);
          
          op = wrapper->_instance->RetrievePropertiesAsync(arg0);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(info[info.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<::Windows::Foundation::Collections::IMap<::Platform::String^, ::Platform::Object^>^> t) 
      {	
        try
        {
          auto result = t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [result](NodeUtils::InvokeCallbackDelegate invokeCallback) {


            TryCatch tryCatch;
            Local<Value> error; 
            Local<Value> arg1 = NodeRT::Collections::MapWrapper<::Platform::String^,::Platform::Object^>::CreateMapWrapper(result, 
            [](::Platform::String^ val) -> Local<Value> {
              return NodeRT::Utils::NewString(val->Data());
            },
            [](Local<Value> value) -> bool {
              return value->IsString();
            },
            [](Local<Value> value) -> ::Platform::String^ {
              return ref new Platform::String(NodeRT::Utils::StringToWchar(v8::String::Value(value)));
            },
            [](::Platform::Object^ val) -> Local<Value> {
              return CreateOpaqueWrapper(val);
            },
            [](Local<Value> value) -> bool {
              return NodeRT::Utils::IsWinRtWrapperOf<::Platform::Object^>(value);
            },
            [](Local<Value> value) -> ::Platform::Object^ {
              return dynamic_cast<::Platform::Object^>(NodeRT::Utils::GetObjectInstance(value));
            }
          );
            if (tryCatch.HasCaught())
            {
              error = Nan::To<Object>(tryCatch.Exception()).ToLocalChecked();
            }
            else 
            {
              error = Undefined();
            }
            if (arg1.IsEmpty()) arg1 = Undefined();
            Local<Value> args[] = {error, arg1};
			// TODO: this is ugly! Needed due to the possibility of expception occuring inside object convertors
			// can be fixed by wrapping the conversion code in a function and calling it on the fly
			// we must clear the try catch block here so the invoked inner method exception won't get swollen (issue #52) 
			tryCatch.~TryCatch();

		    
            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
             
            Local<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Local<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });
    }
    static void SavePropertiesAsync(Nan::NAN_METHOD_ARGS_TYPE info)
    {
      HandleScope scope;

      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::BasicProperties^>(info.This()))
      {
        return;
      }

      if (info.Length() == 0 || !info[info.Length() -1]->IsFunction())
      {
          Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: No callback was given")));
          return;
      }

      BasicProperties *wrapper = BasicProperties::Unwrap<BasicProperties>(info.This());

      ::Windows::Foundation::IAsyncAction^ op;
    

      if (info.Length() == 2
        && (NodeRT::Utils::IsWinRtWrapperOf<::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^>(info[0]) || info[0]->IsObject()))
      {
        try
        {
          ::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^ arg0 = 
            [] (v8::Local<v8::Value> value) -> ::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^
            {
              if (value->IsObject())
              {
                return NodeRT::Collections::JsObjectToWinrtMap<::Platform::Object^>(value.As<Object>(), 
                 [](Local<Value> value) -> bool {
                   return NodeRT::Utils::IsWinRtWrapperOf<::Platform::Object^>(value);
                 },
                 [](Local<Value> value) -> ::Platform::Object^ {
                   return dynamic_cast<::Platform::Object^>(NodeRT::Utils::GetObjectInstance(value));
                 }
                );
              }
              else
              {
                return dynamic_cast<::Windows::Foundation::Collections::IIterable<::Windows::Foundation::Collections::IKeyValuePair<::Platform::String^, ::Platform::Object^>^>^>(NodeRT::Utils::GetObjectInstance(value));
              }
            } (info[0]);
          
          op = wrapper->_instance->SavePropertiesAsync(arg0);
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else if (info.Length() == 1)
      {
        try
        {
          op = wrapper->_instance->SavePropertiesAsync();
        }
        catch (Platform::Exception ^exception)
        {
          NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
          return;
        }
      }
      else 
      {
        Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"Bad arguments: no suitable overload found")));
        return;
      }
    
      auto opTask = create_task(op);
      uv_async_t* asyncToken = NodeUtils::Async::GetAsyncToken(info[info.Length() -1].As<Function>());

      opTask.then( [asyncToken] (task<void> t) 
      {	
        try
        {
          t.get();
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [](NodeUtils::InvokeCallbackDelegate invokeCallback) {


            Local<Value> args[] = {Undefined()};

		    
            invokeCallback(_countof(args), args);
          });
        }
        catch (Platform::Exception^ exception)
        {
          NodeUtils::Async::RunCallbackOnMain(asyncToken, [exception](NodeUtils::InvokeCallbackDelegate invokeCallback) {
             
            Local<Value> error = NodeRT::Utils::WinRtExceptionToJsError(exception);
        
            Local<Value> args[] = {error};
            invokeCallback(_countof(args), args);
          });
        }  		
      });
    }
  



    static void DateModifiedGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::BasicProperties^>(info.This()))
      {
        return;
      }

      BasicProperties *wrapper = BasicProperties::Unwrap<BasicProperties>(info.This());

      try 
      {
        ::Windows::Foundation::DateTime result = wrapper->_instance->DateModified;
        info.GetReturnValue().Set(NodeRT::Utils::DateTimeToJS(result));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void ItemDateGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::BasicProperties^>(info.This()))
      {
        return;
      }

      BasicProperties *wrapper = BasicProperties::Unwrap<BasicProperties>(info.This());

      try 
      {
        ::Windows::Foundation::DateTime result = wrapper->_instance->ItemDate;
        info.GetReturnValue().Set(NodeRT::Utils::DateTimeToJS(result));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    
    static void SizeGetter(Local<String> property, const Nan::PropertyCallbackInfo<v8::Value> &info)
    {
      HandleScope scope;
      
      if (!NodeRT::Utils::IsWinRtWrapperOf<::Windows::Storage::FileProperties::BasicProperties^>(info.This()))
      {
        return;
      }

      BasicProperties *wrapper = BasicProperties::Unwrap<BasicProperties>(info.This());

      try 
      {
        unsigned __int64 result = wrapper->_instance->Size;
        info.GetReturnValue().Set(Nan::New<Number>(static_cast<double>(result)));
        return;
      }
      catch (Platform::Exception ^exception)
      {
        NodeRT::Utils::ThrowWinRtExceptionInJs(exception);
        return;
      }
    }
    


  private:
    ::Windows::Storage::FileProperties::BasicProperties^ _instance;
    static Persistent<FunctionTemplate> s_constructorTemplate;

    friend v8::Local<v8::Value> WrapBasicProperties(::Windows::Storage::FileProperties::BasicProperties^ wintRtInstance);
    friend ::Windows::Storage::FileProperties::BasicProperties^ UnwrapBasicProperties(Local<Value> value);
  };
  Persistent<FunctionTemplate> BasicProperties::s_constructorTemplate;

  v8::Local<v8::Value> WrapBasicProperties(::Windows::Storage::FileProperties::BasicProperties^ winRtInstance)
  {
    EscapableHandleScope scope;

    if (winRtInstance == nullptr)
    {
      return scope.Escape(Undefined());
    }

    Local<Value> opaqueWrapper = CreateOpaqueWrapper(winRtInstance);
    Local<Value> args[] = {opaqueWrapper};
    Local<FunctionTemplate> localRef = Nan::New<FunctionTemplate>(BasicProperties::s_constructorTemplate);
    return scope.Escape(Nan::NewInstance(Nan::GetFunction(localRef).ToLocalChecked(),_countof(args), args).ToLocalChecked());
  }

  ::Windows::Storage::FileProperties::BasicProperties^ UnwrapBasicProperties(Local<Value> value)
  {
     return BasicProperties::Unwrap<BasicProperties>(Nan::To<Object>(value).ToLocalChecked())->_instance;
  }

  void InitBasicProperties(Local<Object> exports)
  {
    BasicProperties::Init(exports);
  }

} } } } 

NAN_MODULE_INIT(init)
{
  // we ignore failures for now since it probably means that the initialization already happened for STA, and that's cool
  CoInitializeEx(nullptr, COINIT_MULTITHREADED);
  //if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)))
  /*{
    Nan::ThrowError(Nan::Error(NodeRT::Utils::NewString(L"error in CoInitializeEx()")));
    return;
  }*/
  
  NodeRT::Windows::Storage::FileProperties::InitPropertyPrefetchOptionsEnum(target);
  NodeRT::Windows::Storage::FileProperties::InitThumbnailTypeEnum(target);
  NodeRT::Windows::Storage::FileProperties::InitThumbnailModeEnum(target);
  NodeRT::Windows::Storage::FileProperties::InitThumbnailOptionsEnum(target);
  NodeRT::Windows::Storage::FileProperties::InitPhotoOrientationEnum(target);
  NodeRT::Windows::Storage::FileProperties::InitVideoOrientationEnum(target);
  NodeRT::Windows::Storage::FileProperties::InitGeotagHelper(target);
  NodeRT::Windows::Storage::FileProperties::InitStorageItemThumbnail(target);
  NodeRT::Windows::Storage::FileProperties::InitIStorageItemExtraProperties(target);
  NodeRT::Windows::Storage::FileProperties::InitMusicProperties(target);
  NodeRT::Windows::Storage::FileProperties::InitVideoProperties(target);
  NodeRT::Windows::Storage::FileProperties::InitImageProperties(target);
  NodeRT::Windows::Storage::FileProperties::InitDocumentProperties(target);
  NodeRT::Windows::Storage::FileProperties::InitStorageItemContentProperties(target);
  NodeRT::Windows::Storage::FileProperties::InitBasicProperties(target);

  NodeRT::Utils::RegisterNameSpace("Windows.Storage.FileProperties", target);
}


NODE_MODULE(binding, init)