load("@bazel_skylib//rules:common_settings.bzl", "bool_flag")
load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
load("@rules_cuda//cuda:defs.bzl", "cuda_library", "requires_cuda")

copy_file(
    name = "highs-config",
    src = "highs/HConfig.h.bazel.in",
    out = "HConfig.h",
    visibility = ["//visibility:public"],
)

bool_flag(
    name = "cupdlp_gpu",
    build_setting_default = False,
    visibility = ["//visibility:public"],
)

config_setting(
    name = "cupdlp_gpu_enabled",
    flag_values = {
        "@rules_cuda//cuda:enable": "True",
        "@local_cuda//:valid_toolchain_found": "True",
        ":cupdlp_gpu": "True",
    },
)

cc_library(
    name = "config",
    srcs = ["HConfig.h"],
    visibility = ["//visibility:public"],
)

cuda_library(
    name = "cupdlp",
    srcs = glob([
        "highs/pdlp/cupdlp/cuda/*.cu",
        "highs/pdlp/hipdlp/*.cu",
    ]),
    hdrs = glob([
        "highs/pdlp/cupdlp/cuda/*.cuh",
    ]),
    defines = ["CUPDLP_GPU"],
    target_compatible_with = requires_cuda(),
    deps = [
        "@local_cuda//:cublas",
        "@local_cuda//:cusparse",
    ],
)

cc_library(
    name = "highs",
    srcs = [
        "highs/interfaces/highs_c_api.cpp",
        "highs/HighsExternalApi.cpp",
        "highs/HighsExternalDeps.cpp",
    ] + glob([
        "highs/io/*.cpp",
        "highs/io/filereaderlp/*.cpp",
        "highs/ipm/*.cpp",
        "highs/ipm/ipx/*.cc",
        "highs/ipm/basiclu/*.c",
        "highs/ipm/hipo/ipm/*.cpp",
        "highs/ipm/hipo/factorhighs/*.cpp",
        "highs/ipm/hipo/auxiliary/*.cpp",
        "highs/lp_data/*.cpp",
        "highs/mip/*.cpp",
        "highs/model/*.cpp",
        "highs/parallel/*.cpp",
        "highs/pdlp/*.cpp",
        "highs/pdlp/cupdlp/*.c",
        "highs/pdlp/hipdlp/*.cc",
        "highs/presolve/*.cpp",
        "highs/qpsolver/*.cpp",
        "highs/simplex/*.cpp",
        "highs/test_kkt/*.cpp",
        "highs/util/*.cpp",
    ]),
    hdrs = [
        "highs/mip/feasibilityjump.hh",
    ] + glob([
        "**/*.h",
        "highs/pdlp/hipdlp/*.hpp",
        "highs/io/filereaderlp/*.hpp",
        "highs/qpsolver/*.hpp",
        "extern/zstr/*.hpp",
    ]),
    copts = select({
        "@rules_cc//cc/compiler:msvc-cl": [],
        "//conditions:default": [
            "-fexceptions",
            "-Wno-unused-variable",
            "-Wno-unused-but-set-variable",
        ],
    }),
    defines = select({
        ":cupdlp_gpu_enabled": ["CUPDLP_GPU"],
        "//conditions:default": ["CUPDLP_CPU"],
    }),
    includes = [
        "extern",
        "extern/catch",
        # "extern/filereaderlp",
        # "extern/zstr",
        "highs",
        # "highs/ipm",
        # "highs/ipm/ipx",
        # "highs/ipm/basiclu",
        # "highs/lp_data",
        # "highs/mip",
        # "highs/model",
        # "highs/parallel",
        # "highs/presolve",
        # "highs/qpsolver",
        # "highs/simplex",
        # "highs/test_kkt",
        # "highs/util",
    ],
    linkopts = select({
        "@rules_cc//cc/compiler:msvc-cl": ["-DEFAULTLIB:shell32.lib"],
        "//conditions:default": ["-lpthread"],
    }),
    visibility = ["//visibility:public"],
    deps = [
        "//:config",
        "@zlib",
    ] + select({
        ":cupdlp_gpu_enabled": [":cupdlp"],
        "//conditions:default": [],
    }),
)

cc_library(
    name = "highs-runtime-opts",
    hdrs = [
        "extern/cli11/CLI11.hpp",
        "app/HighsRuntimeOptions.h",
    ],
    visibility = ["//visibility:public"],
    deps = [
        "//:highs",
    ],
)

cc_binary(
    name = "highs-bin",
    srcs = ["app/RunHighs.cpp"],
    visibility = ["//visibility:public"],
    deps = [
        "//:highs",
        "//:highs-runtime-opts",
    ],
)

cc_binary(
    name = "call-highs-example",
    srcs = ["examples/call_highs_from_cpp.cpp"],
    visibility = ["//visibility:public"],
    deps = [
        "//:highs",
    ],
)

## Add tests
copy_file(
    name = "highs-check-config",
    src = "check/HCheckConfig.h.bazel.in",
    out = "HCheckConfig.h",
)

cc_library(
    name = "check-config",
    srcs = ["HCheckConfig.h"],
)

cc_library(
    name = "test_lib",
    testonly = True,
    srcs = [
        "HCheckConfig.h",
        "check/Avgas.cpp",
        "check/TestMain.cpp",
    ],
    hdrs = [
        "check/Avgas.h",
        "check/SpecialLps.h",
        "check/matrix_multiplication.hpp",
        "extern/catch/catch.hpp",
    ],
    copts = ["-Iextern"],
    data = glob(["check/instances/*"]),
    includes = ["check"],
    deps = [
        ":highs",
        "//:check-config",
    ],
)

TEST_NAMES = [
    "TestAlienBasis",
    "TestBasis",
    "TestBasisSolves",
    "TestCheckSolution",
    "TestCrossover",
    "TestDualize",
    "TestEkk",
    "TestFactor",
    "TestFilereader",
    "TestHighsGFkSolve",
    "TestHighsHash",
    "TestHighsHessian",
    "TestHighsIntegers",
    "TestHighsModel",
    "TestHighsParallel",
    "TestHighsRbTree",
    "TestHSet",
    "TestICrash",
    "TestInfo",
    "TestIO",
    "TestIpx",
    "TestLogging",
    "TestLpModification",
    "TestLpOrientation",
    "TestLpSolvers",
    "TestLpValidation",
    "TestMipSolver",
    "TestPresolve",
    "TestPresolveRules",
    "TestQpSolver",
    "TestRanging",
    "TestRunData",
    "TestRays",
    "TestSemiVariables",
    "TestSetup",
    "TestSort",
    "TestSpecialLps",
    "TestThrow",
]

[cc_test(
    name = name,
    srcs = ["check/%s.cpp" % name],
    copts = ["-Iextern"] + select({
        "@rules_cc//cc/compiler:msvc-cl": [],
        "//conditions:default": [
            "-fexceptions",
            "-Wno-unused-variable",
            "-Wno-unused-but-set-variable",
        ],
    }),
    deps = [
        ":highs",
        ":test_lib",
    ],
) for name in TEST_NAMES]