#!/usr/bin/env lua
--Set up paths so we can access Brat runtime files
program_path = debug.getinfo(1, "S").source
program_path = program_path:match("([^@].+/)bin/brat$")

if program_path == nil then
    program_path = "../"
end

package.cpath = package.cpath .. ";" .. program_path .. "lib/?.so"
package.path = package.path ..
";" .. program_path .. "core/?.lua;" .. program_path .. "stdlib/?.lua;" .. program_path .. "lib/?.lua"

require "lfs"

io.output(io.stdout)

local abort = function(message)
    print(message)
    os.exit(-1)
end

require 'core'
require "parser/brat2lua"
local brat2lua = _exports['brat2lua']

local compile_file = function(file_name, force)
    local brat_file_name = file_name .. ".brat"
    local lua_file_name = file_name .. ".lua"

    local brat_modified = lfs.attributes(brat_file_name, "modification")

    --.brat file does not exist
    if brat_modified == nil then
        abort("Could not open " .. brat_file_name)
    end

    local lua_modified = lfs.attributes(lua_file_name, "modification")

    if force or lua_modified == nil or brat_modified > lua_modified then
        brat2lua:compile_underfile(base_string:new(file_name))
    end
end

local interactive = function()
    local ffi = require "ffi"

    ffi.cdef [[
  int linenoiseHistoryAdd(const char *line);
  char *linenoise(const char *prompt);
  ]]

    local ln = ffi.load("linenoise")

    local get_line = ln.linenoise;
    local add_line = ln.linenoiseHistoryAdd;

    local input
    local line = 1
    local parsed_function
    local lua_code
    _exports = {}

    local interactive_env = {
        require = require,
        package = package,
        print = print,
        object = object,
        array = array,
        number = number,
        string = base_string,
        exception = exception,
        hash = hash,
        regex = regex,
        symbol = symbol,
        _self = object,
        _type = type,
        _error = error,
        _tostring = tostring,
        _exports = _exports,
        _rawget = rawget,
        _table = table,
    }

    local rescue = function(obj, err)
        return err
    end

    local protect_options = hash:new({ [base_string:new("rescue")] = rescue })
    local compiler = brat2lua:start_underinteractive()

    print("# Interactive Brat")

    while true do
        input = get_line("brat:" .. line .. "> ")

        if input ~= nil then
            input = ffi.string(input)
        end

        if input == nil or input == "quit" then
            return
        elseif input ~= "" and not input:match("^%s+$") then
            line = line + 1
            add_line(input)

            lua_code = compiler:run(base_string:new(input))._lua_string

            parsed_function = assert(loadstring(lua_code, "interactive"))

            setfenv(parsed_function, interactive_env)

            print("#=> " .. tostring(object:protect(parsed_function, protect_options)))

            for k, v in pairs(_exports) do
                interactive_env[k] = v
            end

            _exports = {}
        end
    end
end

local run_string = function(string)
    local parsed = brat2lua:compile_understring(base_string:new(string))._lua_string

    parsed_function = assert(load(parsed, "string"))

    local rescue = function(obj, err)
        return err
    end

    local protect_options = hash:new({ [base_string:new("rescue")] = rescue })

    local result = tostring(object:protect(parsed_function, protect_options))

    return result
end

local get_filename = function(arg)
    local file_name = arg:match("(.+).brat")

    if file_name == nil then
        abort("File to execute should end in .brat: " .. tostring(arg))
    end

    return file_name
end

if #arg == 0 then
    interactive()
    abort("Exiting")
end

local first_arg = arg[1]

if first_arg == "-!" then
    load(io.read("*a"))()
elseif first_arg == "-" then
    run_string(io.read("*a"))
else
    local file_name

    if first_arg == "-x" or first_arg == "-f" then
        file_name = get_filename(arg[2])
    else
        file_name = get_filename(first_arg)
    end

    if first_arg == '-f' then
        compile_file(file_name, true)
    else
        compile_file(file_name)
    end

    if first_arg == '-x' then
        dofile(file_name .. ".lua")
        os.remove(file_name .. ".lua")
    end
end