#include "binfmt/Script.h"
#include "binfmt/ELF.h"
#include "thread/Scheduler.h"

#define SHEBANG "#!"

Result<bool> ScriptLoader::sniff()
{
    u8 buf[2];
    usize nread = TRY(m_inode->read(buf, 0, sizeof buf));
    if (nread < 2) return false;

    return !memcmp(buf, SHEBANG, 2);
}

Result<u64> ScriptLoader::load(AddressSpace* space)
{
    u8 buf[256];
    usize nread = TRY(m_inode->read(buf, 2, 255));
    if (!nread) return err(ENOEXEC);
    for (usize i = 0; i < nread; i++)
    {
        if (buf[i] == '\n') buf[i] = '\0';
        else if (buf[i] == '\r' && (i + 1) < nread && buf[i + 1] == '\n')
            buf[i] = buf[i + 1] = '\0';
        else
            continue;
        break;
    }

    auto view = StringView { (const char*)buf };
    m_interpreter_cmdline = TRY(view.split(" "));
    if (!m_interpreter_cmdline.size()) return err(ENOEXEC);

    auto& interpreter_path = m_interpreter_cmdline[0];
    auto* current = Scheduler::current();

    auto interpreter =
        TRY(VFS::resolve_path(interpreter_path.chars(), current->auth, current->current_directory, true));
    if (!VFS::can_execute(interpreter, current->auth)) return err(EACCES);

    auto loader = TRY(BinaryFormat::create_loader(interpreter, m_recursion_level + 1));
    u64 entry = TRY(loader->load(space));

    m_interpreter_cmdline = TRY(loader->cmdline(interpreter_path, move(m_interpreter_cmdline)));
    return entry;
}

Result<Vector<String>> ScriptLoader::cmdline(const String& path, Vector<String> args)
{
    Vector<String> new_args;
    TRY(new_args.try_reserve(m_interpreter_cmdline.size() + args.size() + 1));
    for (auto& arg : m_interpreter_cmdline) { TRY(new_args.try_append(move(arg))); }
    auto arg = TRY(path.clone());
    TRY(new_args.try_append(move(arg)));
    for (usize i = 1; i < args.size(); i++) { TRY(new_args.try_append(move(args[i]))); }
    return new_args;
}

ScriptLoader::ScriptLoader(SharedPtr<VFS::Inode> inode, int recursion_level)
    : BinaryFormatLoader(inode, recursion_level)
{
}

Result<SharedPtr<BinaryFormatLoader>> ScriptLoader::create(SharedPtr<VFS::Inode> inode, void*, int recursion_level)
{
    return (SharedPtr<BinaryFormatLoader>)TRY(make_shared<ScriptLoader>(inode, recursion_level));
}