{{ var className = "Nan" + it.name; }}

{{#def.idlType}}
{{#def.buildValidEnumValuesList}}
{{#def.impl}}
{{#def.enum}}
{{#def.extractV8Value}}
{{#def.callback}}
{{#def.overload}}
{{#def.array}}
{{#def.members}}

{{
var callback = extractAllCallbackMethod(it);
var hasCallback = callback.methods.length > 0;
var allMethodMembers = getAllMethodMembers(it);
}}

{{~ allMethodMembers :p:i }}

{{
  var unwrapStr = 'auto myself = ObjectWrap::Unwrap<' + className + '>(info.Holder());';
  var functionCallPrefix = 'myself->impl_->';
  var isStaticMethod = (p.static === true);
  if ( isStaticMethod ) {
    functionCallPrefix = it.name + '::';
    unwrapStr = '';
  }

  var generateMethod = isFirstMethodInOverloadList(it, p, i);
  var overloadList = it.operationMap[p.name];

  var methodName = p.name;
  var methodOverloadList = [];
  overloadList.forEach(o => {
    methodOverloadList.push(allMethodMembers[o.index]);
  });
}}

{{? generateMethod}}

NAN_METHOD({{=className}}::{{=methodName}}) {

  {{=unwrapStr}}

  info.GetReturnValue().Set(Nan::Undefined());

{{~ methodOverloadList :method:index}}
{{
  var numArgs = method.arguments.length;
  var elseIf = '';
  if (index > 0) {
    elseIf = 'else ';
  }
}}

  {{=elseIf}}if ({{=generateArgsIfConditionStr(method, it.refTypeMap, callback)}}) {
    // Method: name = {{=method.name}}, taking {{=numArgs}} argument(s)

{{ var args = []; }}

{{~ method.arguments :q:j}}

{{
args.push('arg' + j);
}}

{{? isCallback(q.idlType, callback) }}
{{var callbackHelperClassName = getCallbackHelperClassName(q.idlType.idlType);}}
    auto function{{=j}} = {{=extractArgument(q.idlType, it.refTypeMap, callback)}}(info[{{=j}}]);
    {{=callbackHelperClassName}}* arg{{=j}} = new {{=callbackHelperClassName}}();
    arg{{=j}}->Reset(function{{=j}}, info.This());
{{?? isInterface(q.idlType, it.refTypeMap) }}
    // Convert to impl {{=q.idlType.idlType}} pointer
    auto nanArg{{=j}} = {{=extractArgument(q.idlType, it.refTypeMap)}}(info[{{=j}}]);
    auto arg{{=j}} = *(ObjectWrap::Unwrap<Nan{{=q.idlType.idlType}}>(nanArg{{=j}})->{{=generateGetImplMethodName(q.idlType.idlType)}}());
{{?? true}}

{{
var defaultValueStr = '';
if (q.default && q.default.value) {
  if (q.default.type === 'string' || q.default.type === 'DOMString') {
    defaultValueStr = ', "' + q.default.value + '"';
  } else {
    defaultValueStr = ', ' + q.default.value;
  }
}
}}
    auto arg{{=j}} = {{=extractArgument(q.idlType, it.refTypeMap)}}(info[{{=j}}]{{=defaultValueStr}});

{{?}}


{{
  var enumCheck = isEnumType(q.idlType, it.refTypeMap);
  var enumDef = getEnumDef(q.idlType, it.refTypeMap);
  var enumName = enumDef.name;
}}

{{? enumCheck }}
    // Valid enum values for parameter info{{=j}}, original IDL type: {{=enumName}}
    const char* VALID_ENUM_{{=(enumName.toUpperCase())}}_INFO{{=j}}[] = { {{=buildValidEnumValuesList(enumDef)}} };
    ThrowIfEnumValueNotValid(arg{{=j}}, VALID_ENUM_{{=(enumName.toUpperCase())}}_INFO{{=j}}, {{=q.name}}, {{=enumName}});
{{?}}

{{~}}


{{
if (method.idlType.generic === 'Promise') {
  /*args.push('info.GetIsolate()');*/
}
var argStr = args.join(', ');
}}

{{? method.idlType.generic === 'Promise' }}
    // Return promise
    {{=functionCallPrefix}}SetJavaScriptThis(info.This());
    auto promise = {{=functionCallPrefix}}{{=methodName}}({{=argStr}});
    info.GetReturnValue().Set(promise);
{{?? method.idlType.idlType === 'void' }}
    // Return type is void
    {{=functionCallPrefix}}{{=methodName}}({{=argStr}});
    info.GetReturnValue().Set(Nan::Undefined());
{{?? method.idlType.idlType === 'String' || method.idlType.idlType === 'DOMString' }}
    // Return type is string
    auto value = {{=functionCallPrefix}}{{=methodName}}({{=argStr}});
    info.GetReturnValue().Set(Nan::New(value).ToLocalChecked());
{{?? method.idlType.idlType === 'ArrayBuffer' }}
    // Return type is ArrayBuffer
    ArrayBuffer arrayBuffer = {{=functionCallPrefix}}{{=methodName}}({{=argStr}});
    v8::Local<v8::Object> value;
    if (Nan::NewBuffer(arrayBuffer.data, arrayBuffer.size).ToLocal(&value))
      info.GetReturnValue().Set(value);
{{?? isInterface(method.idlType, it.refTypeMap)}}
    // Return type is an interface object
    auto implObj = {{=functionCallPrefix}}{{=methodName}}({{=argStr}});
{{ var retType = method.idlType.idlType; }}
    if (!implObj) {
      info.GetReturnValue().Set(Nan::Undefined());
    } else {
      auto value = Nan{{=retType}}::NewInstance();
      ObjectWrap::Unwrap<Nan{{=retType}}>(value)->Set{{=retType}}Impl(implObj);
      info.GetReturnValue().Set(value);
    }
{{?? isDictionary(method.idlType, it.refTypeMap)}}
    // Return type is a dictionary object
    auto value = {{=functionCallPrefix}}{{=methodName}}({{=argStr}});
    info.GetReturnValue().Set(static_cast<v8::Local<v8::Object>>(value));
{{?? isArray(method.idlType) }}
    auto value = {{=functionCallPrefix}}{{=methodName}}({{=argStr}});
    info.GetReturnValue().Set(static_cast<v8::Local<v8::Array>>(value));
{{?? isTypedArray(method.idlType) }}
    auto value = {{=functionCallPrefix}}{{=methodName}}({{=argStr}});
    info.GetReturnValue().Set(value.GetTypedArray());
{{?? true }}
    // Primitive types (int, double ...)
    auto value = {{=functionCallPrefix}}{{=methodName}}({{=argStr}});
    info.GetReturnValue().Set(Nan::New(value));
{{?}}

  } // Closing bracket of 'if (info.Length() == {{=numArgs}})'

{{~}}

  else {
    // TODO: handle no-match by modifying template nanCxxImplMethod.def
  }
}

{{?}}

{{~}}
