/*!A cross-platform build utility based on Lua
 *
 * 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.
 *
 * Copyright (C) 2015-present, Xmake Open Source Community.
 *
 * @author      ruki
 * @file        deplibs.c
 *
 */

/* //////////////////////////////////////////////////////////////////////////////////////
 * trace
 */
#define TB_TRACE_MODULE_NAME "deplibs_macho"
#define TB_TRACE_MODULE_DEBUG (0)

/* //////////////////////////////////////////////////////////////////////////////////////
 * includes
 */
#include "prefix.h"

/* //////////////////////////////////////////////////////////////////////////////////////
 * private implementation
 */

/* //////////////////////////////////////////////////////////////////////////////////////
 * implementation
 */
tb_bool_t xm_binutils_macho_deplibs(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua) {
    tb_assert_and_check_return_val(istream && lua, tb_false);

    // init Mach-O context
    xm_macho_context_t context;
    if (!xm_binutils_macho_context_init(istream, base_offset, &context)) {
        return tb_false;
    }

    // skip header to reach load commands
    tb_size_t header_size = context.is64 ? sizeof(xm_macho_header_64_t) : sizeof(xm_macho_header_32_t);
    if (!tb_stream_seek(istream, base_offset + header_size)) {
        return tb_false;
    }

    lua_newtable(lua);
    tb_size_t result_count = 0;

    // iterate load commands
    for (tb_uint32_t i = 0; i < context.ncmds; i++) {
        xm_macho_load_command_t lc;
        tb_hize_t current_cmd_offset = tb_stream_offset(istream);
        
        if (!tb_stream_bread(istream, (tb_byte_t*)&lc, sizeof(lc))) {
            return tb_false;
        }
        xm_binutils_macho_swap_load_command(&lc, context.swap);

        // check for LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB, LC_REEXPORT_DYLIB, LC_ID_DYLIB
        if (lc.cmd == XM_MACHO_LC_LOAD_DYLIB || lc.cmd == XM_MACHO_LC_ID_DYLIB || 
            lc.cmd == XM_MACHO_LC_LOAD_WEAK_DYLIB || lc.cmd == XM_MACHO_LC_REEXPORT_DYLIB) {
            
            xm_macho_dylib_command_t dc;
            if (tb_stream_seek(istream, current_cmd_offset)) {
                 if (tb_stream_bread(istream, (tb_byte_t*)&dc, sizeof(dc))) {
                     xm_binutils_macho_swap_dylib_command(&dc, context.swap);
                     
                     tb_uint32_t name_offset = dc.dylib.offset;
                     if (name_offset < lc.cmdsize) {
                         // name is at current_cmd_offset + name_offset
                         if (tb_stream_seek(istream, current_cmd_offset + name_offset)) {
                             tb_char_t dylib_path[1024];
                             tb_size_t max_len = lc.cmdsize - name_offset;
                             if (max_len > sizeof(dylib_path) - 1) max_len = sizeof(dylib_path) - 1;
                             
                             tb_size_t pos = 0;
                             tb_byte_t c;
                             while (pos < max_len) {
                                 if (!tb_stream_bread(istream, &c, 1)) break;
                                 if (c == 0) break;
                                 dylib_path[pos++] = (tb_char_t)c;
                             }
                             dylib_path[pos] = '\0';
                             
                             if (pos > 0) {
                                 lua_pushinteger(lua, result_count + 1);
                                 lua_pushstring(lua, dylib_path);
                                 lua_settable(lua, -3);
                                 result_count++;
                             }
                         }
                     }
                 }
            }
        }

        // move to next command
        if (!tb_stream_seek(istream, current_cmd_offset + lc.cmdsize)) {
            break;
        }
    }

    return tb_true;
}
