local ffi = require("ffi")
local lib
ffi.cdef([[  typedef void MagickWand;
  typedef void PixelWand;

  typedef int MagickBooleanType;
  typedef int ExceptionType;
  typedef int ssize_t;
  typedef int CompositeOperator;
  typedef int GravityType;
  typedef int OrientationType;
  typedef int InterlaceType;

  void MagickWandGenesis();
  MagickWand* NewMagickWand();
  MagickWand* CloneMagickWand(const MagickWand *wand);
  MagickWand* DestroyMagickWand(MagickWand*);
  MagickBooleanType MagickReadImage(MagickWand*, const char*);
  MagickBooleanType MagickReadImageBlob(MagickWand*, const void*, const size_t);

  char* MagickGetException(const MagickWand*, ExceptionType*);

  int MagickGetImageWidth(MagickWand*);
  int MagickGetImageHeight(MagickWand*);

  MagickBooleanType MagickAddImage(MagickWand*, const MagickWand*);

  MagickBooleanType MagickAdaptiveResizeImage(MagickWand*, const size_t, const size_t);

  MagickBooleanType MagickWriteImage(MagickWand*, const char*);

  unsigned char* MagickGetImageBlob(MagickWand*, size_t*);

  void* MagickRelinquishMemory(void*);

  MagickBooleanType MagickCropImage(MagickWand*,
    const size_t, const size_t, const ssize_t, const ssize_t);

  MagickBooleanType MagickBlurImage(MagickWand*, const double, const double);
  MagickBooleanType MagickModulateImage(MagickWand*, const double, const double, const double);
  MagickBooleanType MagickBrightnessContrastImage(MagickWand*, const double, const double);

  MagickBooleanType MagickSetImageFormat(MagickWand* wand, const char* format);
  char* MagickGetImageFormat(MagickWand* wand);

  size_t MagickGetImageCompressionQuality(MagickWand * wand);
  MagickBooleanType MagickSetImageCompressionQuality(MagickWand *wand,
  const size_t quality);

  MagickBooleanType MagickSharpenImage(MagickWand *wand,
    const double radius,const double sigma);

  MagickBooleanType MagickScaleImage(MagickWand *wand,
    const size_t columns,const size_t rows);

  MagickBooleanType MagickRotateImage(MagickWand *wand,
  const PixelWand *background,const double degrees);

  MagickBooleanType MagickSetOption(MagickWand *,const char *,const char *);
  char* MagickGetOption(MagickWand *,const char *);

  MagickBooleanType MagickCompositeImage(MagickWand *wand,
    const MagickWand *source_wand,const CompositeOperator compose,
    const ssize_t x,const ssize_t y);

  GravityType MagickGetImageGravity(MagickWand *wand);
  MagickBooleanType MagickSetImageGravity(MagickWand *wand,
    const GravityType gravity);

  MagickBooleanType MagickStripImage(MagickWand *wand);

  MagickBooleanType MagickGetImagePixelColor(MagickWand *wand,
    const ssize_t x,const ssize_t y,PixelWand *color);

  MagickWand* MagickCoalesceImages(MagickWand*);

  PixelWand *NewPixelWand(void);
  PixelWand *DestroyPixelWand(PixelWand *);

  double PixelGetAlpha(const PixelWand *);
  double PixelGetRed(const PixelWand *);
  double PixelGetGreen(const PixelWand *);
  double PixelGetBlue(const PixelWand *);

  void PixelSetAlpha(PixelWand *wand, const double alpha);
  void PixelSetRed(PixelWand *wand, const double red);
  void PixelSetGreen(PixelWand *wand, const double green);
  void PixelSetBlue(PixelWand *wand, const double blue);

  MagickBooleanType MagickTransposeImage(MagickWand *wand);

  MagickBooleanType MagickTransverseImage(MagickWand *wand);

  MagickBooleanType MagickFlipImage(MagickWand *wand);

  MagickBooleanType MagickFlopImage(MagickWand *wand);

  char* MagickGetImageProperty(MagickWand *wand, const char *property);
  MagickBooleanType MagickSetImageProperty(MagickWand *wand,
    const char *property,const char *value);

  OrientationType MagickGetImageOrientation(MagickWand *wand);
  MagickBooleanType MagickSetImageOrientation(MagickWand *wand,
    const OrientationType orientation);

  InterlaceType MagickGetImageInterlaceScheme(MagickWand *wand);
  MagickBooleanType MagickSetImageInterlaceScheme(MagickWand *wand,
    const InterlaceType interlace_scheme);

  MagickBooleanType MagickAutoOrientImage(MagickWand *wand);

  MagickBooleanType MagickResetImagePage(MagickWand *wand, const char *page);

  MagickBooleanType MagickSetImageDepth(MagickWand *,const unsigned long);
  unsigned long MagickGetImageDepth(MagickWand *);

]])
local get_flags
get_flags = function()
	local nvim_data_pkgconfig = vim.fn.stdpath("data") .. "/magick/lib/pkgconfig"
	local proc = io.popen(
		[[PKG_CONFIG_PATH="]]
			.. nvim_data_pkgconfig
			.. [[:$HOME/.local/lib/pkgconfig:/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH" pkg-config --cflags --libs MagickWand]],
		"r"
	)
	local flags = proc:read("*a")
	get_flags = function()
		return flags
	end
	proc:close()
	return flags
end
local get_filters
get_filters = function()
	local suffixes = {
		"magick/resample.h",
		"MagickCore/resample.h",
	}
	local prefixes = {
		vim.fn.stdpath("data") .. "/magick/include/ImageMagick",
		"/usr/include/ImageMagick",
		"/usr/local/include/ImageMagick",
		vim.fn.expand("$HOME") .. "/.local/include/ImageMagick",
		"/home/linuxbrew/.linuxbrew/include/ImageMagick",
		unpack((function()
			local _accum_0 = {}
			local _len_0 = 1
			for p in get_flags():gmatch("-I([^%s]+)") do
				_accum_0[_len_0] = p
				_len_0 = _len_0 + 1
			end
			return _accum_0
		end)()),
	}
	for _index_0 = 1, #prefixes do
		local p = prefixes[_index_0]
		for _index_1 = 1, #suffixes do
			local suffix = suffixes[_index_1]
			local full = tostring(p) .. "/" .. tostring(suffix)
			do
				local f = io.open(full)
				if f then
					local content
					do
						local _with_0 = f:read("*a")
						f:close()
						content = _with_0
					end
					local filter_types = content:match("(typedef enum.-FilterTypes?;)")
					if filter_types then
						ffi.cdef(filter_types)
						if filter_types:match("FilterTypes;") then
							return "FilterTypes"
						end
						return "FilterType"
					end
				end
			end
		end
	end
	return false
end
local get_filter
get_filter = function(name)
	return lib[name .. "Filter"]
end
local can_resize
do
	local enum_name = get_filters()
	if enum_name then
		ffi.cdef([[    MagickBooleanType MagickResizeImage(MagickWand*,
      const size_t, const size_t,
      const ]] .. enum_name .. [[, const double);
  ]])
		can_resize = true
	end
end
local try_to_load
try_to_load = function(...)
	local out
	local _list_0 = {
		...,
	}
	for _index_0 = 1, #_list_0 do
		local _continue_0 = false
		repeat
			local name = _list_0[_index_0]
			if "function" == type(name) then
				name = name()
				if not name then
					_continue_0 = true
					break
				end
			end
			if pcall(function()
				out = ffi.load(name)
			end) then
				return out
			end
			_continue_0 = true
		until true
		if not _continue_0 then
			break
		end
	end
	return error("Failed to load ImageMagick (" .. tostring(...) .. ")")
end

--- e.g. libMagickWand-7.Q16HDRI.so
local function get_lib_name()
	local lname = get_flags():match("-l(MagickWand[^%s]*)")
	local suffix
	if ffi.os == "OSX" then
		suffix = ".dylib"
	elseif ffi.os == "Windows" then
		suffix = ".dll"
	else
		suffix = ".so"
	end
	return lname and "lib" .. lname .. suffix
end

local lib_name = get_lib_name()
local lib_path_in_nvim_data = lib_name and vim.fn.stdpath("data") .. "/magick/lib/" .. lib_name

lib = try_to_load(lib_path_in_nvim_data, "MagickWand", lib_name)

return {
	lib = lib,
	can_resize = can_resize,
	get_filter = get_filter,
}
