/*
 * Copyright (C) 2022 Huawei Device Co., Ltd.
 * 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
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { TypeMeta } from './type_meta';
import { TypeFlag } from './type_flag';
import { BindFunction } from './bind_function';

export class Reflect {
    constructor(addon: any) {
        this.addon = addon;

        addon.JSBind.reflect(this.outputBuildInType.bind(this), this.outputFunction.bind(this));
    }

    formatTypeName(meta: TypeMeta, tplParams?: number[]): string {
        const flags = meta.flag;
        const kind = flags & TypeFlag.TYPE_MASK;

        let result: string;

        switch (kind) {
            case TypeFlag.IS_VOID:
                result = 'void';
                break;
            case TypeFlag.IS_BOOLEAN:
                result = 'boolean';
                break;
            case TypeFlag.IS_ARITHMETIC:
                result = 'number';
                break;
            case TypeFlag.IS_STRING:
                result = 'string';
                break;
            case TypeFlag.IS_ARRAY:
                if (tplParams !== undefined && typeof tplParams[0] === 'number') {
                    result = this.typeIdTbl[tplParams[0]].name + '[]';
                } else {
                    result = 'any';
                }
                break;
            case TypeFlag.IS_FUNCTION:
                console.info('function: ' + tplParams);
                if (tplParams !== undefined) {
                    const rtn: number = tplParams[0];
                    const params: number[] = tplParams.slice(1);
                    result = (
                        '(' + params.map(
                            (typeId: number, index: number) => 'a' + index + ': ' + this.typeIdTbl[typeId].name
                        ).join(', ') + ')'
                    );
                    result += ' => ' + this.typeIdTbl[rtn].name;
                } else {
                    result = 'function';
                }
                break;
            default:
                result = 'any';
        }
        return result;
    }

    outputBuildInType(id: number, flag: number) {
        console.info('outputBuildInType: ' + id + ', ' + flag);
        const meta: TypeMeta = {id, flag};
        meta.name = this.formatTypeName(meta);
        console.info('outputBuildInType meta: ' + meta.name);
        this.typeIdTbl[id] = meta;
    }

    outputFunction(name: string, params: number[]) {
        console.info('outputFunction: ' + name + ', ' + params);
        const typeList = params.map((id: number) => {
            if (this.typeIdTbl[id]) {
                console.info(this.typeIdTbl[id].name);
            } else {
                let flag: TypeFlag = TypeFlag.NONE;
                let params: number[] = [];
                this.addon.JSBind.queryType(id, (nativeFlag: number, nativeParams: number[]) => {
                    console.info('flag from native: ' + nativeFlag);
                    flag = nativeFlag;
                    params = nativeParams;
                });
                const meta: TypeMeta = {id, flag};
                meta.name = this.formatTypeName(meta, params);
                this.typeIdTbl[id] = meta;
                console.info(this.typeIdTbl[id].name);
            }
            return this.typeIdTbl[id];
        });

        const bindFunction = new BindFunction(
            name,
            typeList[0],
            typeList.slice(1),
            true);

        this.functionTbl.push(bindFunction);
    }

    addon: any;

    typeIdTbl: { [id: number]: TypeMeta } = {};

    functionTbl: BindFunction[] = [];
}