/*
 * Copyright (c) 2024 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 { Language } from "../../Language.js"
import { IndentedPrinter } from "../../IndentedPrinter.js"
import { PrimitiveTypeList } from "../../peer-generation/PrimitiveType.js"
import * as idl from "../../idl/index.js"
import {
    ExpressionStatement,
    LanguageExpression,
    LanguageStatement,
    LanguageWriter,
    Method,
    PrintHint,
    MethodModifier,
    MethodSignature,
    ReturnStatement
} from "../LanguageWriter.js"
import { ReferenceResolver } from "../../peer-generation/ReferenceResolver.js";

////////////////////////////////////////////////////////////////
//                         STATEMENTS                         //
////////////////////////////////////////////////////////////////

export class CLikeReturnStatement extends ReturnStatement {
    constructor(public expression: LanguageExpression) { super(expression) }
    write(writer: LanguageWriter): void {
        writer.print(this.expression ? `return ${this.expression.asString()};` : "return;")
    }
}

export class CLikeLoopStatement implements LanguageStatement {
    constructor(private counter: string, private limit: string, private statement: LanguageStatement | undefined) {}
    write(writer: LanguageWriter): void {
        writer.print(`for (int ${this.counter} = 0; ${this.counter} < ${this.limit}; ${this.counter}++) {`)
        if (this.statement) {
            writer.pushIndent()
            this.statement.write(writer)
            writer.popIndent()
            writer.print("}")
        }
    }
}

export class CLikeExpressionStatement extends ExpressionStatement {
    constructor(public expression: LanguageExpression) { super(expression) }
    write(writer: LanguageWriter): void {
        const text = this.expression.asString()
        if (text.length > 0) {
            writer.print(`${this.expression.asString()};`)
        }
    }
}

class CLikeThrowErrorStatement implements LanguageStatement {
    constructor(public exception: LanguageExpression) { }
    write(writer: LanguageWriter): void {
        writer.print(`throw ${this.exception.asString()};`)
    }
}

////////////////////////////////////////////////////////////////
//                           WRITER                           //
////////////////////////////////////////////////////////////////

export abstract class CLikeLanguageWriter extends LanguageWriter {
    protected constructor(printer: IndentedPrinter, resolver: ReferenceResolver, language: Language) {
        super(printer, resolver, language)
    }
    writeFunctionDeclaration(name: string, signature: MethodSignature): void {
        this.writeMethodDeclaration(name, signature)
    }
    writeFunctionImplementation(name: string, signature: MethodSignature, op: (writer: this) => void): void {
        this.writeMethodImplementation(new Method(name, signature), op)
    }
    makeThrowError(message: string | LanguageExpression): LanguageStatement {
        if (typeof message === 'string')
            message = this.makeString(`new Error("${message}")`)
        return new CLikeThrowErrorStatement(message)
    }
    makeEquals(args: LanguageExpression[]): LanguageExpression {
        return this.makeNaryOp("==", args)
    }
    writeMethodCall(receiver: string, method: string, params: string[], nullable = false): void {
        this.printer.print(`${receiver}.${method}(${params.join(", ")});`)
    }
    writeMethodDeclaration(name: string, signature: MethodSignature, modifiers?: MethodModifier[]): void {
        this.writeDeclaration(name, signature, modifiers, ";")
    }
    writeEnum(name: string, members: { name: string, stringId: string | undefined, numberId: number }[], options: { isExport: boolean, isDeclare?: boolean }, op: (writer: LanguageWriter) => void): void {
        throw new Error("WriteEnum for C-family languages is not implemented")
    }
    writeMethodImplementation(method: Method, op: (writer: this) => void) {
        this.writeDeclaration(method.name, method.signature, method.modifiers)
        this.printer.print(`{`)
        this.pushIndent()
        op(this)
        this.popIndent()
        this.printer.print(`}`)
    }
    protected writeDeclaration(name: string, signature: MethodSignature, modifiers?: MethodModifier[], postfix?: string): void {
        let prefix = modifiers
            ?.filter(it => this.supportedModifiers.includes(it))
            .map(it => this.mapMethodModifier(it)).join(" ")
        prefix = prefix ? prefix + " " : ""
        this.print(`${prefix}${this.stringifyMethodReturnType(signature.returnType, signature.retHint())} ${name}(${signature.args.map((it, index) => `${this.stringifyMethodArgType(it, signature.argHint(index))} ${signature.argName(index)}`).join(", ")})${postfix ?? ""}`)
    }
    protected stringifyMethodReturnType(type: idl.IDLType, _?:PrintHint): string {
        return this.getNodeName(type)
    }
    protected stringifyMethodArgType(type: idl.IDLType, _?:PrintHint): string {
        return this.getNodeName(type)
    }
}