/*
 * Copyright (c) 2021-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.
 */

#include "disassembler.h"
#include "utils/const_value.h"

#include <sstream>
#include <iomanip>

namespace panda::disasm {

void Disassembler::GetInsInfo(const panda_file::File::EntityId& code_id, MethodInfo* method_info /* out */) const {
    panda_file::CodeDataAccessor code_accessor(*file_, code_id);

    auto ins_sz = code_accessor.GetCodeSize();
    auto ins_arr = code_accessor.GetInstructions();

    auto bc_ins = BytecodeInstruction(ins_arr);
    auto bc_ins_last = bc_ins.JumpTo(ins_sz);

    auto instruction_offset = code_id.GetOffset() + static_cast<uint32_t>(code_accessor.GetInstructions() - file_->GetSpanFromId(code_id).data());
    while (bc_ins.GetAddress() != bc_ins_last.GetAddress()) {
        std::stringstream ss;

        ss << "offset: 0x" << std::setfill('0') << std::setw(ark::INSTRUCTION_OFFSET_WIDTH) << std::hex
           << instruction_offset + bc_ins.GetAddress() - BytecodeInstruction(ins_arr).GetAddress();
        ss << ", " << std::setfill('.');

        BytecodeInstruction::Format format = bc_ins.GetFormat();

        switch (format) {
% Panda::formats.each do |fmt|
        case BytecodeInstruction::Format::<%= fmt.pretty.upcase %>:
            ss << std::setw(ark::INSTRUCTION_FORMAT_WIDTH) << std::left << "[<%= fmt.pretty.upcase %>]";
            break;
% end
        default:
            break;
        }

        ss << "[";

        const uint8_t* pc = bc_ins.GetAddress();
        const size_t sz = bc_ins.GetSize();

        for (size_t i = 0; i < sz; i++) {
            ss << "0x" << std::setw(ark::INSTRUCTION_VALUE_WIDTH) << std::setfill('0') << std::right << std::hex << static_cast<int>(pc[i]);

            if (i != sz - 1) {
                ss << " ";
            }
        }

        ss << "]";

        method_info->instructions_info.push_back(ss.str());

        bc_ins = bc_ins.GetNext();
    }

    return;
}

} // namespace panda::disasm