require 'bundler'
Bundler.require :default

require_relative '../../../lib/blitz3d/command'
require_relative '../../../lib/blitz3d/module'
require_relative '../../../lib/blitz3d/runtime'

module_dir = File.expand_path('modules', __dir__)

FileUtils.rm_rf module_dir
Dir.mkdir module_dir

Blitz3D::Module.all.each do |mod|
  next unless mod.public_symbols?

  File.open(File.expand_path("#{mod.id}.c", module_dir), 'w') do |f|
    f.write "// AUTOGENERATED. DO NOT EDIT.\n"
    f.write "// RUN `ruby src/bindings/luajit/generate.rb` TO UPDATE.\n\n"

    f.write "#include <bb/#{mod.id}/commands.h>\n\n"

    id = mod.id.tr('.', '_').tr('-', '_')

    f.write "#if #{mod.platforms.map { |p| "defined(BB_#{p.upcase})" }.join(' || ')}\n"
    f.write "#define BB_#{id.upcase}_ENABLED\n\n"

    mod.commands.each do |command|
      f.write "static int l_#{id}_#{command.name}( lua_State *L ){\n"

      command.params.each.with_index do |param, i|
        # lua_gettop()

        case param.type
        when '%'
          type = 'bb_int_t'
          expr = "luaL_checknumber( L,#{i + 1} )"
        when '#'
          type = 'bb_float_t'
          expr = "luaL_checknumber( L,#{i + 1} )"
        when '$'
          type = 'BBStr *'
          expr = "_bbStrConst( luaL_checkstring( L,#{i + 1} ) )"
        end

        ident = "__#{param.identifier}"

        if param.default
          f.write %(  #{type} #{ident};\n)
          f.write %(  if( lua_gettop( L ) > #{i} ){\n)
          f.write %(    #{ident} = #{expr};\n)
          f.write %(  }else{\n)

          case param.type
          when '%', '#'
            f.write %(   #{ident} = #{param.default};\n)
          when '$'
            f.write %(   #{ident} = "#{param.default}";\n)
          end

          f.write %(  }\n)
        else
          f.write "  #{type} #{ident} = #{expr};\n"
        end
      end

      invoke = "#{command.symbol}( #{command.params.map { |p| "__#{p.identifier}" }.join(',')} )"

      if command.return_type.present?
        case command.return_type
        when '%'
          f.write "  lua_pushnumber( L,(bb_int_t)#{invoke} );\n"
        when '#'
          f.write "  lua_pushnumber( L,(bb_float_t)#{invoke} );\n"
        when '$'
          f.write "  lua_pushstring( L,#{invoke} );\n"
        end

        f.write "  return 1;\n"
      else
        f.write "  #{invoke};\n"
        f.write "  return 0;\n"
      end
      f.write "}\n\n"
    end

    f.write "#endif\n"
  end
end

File.open(File.expand_path('loadlib.c', module_dir), 'w') do |f|
  f.write "// AUTOGENERATED. DO NOT EDIT.\n"
  f.write "// RUN `ruby src/bindings/luajit/generate.rb` TO UPDATE.\n\n"

  f.write "\nvoid blitzlua_loadlib( lua_State *L ){\n"

  Blitz3D::Module.all.each do |mod|
    next unless mod.public_symbols?

    id = mod.id.tr('.', '_').tr('-', '_')

    f.write "#ifdef BB_#{id.upcase}_ENABLED\n"

    mod.commands.each do |command|
      f.write %(  lua_pushcfunction( L,l_#{id}_#{command.name} );\n)
      f.write %(  lua_setglobal( L,"#{command.name}" );\n)
    end

    f.write "#endif\n"
  end

  f.write "}\n"
end


runtime_dir = File.expand_path('runtime', __dir__)

FileUtils.rm_rf runtime_dir
Dir.mkdir runtime_dir

Blitz3D::Runtime.all.each do |rt|
  File.open(File.expand_path("#{rt.id}.c", runtime_dir), 'w') do |f|
    f.write "// AUTOGENERATED. DO NOT EDIT.\n"
    f.write "// RUN `ruby src/bindings/luajit/generate.rb` TO UPDATE.\n\n"

    f.write "#include <luajit.h>\n"
    f.write "#include <lauxlib.h>\n\n"

    rt.dependencies(:list).each do |mod|
      next if !mod.public_symbols?

      f.write %(#include "../modules/#{mod.id}.c"\n)
    end

    f.write "#include \"../modules/loadlib.c\"\n\n"
  end
end

File.open(File.expand_path('CMakeLists.txt', runtime_dir), 'w') do |f|
  f.write "# AUTOGENERATED. DO NOT EDIT.\n"
  f.write "# RUN `ruby src/bindings/luajit/generate.rb` TO UPDATE.\n\n"

  f.write "include_directories(../../../modules)\n"
  f.write "if(NOT BB_MSVC)\n"
  f.write "  add_compile_options(-Wno-int-conversion)\n"
  f.write "endif()\n\n"

  Blitz3D::Runtime.all.each do |rt|
    f.write "if(TARGET runtime.#{rt.id}.static)\n"
    f.write "  blitzlua(#{rt.id})\n"
    f.write "endif()\n\n"
  end
end
