1/*
2 * Copyright (c) 2023 Huawei Device 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 */
15
16import type { SourceFile } from 'typescript';
17import { SyntaxKind } from 'typescript';
18import type { FunctionEntity } from '../declaration-node/functionDeclaration';
19import {
20  getCallbackStatement,
21  getReturnStatement,
22  getWarnConsole,
23  getReturnData,
24  getOverloadedFunctionCallbackStatement,
25  overloadedFunctionArr
26} from './generateCommonUtil';
27import { methodArrayItemForEach } from './generateCommonMethod';
28
29interface AssemblyFunctionProps {
30  functionArray: Array<FunctionEntity>;
31  functionEntity: FunctionEntity;
32  functionBody: string;
33  sourceFile: SourceFile;
34  mockApi: string;
35}
36
37interface AssemblyFunctionBack {
38  isReturnPromise: boolean;
39  promiseReturnValue: string;
40  functionOtherReturnValue: string;
41  isCallBack: boolean;
42  functionBody: string;
43}
44
45/**
46 * generate function
47 * @param rootName
48 * @param functionArray
49 * @param sourceFile
50 * @param mockApi
51 * @param isRoot
52 * @returns
53 */
54export function generateCommonFunction(
55  rootName: string,
56  functionArray: Array<FunctionEntity>,
57  sourceFile: SourceFile,
58  mockApi: string,
59  isRoot: boolean
60): string {
61  let functionBody = '';
62  const functionEntity = functionArray[0];
63  if (isRoot) {
64    functionBody = `${functionEntity.isExport ? 'export ' : ''}const ${
65      functionEntity.functionName
66    } = function(...args) {`;
67  } else {
68    functionBody = `${functionEntity.functionName}: function(...args) {`;
69  }
70  functionBody += getWarnConsole(rootName, functionEntity.functionName);
71
72  if (functionArray.length === 1) {
73    const args = functionEntity.args;
74    const len = args.length;
75    if (len && args[len - 1].paramName.toLowerCase().includes('callback')) {
76      functionBody += getCallbackStatement(mockApi, args[len - 1]?.paramTypeString);
77    }
78    if (functionEntity.returnType.returnKind !== SyntaxKind.VoidKeyword) {
79      if (rootName === 'featureAbility' && functionEntity.returnType.returnKindName === 'Context') {
80        functionBody += 'return _Context;';
81      } else if (rootName === 'inputMethod' && functionEntity.returnType.returnKindName === 'InputMethodSubtype') {
82        functionBody += 'return mockInputMethodSubtype().InputMethodSubtype;';
83      } else {
84        functionBody += getReturnStatement(functionEntity.returnType, sourceFile);
85      }
86    }
87  } else {
88    const assemblyBack = assemblyFunctionBody({ functionArray, functionEntity, functionBody, mockApi, sourceFile });
89    functionBody = assemblyFuntion(assemblyBack, functionArray, sourceFile, mockApi);
90    functionBody = assemblyBack.functionBody;
91  }
92  functionBody += isRoot ? '};' : '},';
93  if (isRoot) {
94    functionBody += `
95      if (!global.${functionEntity.functionName}) {
96        global.${functionEntity.functionName} = ${functionEntity.functionName};
97      }
98    `;
99  }
100  return functionBody;
101}
102
103/**
104 * generate assembly function
105 * @param props
106 * @returns
107 */
108function assemblyFunctionBody(props: AssemblyFunctionProps): AssemblyFunctionBack {
109  let argSet: Set<string> = new Set<string>();
110  let argParamsSet: string = '';
111  let returnSet: Set<string> = new Set<string>();
112  let isCallBack = false;
113  let needOverloaded = false;
114  props.functionArray.forEach(value => {
115    ({ returnSet, argSet, isCallBack, argParamsSet, needOverloaded} =
116      methodArrayItemForEach({returnSet, value, argSet, isCallBack, argParamsSet, needOverloaded}));
117  });
118  props.functionBody = forEachFuntionArray(isCallBack, props, needOverloaded, argParamsSet);
119  let isReturnPromise = false;
120  let promiseReturnValue = '';
121  let functionOtherReturnValue = '';
122  returnSet.forEach(value => {
123    if (value.includes('Promise<')) {
124      isReturnPromise = true;
125      promiseReturnValue = value;
126    } else {
127      if (!functionOtherReturnValue) {
128        functionOtherReturnValue = value;
129      }
130    }
131  });
132  return {
133    isReturnPromise,
134    promiseReturnValue,
135    functionOtherReturnValue,
136    isCallBack,
137    functionBody: props.functionBody
138  };
139}
140
141/**
142 * forEach functionArray
143 * @param isCallBack
144 * @param props
145 * @param needOverloaded
146 * @param argParamsSet
147 * @returns
148 */
149function forEachFuntionArray(
150  isCallBack: boolean,
151  props: AssemblyFunctionProps,
152  needOverloaded: boolean,
153  argParamsSet: string
154): string {
155  if (isCallBack) {
156    if (overloadedFunctionArr.includes(props.functionEntity.functionName) && needOverloaded) {
157      const stateEment = getOverloadedFunctionCallbackStatement(props.functionArray, props.sourceFile, props.mockApi);
158      props.functionBody += stateEment;
159    } else {
160      props.functionBody += getCallbackStatement(props.mockApi, argParamsSet);
161    }
162  }
163  return props.functionBody;
164}
165
166/**
167 * assembly Function
168 * @param porps
169 * @param functionArray
170 * @param sourceFile
171 * @param mockApi
172 * @returns
173 */
174function assemblyFuntion(
175  porps: AssemblyFunctionBack,
176  functionArray: Array<FunctionEntity>,
177  sourceFile: SourceFile,
178  mockApi: string
179): string {
180  if (porps.isReturnPromise) {
181    if (porps.promiseReturnValue) {
182      let returnType = null;
183      functionArray.forEach(value => {
184        if (value.returnType.returnKindName === porps.promiseReturnValue) {
185          returnType = value.returnType;
186        }
187      });
188      porps.functionBody += getReturnData(porps.isCallBack, porps.isReturnPromise, returnType, sourceFile, mockApi);
189    } else {
190      porps.functionBody += `
191          return new Promise((resolve, reject) => {
192            resolve('[PC Preview] unknow boolean');
193          })
194        `;
195    }
196  } else if (porps.functionOtherReturnValue) {
197    let returnType = null;
198    functionArray.forEach(value => {
199      if (value.returnType.returnKindName === porps.functionOtherReturnValue) {
200        returnType = value.returnType;
201      }
202    });
203    porps.functionBody += getReturnData(porps.isCallBack, porps.isReturnPromise, returnType, sourceFile, mockApi);
204  }
205  return porps.functionBody;
206}
207