1/*
2* Copyright (c) 2022 Shenzhen Kaihong Digital Industry Development Co., Ltd.
3* Licensed under the Apache License, Version 2.0 (the "License");
4* you may not use this file except in compliance with the License.
5* You may obtain a copy of the License at
6*
7* http://www.apache.org/licenses/LICENSE-2.0
8*
9* Unless required by applicable law or agreed to in writing, software
10* distributed under the License is distributed on an "AS IS" BASIS,
11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12* See the License for the specific language governing permissions and
13* limitations under the License.
14*/
15const { generateFunctionDirect } = require('./function_direct');
16const { generateFunctionSync } = require('./function_sync');
17const { generateFunctionAsync } = require('./function_async');
18const { generateFunctionOnOff } = require('./function_onoff');
19const { generateThreadsafeFunc } = require('./function_threadsafe');
20const { FuncType, InterfaceList, getArrayType, getArrayTypeTwo, getMapType, EnumList, jsType2CType,
21    isOnOffRegisterFunc, isCreateThreadsafeFunc } = require('../tools/common');
22const { jsToC, getCType, paramGenerate } = require('./param_generate');
23const { cToJs } = require('./return_generate');
24const re = require('../tools/re');
25const { NapiLog } = require('../tools/NapiLog');
26const { addUniqFunc2List, addUniqObj2List, setOverrideFunc } = require('../tools/tool');
27
28let middleHTmplete = `
29class [className]_middle {
30public:
31    struct constructor_value_struct {[contructorValueIn]
32    };
33    static napi_value constructor(napi_env env, napi_callback_info info);
34    static void release(DataPtr p);
35    [static_funcs]
36};
37`;
38
39let middleBodyTmplete = `
40    napi_value [className]_middle::constructor(napi_env env, napi_callback_info info)
41    {
42        XNapiTool *pxt = new XNapiTool(env, info);
43        napi_status status;
44        size_t argc;
45        status = napi_get_cb_info(env, info, &argc, nullptr, nullptr, nullptr);
46        if (argc > 0) {
47            napi_value args[argc];
48            status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
49            if (status != napi_ok) {
50                return nullptr;
51            }
52            struct constructor_value_struct *vio = new constructor_value_struct();
53            [getConstructorParam]
54            [className] *p = new [className]([constructorParam]);
55            napi_value thisvar = pxt->WrapInstance(reinterpret_cast<DataPtr>(p), [className]_middle::release);
56            delete vio;
57            return thisvar;
58        } else {
59            [className] *p = new [className]();
60            napi_value thisvar = pxt->WrapInstance(reinterpret_cast<DataPtr>(p), [className]_middle::release);
61            return thisvar;
62        }
63    }
64    void [className]_middle::release(DataPtr p)
65    {
66        void *dataPtr = p;
67        [className] *p2 = static_cast<[className] *>(dataPtr);
68        delete p2;
69    }
70    [static_funcs]
71`;
72
73function getHDefineOfVariable(name, type, variable, optional) {
74    if (type.indexOf('|') >= 0) {
75        unionTypeString(name, type, variable, optional);
76    } else if (type === 'string') {
77        variableTypeString(optional, variable, name);
78    } else if (InterfaceList.getValue(type)) {
79        variableTypeInterface(optional, variable, type, name);
80    } else if (EnumList.getValue(type)) {
81      // 如果是枚举string类型,需要将其转换为std::string类型
82      variableTypeEnum(type, variable, name);
83    } else if (type.indexOf('Array<') === 0) {
84        typeArrFunctionOne(type, variable, name, optional);
85    } else if (type === 'boolean') {
86        variableTypeBoolean(optional, variable, name);
87    } else if (type.substring(type.length - 2) === '[]') {
88        typeArrFunctionTwo(type, variable, name, optional);
89    } else if (type.substring(0, 4) === 'Map<' || type.indexOf('{[key:') === 0) {
90        variable.hDefine += mapTypeString(type, name, optional); // 支持可选参数?
91    } else if (type === 'any') {
92        variable.hDefine += anyTypeString(type, name);
93    } else if (type.substring(0, 12) === 'NUMBER_TYPE_') {
94        variableTypeNumber(optional, variable, type, name);
95    } else if (type === 'Object' || type === 'object') {
96        variable.hDefine += '\n    std::map<std::string, std::any> %s;'.format(name);
97    }
98    else {
99        NapiLog.logError(`
100        ---- generateVariable fail %s,%s ----
101        `.format(name, type));
102    }
103}
104
105function variableTypeNumber(optional, variable, type, name) {
106  if (optional) {
107    variable.hDefine += '\n    std::optional<%s> %s;'.format(type, name);
108  } else {
109    variable.hDefine += '\n    %s %s;'.format(type, name);
110  }
111}
112
113function variableTypeBoolean(optional, variable, name) {
114  if (optional) {
115    variable.hDefine += '\n    std::optional<bool> %s;'.format(name);
116  } else {
117    variable.hDefine += '\n    bool %s;'.format(name);
118  }
119}
120
121function variableTypeEnum(type, variable, name) {
122  let enumBasicType = EnumList.getValue(type)[0].type;
123  if (enumBasicType === 'string') {
124    variable.hDefine += '\n    %s %s;'.format('std::string', name);
125  } else {
126    variable.hDefine += '\n    %s %s;'.format(type, name);
127  }
128}
129
130function variableTypeInterface(optional, variable, type, name) {
131  if (optional) {
132    variable.hDefine += '\n    std::optional<%s> %s;'.format(type, name);
133  } else {
134    variable.hDefine += '\n    %s %s;'.format(type, name);
135  }
136}
137
138function variableTypeString(optional, variable, name) {
139  if (optional) {
140    variable.hDefine += '\n    std::optional<std::string> %s;'.format(name);
141  } else {
142    variable.hDefine += '\n    std::string %s;'.format(name);
143  }
144}
145
146function typeArrFunctionTwo(type, variable, name, optional) {
147    let arrayType = getArrayTypeTwo(type);
148    if (arrayType === 'any') {
149        variable.hDefine += '\n    std::string %s_type;\n    std::any %s;'.format(name, name);
150    } else {
151        let cType = jsType2CType(arrayType);
152        if (optional) {
153            variable.hDefine += '\n    std::optional<std::vector<%s>> %s;'.format(cType, name);
154        } else {
155            variable.hDefine += '\n    std::vector<%s> %s;'.format(cType, name);
156        }
157    }
158}
159
160function typeArrFunctionOne(type, variable, name, optional) {
161    let arrayType = getArrayType(type);
162    if (arrayType === 'any') {
163        variable.hDefine += '\n    std::string %s_type; \n    std::any %s;'.format(name, name);
164    } else {
165        let cType = jsType2CType(arrayType);
166        if (optional) {
167            variable.hDefine += '\n    std::optional<std::vector<%s>> %s;'.format(cType, name);
168        } else {
169            variable.hDefine += '\n    std::vector<%s> %s;'.format(cType, name);
170        }
171    }
172}
173
174function generateVariable(value, variable, className) {
175    let name = value.name;
176    let type = value.type;
177    let optional = value.optional;
178    if (!value.isParentMember) {
179        // 只有类/接口自己的成员属性需要在.h中定义, 父类/父接口不需要
180        getHDefineOfVariable(name, type, variable, optional);
181    }
182    if (optional && type.indexOf('|') < 0) {
183        optionalParamGetSet(type, variable, name, className);
184    } else {
185          variable.middleH += `
186          static napi_value getvalue_%s(napi_env env, napi_callback_info info);
187          static napi_value setvalue_%s(napi_env env, napi_callback_info info);\n`.format(name, name);
188        variable.middleValue += `
189          napi_value %s::getvalue_%s(napi_env env, napi_callback_info info)
190        {
191            XNapiTool *pxt = std::make_unique<XNapiTool>(env, info).release();
192            void *instPtr = pxt->UnWarpInstance();
193            %s *p = static_cast<%s *>(instPtr);
194            napi_value result = nullptr;
195              `.format(className + '_middle', name, className, className) +
196              cToJs('p->' + name, type, 'result', 1, optional) + `
197            delete pxt;
198            return result;
199        }
200          napi_value %s::setvalue_%s(napi_env env, napi_callback_info info)
201        {
202            std::shared_ptr<XNapiTool> pxt = std::make_shared<XNapiTool>(env, info);
203            void *instPtr = pxt->UnWarpInstance();
204            %s *p = static_cast<%s *>(instPtr);
205              `.format(className + '_middle', name, className, className) +
206              jsToC('p->' + name, 'pxt->GetArgv(XNapiTool::ZERO)',
207            type, 0, optional) + `
208            return nullptr;
209        }`;
210    }
211}
212
213function optionalParamGetSet(type, variable, name, className) {
214    let optType = getCType(type);
215    variable.middleH += `
216        static napi_value getvalue_%s(napi_env env, napi_callback_info info);
217        static napi_value setvalue_%s(napi_env env, napi_callback_info info);\n`.format(name, name);
218    variable.middleValue += `
219        napi_value %s::getvalue_%s(napi_env env, napi_callback_info info)
220        {
221            XNapiTool *pxt = std::make_unique<XNapiTool>(env, info).release();
222            void *instPtr = pxt->UnWarpInstance();
223            %s *p = static_cast<%s *>(instPtr);
224            napi_value result = nullptr;
225            if(p->%s.has_value()) {
226                `.format(className + '_middle', name, className, className, name) +
227                cToJs('p->%s.value()'.format(name), type, 'result') + `
228            }
229            delete pxt;
230            return result;
231        }
232        napi_value %s::setvalue_%s(napi_env env, napi_callback_info info)
233        {
234            std::shared_ptr<XNapiTool> pxt = std::make_shared<XNapiTool>(env, info);
235            void *instPtr = pxt->UnWarpInstance();
236            %s *p = static_cast<%s *>(instPtr);
237            if (pxt->GetProperty(pxt->GetArgv(XNapiTool::ZERO), "%s")) {
238                %s %s_tmp;
239                `.format(className + '_middle', name, className, className, name, optType, name) +
240                jsToC('%s_tmp'.format(name), 'pxt->GetArgv(XNapiTool::ZERO)', type) + `
241                p->%s.emplace(%s_tmp);`.format(name, name) + `
242            }
243            return nullptr;
244        }`;
245}
246
247function unionTypeString(name, type, variable, optional) {
248    if (optional) {
249        variable.hDefine += `\n    std::optional<std::string> %s_type;
250        std::optional<std::any> %s;`.format(name, name);
251    } else {
252        variable.hDefine += `\n    std::string %s_type;
253        std::any %s;`.format(name, name);
254    }
255}
256
257function mapTypeString(type, name, optional) {
258    let mapType = getMapType(type);
259    let mapTypeString;
260    if (mapType[1] !== undefined && mapType[1] !== null && mapType[2] === undefined) {
261        if (mapType[1] === 'string') {
262            mapTypeString = 'std::string, std::string';
263        } else if (mapType[1] === 'boolean') {
264            mapTypeString = 'std::string, bool';
265        } else if (mapType[1].substring(0, 12) === 'NUMBER_TYPE_') {
266            mapTypeString = 'std::string, %s'.format(mapType[1]);
267        } else if (mapType[1].substring(0, 12) === 'any') {
268            mapTypeString = `std::string, std::any`.format(mapType[1]);
269            return `\n    std::map<%s> %s;
270            std::string %s_type;`.format(mapTypeString, name, name);
271        } else if (InterfaceList.getValue(mapType[1])) {
272            mapTypeString = 'std::string, %s'.format(mapType[1]);
273        }
274    }
275    if (mapType[2] !== undefined) {
276        if (mapType[2] === 'string') {
277            mapTypeString = 'std::string, std::map<std::string, std::string>';
278        } else if (mapType[2] === 'boolean') {
279            mapTypeString = 'std::string, std::map<std::string, bool>';
280        } else if (mapType[2].substring(0, 12) === 'NUMBER_TYPE_') {
281            mapTypeString = 'std::string, std::map<std::string, %s>'.format(mapType[2]);
282        }
283    }
284    if (mapType[3] !== undefined) {
285        if (mapType[3] === 'string') {
286            mapTypeString = 'std::string, std::vector<std::string>';
287        } else if (mapType[3] === 'boolean') {
288            mapTypeString = 'std::string, std::vector<bool>';
289        } else if (mapType[3].substring(0, 12) === 'NUMBER_TYPE_') {
290            mapTypeString = 'std::string, std::vector<%s>'.format(mapType[3]);
291        }
292    }
293    if (optional) {
294        return '\n    std::optional<std::map<%s>> %s;'.format(mapTypeString, name);
295    } else {
296        return '\n    std::map<%s> %s;'.format(mapTypeString, name);
297    }
298}
299
300function anyTypeString(type, name) {
301    let anyType = `\n    std::string %s_type;
302    std::any %s;`;
303    return anyType.format(name, name);
304}
305
306function generateInterface(name, data, inNamespace) {
307    let param = { valueIn: '', valueOut: '', valueCheckout: '', valueFill: '',
308    valuePackage: '', valueDefine: '', optionalParamDestory: '' };
309    let getConParam = getConstructorFunc(data, param);
310    let resultConnect = connectResult(data, inNamespace, name);
311    let middleFunc = resultConnect[0];
312    let implH = resultConnect[1];
313    let implCpp = resultConnect[2];
314    let middleInit = resultConnect[3];
315    let middleH = resultConnect[4];
316    let selfNs = '';
317    if (inNamespace.length > 0) {
318        let nsl = inNamespace.split('::');
319        nsl.pop();
320        if (nsl.length >= 2) {
321            selfNs = ', ' + nsl[nsl.length - 1];
322        }
323    }
324    let toolNamespace = getToolNamespace(inNamespace);
325    middleInit += `\n    pxt->DefineClass("%s", %s%s%s_middle::constructor,
326        valueList, funcList%s);\n}\n`
327        .format(name, inNamespace, toolNamespace, name, selfNs);
328    let extendsStr = (data.parentNameList && data.parentNameList.length > 0) ?
329        ' : public %s'.format(data.parentNameList.join(', public ')) : '';
330    let result = {
331        implH: `
332class %s%s {
333public:%s\n
334};\n`.format(name, extendsStr, implH),
335        implCpp: implCpp,
336        middleBody: middleBodyTmplete.replaceAll('[className]', name).replaceAll('[static_funcs]', middleFunc)
337        .replaceAll('[getConstructorParam]', getConParam)
338        .replaceAll('[constructorParam]', param.valueFill),
339        middleInit: middleInit,
340        declarationH: `
341class %s;\r`.format(name),
342        middleH: middleHTmplete.replaceAll('[className]', name).replaceAll('[static_funcs]', middleH)
343        .replaceAll('[contructorValueIn]', param.valueIn)
344    };
345    return result;
346}
347
348function getConstructorFunc(data, param) {
349    let funcValues = null;
350    if (data.function !== null && data.function !== undefined) {
351        for (let i = 0; i < data.function.length; i++) {
352            if (data.function[i].name === 'constructor') {
353                funcValues = data.function[i].value;
354            }
355        }
356    }
357    for (let j in funcValues) {
358        paramGenerate(j, funcValues[j], param, data);
359    }
360    let tmpBody = param.valueCheckout.split(';\n');
361    let getConParam = '';
362    for (let i in tmpBody) {
363        let flag = tmpBody[i].replaceAll('\n', '').replaceAll(' ', '');
364        if (flag !== '') {
365            let indexBegin = tmpBody[i].indexOf('pxt->GetArgv(');
366            if (indexBegin > 0 && tmpBody[i].indexOf('\n') < 0) {
367              tmpBody[i] = tmpBody[i].replaceAll('pxt->GetArgv(', 'args[');
368              let index = tmpBody[i].indexOf(')');
369              tmpBody[i] = tmpBody[i].substring(0, index) + ']' + tmpBody[i].substring(index + 1, tmpBody[i].length);
370            } else if (indexBegin > 0 && tmpBody[i].indexOf('\n') >= 0) {
371              tmpBody[i] = tmpBody[i].replaceAll('pxt->GetArgv(', 'args[');
372              let index = tmpBody[i].indexOf('),');
373              tmpBody[i] = tmpBody[i].substring(0, index) + ']' + tmpBody[i].substring(index + 1, tmpBody[i].length);
374            }
375           getConParam += tmpBody[i] + ';\n';
376        }
377    }
378    let index = getConParam.lastIndexOf(';\n');
379    if (getConParam.substring(index - 1, index) === ' ') {
380      getConParam = getConParam.substring(0, index - 1);
381    }
382    return getConParam;
383}
384
385// 递归获取接口及接口父类的所有成员属性和方法
386function getAllPropties(interfaceBody, properties, isParentClass) {
387    for (let i in interfaceBody.value) {
388        interfaceBody.value[i].isParentMember = isParentClass;
389        addUniqObj2List(interfaceBody.value[i], properties.values);
390    }
391    for (let i in interfaceBody.function) {
392        interfaceBody.function[i].isParentMember = isParentClass;
393        if (!addUniqFunc2List(interfaceBody.function[i], properties.functions)) {
394            if (isParentClass) {
395                // 没添加到列表,说明子类方法properties.functions重写了父类方法interfaceBody.function[i]
396                // 找到该子类方法并将其设置为override (生成的重写函数如果没有override关键字会触发门禁告警)
397                setOverrideFunc(interfaceBody.function[i], properties.functions);
398            }
399        }
400    }
401    if (!isParentClass && interfaceBody.parentNameList && interfaceBody.parentNameList.length > 0) {
402        getAllPropties(interfaceBody.parentBody, properties, true);
403    }
404}
405
406function addVirtualKeywords(data, implH, name) {
407    if (data.childList && data.childList.length > 0) {
408        // 如果该类是其它类的父类,增加虚析构函数使其具备泛型特征 (基类必须有虚函数才能正确使用dynamic_cast和typeinfo等方法)
409        // 如果该类自己也有父类,虚析构函数需要加上override关键字(否则C++门禁会有告警)
410        let ovrrideStr = (data.parentList && data.parentList.length > 0) ? ' override' : '';
411        // 如果虚析构函数已经有override关键字,就不需要再加virtual关键字了(否则C++门禁会有告警)
412        let virtualStr = (data.parentList && data.parentList.length > 0) ? '' : 'virtual ';
413        implH += '\n    %s~%s()%s {};'.format(virtualStr, name, ovrrideStr);
414    }
415    return implH;
416}
417
418function connectResult(data, inNamespace, name) {
419    let implH = '';
420    let implCpp = '';
421    let middleFunc = '';
422    let middleInit = '';
423    let middleH = '';
424    let variable = {
425        hDefine: '',
426        middleValue: '',
427        middleH: ''
428    };
429    middleInit = getMiddleInitFunc(middleInit, data, variable, name, inNamespace);
430    implH += variable.hDefine;
431    middleFunc += variable.middleValue;
432    middleInit += `\n    std::map<const char *, napi_callback> funcList;`;
433    middleH += variable.middleH;
434    for (let i in data.allProperties.functions) {
435        let func = data.allProperties.functions[i];
436        let tmp;
437        if (isOnOffRegisterFunc(func.name)) {
438            tmp = generateFunctionOnOff(func, data, name);
439        } else if (isCreateThreadsafeFunc(func.name)) {
440            tmp = generateThreadsafeFunc(func, data, name);
441        }
442        if (!tmp) {
443            switch (func.type) {
444                case FuncType.DIRECT:
445                    tmp = generateFunctionDirect(func, data, name, implH);
446                    break;
447                case FuncType.SYNC:
448                    tmp = generateFunctionSync(func, data, name);
449                    break;
450                case FuncType.ASYNC:
451                case FuncType.PROMISE:
452                    tmp = generateFunctionAsync(func, data, name, implH);
453                    break;
454                default:
455                    break;
456            }
457        }
458        middleFunc += tmp[0];
459        implH += tmp[1];
460        implCpp += tmp[2];
461        middleH += tmp[3];
462        middleInit = generateMiddleInitFunc(func, inNamespace, middleInit, name);
463    }
464    implH = addVirtualKeywords(data, implH, name);
465    return [middleFunc, implH, implCpp, middleInit, middleH];
466}
467
468function generateMiddleInitFunc(func, inNamespace, middleInit, name) {
469    if (func.name !== 'constructor') {
470        let toolNamespace = getToolNamespace(inNamespace);
471        middleInit += `\n    funcList["%s"] = %s%s%s_middle::%s_middle;`.format(func.name,
472            inNamespace, toolNamespace, name, func.name);
473    }
474    return middleInit;
475}
476
477function getMiddleInitFunc(middleInit, data, variable, name, inNamespace) {
478    middleInit = `{\n    std::map<const char *, std::map<const char *, napi_callback>> valueList;`;
479    data.allProperties = { values: [], functions: [] };
480    getAllPropties(data, data.allProperties, false);
481    let toolNamespace = getToolNamespace(inNamespace);
482    for (let i in data.allProperties.values) {
483        let v = data.allProperties.values[i];
484        generateVariable(v, variable, name);
485        middleInit += `
486        valueList["%s"]["getvalue"] = %s%s%s_middle::getvalue_%s;
487        valueList["%s"]["setvalue"] = %s%s%s_middle::setvalue_%s;`
488        .format(v.name, inNamespace, toolNamespace, name, v.name, v.name, inNamespace, toolNamespace, name, v.name);
489    }
490    return middleInit;
491}
492
493function getToolNamespace(inNamespace) {
494    let index = inNamespace.lastIndexOf('::');
495    let toolNamespace;
496    if (index > 0) {
497      let bodyTmp = inNamespace.substring(0, index);
498      let index2 = bodyTmp.lastIndexOf('::');
499      if (index2 > 0 && index2 < index) {
500          toolNamespace = inNamespace.substring(index2 + 2, index) + '_interface::';
501      } else {
502          toolNamespace = bodyTmp + '_interface::';
503      }
504    } else {
505        toolNamespace = inNamespace + '_interface::';
506    }
507    return toolNamespace;
508}
509
510module.exports = {
511    generateInterface,
512    connectResult,
513    generateVariable,
514    mapTypeString,
515    anyTypeString,
516    getHDefineOfVariable
517};
518