package iup

import (
	"image"
	"image/draw"
	"unsafe"

)

/*
#include <stdlib.h>
#include "iup.h"

static void Log(const char* type, const char* str) {
	IupLog(type, "%s", str);
}
*/
import "C"

// Image creates an image to be shown on a label, button, toggle, or as a cursor.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/elem/iup_image.md
func Image(width, height int, pixMap []byte) Ihandle {
	h := mkih(C.IupImage(C.int(width), C.int(height), (*C.uchar)(unsafe.Pointer(&pixMap[0]))))
	return h
}

// ImageRGB creates an image to be shown on a label, button, toggle, or as a cursor.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/elem/iup_image.md
func ImageRGB(width, height int, pixMap []byte) Ihandle {
	h := mkih(C.IupImageRGB(C.int(width), C.int(height), (*C.uchar)(unsafe.Pointer(&pixMap[0]))))
	return h
}

// ImageRGBA creates an image to be shown on a label, button, toggle, or as a cursor.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/elem/iup_image.md
func ImageRGBA(width, height int, pixMap []byte) Ihandle {
	h := mkih(C.IupImageRGBA(C.int(width), C.int(height), (*C.uchar)(unsafe.Pointer(&pixMap[0]))))
	return h
}

// ImageFromImage creates an image from image.Image.
func ImageFromImage(i image.Image) Ihandle {
	if img, ok := i.(*image.RGBA); ok {
		h := mkih(C.IupImageRGBA(C.int(img.Bounds().Dx()), C.int(img.Bounds().Dy()), (*C.uchar)(unsafe.Pointer(&img.Pix[0]))))
		return h
	}

	b := i.Bounds()
	dst := image.NewRGBA(image.Rect(0, 0, b.Dx(), b.Dy()))
	draw.Draw(dst, dst.Bounds(), i, b.Min, draw.Src)

	h := mkih(C.IupImageRGBA(C.int(dst.Bounds().Dx()), C.int(dst.Bounds().Dy()), (*C.uchar)(unsafe.Pointer(&dst.Pix[0]))))
	return h
}

// ImageToImage returns an image.RGBA from an IUP image handle.
// The returned image is a copy of the IUP image data.
func ImageToImage(ih Ihandle) *image.RGBA {
	width := GetInt(ih, "WIDTH")
	height := GetInt(ih, "HEIGHT")
	bpp := GetInt(ih, "BPP")
	if width == 0 || height == 0 || (bpp != 24 && bpp != 32) {
		return nil
	}

	channels := bpp / 8
	size := width * height * channels

	cWID := C.CString("WID")
	defer C.free(unsafe.Pointer(cWID))

	ptr := C.IupGetAttribute(ih.ptr(), cWID)
	if ptr == nil {
		return nil
	}

	src := unsafe.Slice((*byte)(unsafe.Pointer(ptr)), size)

	img := image.NewRGBA(image.Rect(0, 0, width, height))

	if bpp == 32 {
		copy(img.Pix, src)
	} else {
		for i, j := 0, 0; i < size; i, j = i+3, j+4 {
			img.Pix[j] = src[i]
			img.Pix[j+1] = src[i+1]
			img.Pix[j+2] = src[i+2]
			img.Pix[j+3] = 255
		}
	}

	return img
}

// ImageGetHandle returns an IupImage handle from a name.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_imagegethandle.md
func ImageGetHandle(name string) Ihandle {
	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	h := mkih(C.IupImageGetHandle(cName))
	return h
}

// ImageSave saves an IUP image to a file in the specified format.
// Format can be "PNG", "JPEG", or "BMP". If empty, the format is detected from the filename extension.
// Returns 1 on success, 0 on failure.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_imagesave.md
func ImageSave(ih Ihandle, filename, format string) int {
	cFilename := C.CString(filename)
	defer C.free(unsafe.Pointer(cFilename))

	var cFormat *C.char
	if format != "" {
		cFormat = C.CString(format)
		defer C.free(unsafe.Pointer(cFormat))
	}

	return int(C.IupImageSave(ih.ptr(), cFilename, cFormat))
}

// ImageSaveToBuffer saves an IUP image to a byte slice in the specified format.
// Format must be "PNG", "JPEG", or "BMP".
// Returns nil if the operation fails.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_imagesavetobuffer.md
func ImageSaveToBuffer(ih Ihandle, format string) []byte {
	cFormat := C.CString(format)
	defer C.free(unsafe.Pointer(cFormat))

	var size C.int
	buf := C.IupImageSaveToBuffer(ih.ptr(), cFormat, &size)
	if buf == nil {
		return nil
	}
	defer C.free(unsafe.Pointer(buf))

	return C.GoBytes(unsafe.Pointer(buf), size)
}

// NextField shifts the focus to the next element that can have the focus.
// It is relative to the given element and does not depend on the element currently with the focus.
//
// It will search for the next element first in the children, then in the brothers,
// then in the uncles and their children, and so on.
//
// This sequence is not the same sequence used by the Tab key, which is dependent on the native system.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_nextfield.md
func NextField(ih Ihandle) Ihandle {
	return mkih(C.IupNextField(ih.ptr()))
}

// PreviousField shifts the focus to the previous element that can have the focus.
// It is relative to the given element and does not depend on the element currently with the focus.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_previousfield.md
func PreviousField(ih Ihandle) Ihandle {
	return mkih(C.IupPreviousField(ih.ptr()))
}

// GetFocus returns the identifier of the interface element that has the keyboard focus, i.e. the element that will receive keyboard events.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getfocus.md
func GetFocus() Ihandle {
	return mkih(C.IupGetFocus())
}

// SetFocus sets the interface element that will receive the keyboard focus, i.e., the element that will receive keyboard events.
// But this will be processed only after the control actually receive the focus.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_setfocus.md
func SetFocus(ih Ihandle) Ihandle {
	return mkih(C.IupSetFocus(ih.ptr()))
}

// MenuItem creates an item of the menu interface element. When selected, it generates an action.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/elem/iup_menuitem.md
func MenuItem(title string) Ihandle {
	cTitle := C.CString(title)
	defer C.free(unsafe.Pointer(cTitle))

	h := mkih(C.IupMenuItem(cTitle, nil))
	return h
}

// Menu Creates a menu element, which groups 3 types of interface elements: item, submenu and separator.
// Any other interface element defined inside a menu will be an error.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/elem/iup_menu.md
func Menu(children ...Ihandle) Ihandle {
	children = append(children, Ihandle(0))

	h := mkih(C.IupMenuv((**C.Ihandle)(unsafe.Pointer(&(children[0])))))
	return h
}

// MenuSeparator creates the separator interface element. It shows a line between two menu items.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/elem/iup_menuseparator.md
func MenuSeparator() Ihandle {
	h := mkih(C.IupMenuSeparator())
	return h
}

// Submenu creates a menu item that, when selected, opens another menu.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/elem/iup_submenu.md
func Submenu(title string, child Ihandle) Ihandle {
	cTitle := C.CString(title)
	defer C.free(unsafe.Pointer(cTitle))

	h := mkih(C.IupSubmenu(cTitle, child.ptr()))
	return h
}

// SetHandle associates a name with an interface element.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_sethandle.md
func SetHandle(name string, ih Ihandle) Ihandle {
	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	return mkih(C.IupSetHandle(cName, ih.ptr()))
}

// GetHandle returns the identifier of an interface element that has an associated name using SetHandle.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_gethandle.md
func GetHandle(name string) Ihandle {
	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	return mkih(C.IupGetHandle(cName))
}

// GetName returns a name of an interface element, if the element has an associated name using SetHandle.
// Notice that a handle can have many names. GetName will return the last name set.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getname.md
func GetName(ih Ihandle) string {
	return C.GoString(C.IupGetName(ih.ptr()))
}

// GetAllNames returns the names of all interface elements that have an associated name using SetHandle.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getallnames.md
func GetAllNames() (names []string) {
	n := int(C.IupGetAllNames(nil, 0))
	if n > 0 {
		names = make([]string, n)
		pNames := make([]*C.char, n)
		C.IupGetAllNames((**C.char)(unsafe.Pointer(&pNames[0])), C.int(n))
		for i := 0; i < n; i++ {
			names[i] = C.GoString(pNames[i])
		}
	}
	return
}

// GetAllDialogs returns the names of all dialogs that have an associated name using SetHandle.
// Other dialogs will not be returned.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getalldialogs.md
func GetAllDialogs() (names []string) {
	n := int(C.IupGetAllDialogs(nil, 0))
	if n > 0 {
		names = make([]string, n)
		pNames := make([]*C.char, n)
		C.IupGetAllDialogs((**C.char)(unsafe.Pointer(&pNames[0])), C.int(n))
		for i := 0; i < n; i++ {
			names[i] = C.GoString(pNames[i])
		}
	}
	return
}

// SetLanguage sets the language name used by some pre-defined dialogs.
// Can also be changed using the global attribute LANGUAGE.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_setlanguage.md
func SetLanguage(lng string) {
	cLng := cStrOrNull(lng)
	defer cStrFree(cLng)

	C.IupSetLanguage(cLng)
}

// GetLanguage returns the language used by some pre-defined dialogs.
// Returns the same value as the LANGUAGE global attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getlanguage.md
func GetLanguage() string {
	return C.GoString(C.IupGetLanguage())
}

// SetLanguageString associates a name with a string as an auxiliary method for Internationalization of applications.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_setlanguagestring.md
func SetLanguageString(name, str string) {
	cName, cStr := C.CString(name), cStrOrNull(str)
	defer C.free(unsafe.Pointer(cName))
	defer cStrFree(cStr)

	C.IupStoreLanguageString(cName, cStr) //NOTE string always duplicated
}

// GetLanguageString returns a language dependent string.
// The string must have been associated with the name using the SetLanguageString or SetLanguagePack functions.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getlanguagestring.md
func GetLanguageString(name string) string {
	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	return C.GoString(C.IupGetLanguageString(cName))
}

// SetLanguagePack sets a pack of associations between names and string values.
// It is simply a User element with several attributes set.
// Internally will call SetLanguageString for each name in the pack.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_setlanguagepack.md
func SetLanguagePack(ih Ihandle) {
	C.IupSetLanguagePack(ih.ptr())
}

// Clipboard creates an element that allows access to the clipboard.
// Each clipboard should be destroyed using Destroy,
// but you can use only one for the entire application because it does not store any data inside.
// Or you can simply create and destroy every time you need to copy or paste.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/elem/iup_clipboard.md
func Clipboard() Ihandle {
	h := mkih(C.IupClipboard())
	return h
}

// Timer creates a timer which periodically invokes a callback when the time is up.
// Each timer should be destroyed using Destroy.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/elem/iup_timer.md
func Timer() Ihandle {
	h := mkih(C.IupTimer())
	return h
}

// Thread creates a thread element in IUP, which is not associated to any interface element.
// It is a very simple support to create and manage threads in a multithreading environment.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/elem/iup_thread.md
func Thread() Ihandle {
	h := mkih(C.IupThread())
	return h
}

// Tray creates a system tray icon element in IUP, which is not associated with any interface element.
// It allows placing an icon in the system notification area (system tray).
func Tray() Ihandle {
	h := mkih(C.IupTray())
	return h
}

// Notify creates a notification control for displaying desktop notifications.
//
// Desktop notifications appear in the system's notification area/center
// and can include a title, body text, icon, and action buttons.
func Notify() Ihandle {
	h := mkih(C.IupNotify())
	return h
}

// User creates a user element in IUP, which is not associated to any interface element.
// It is used to map an external element to a IUP element.
// Its use is usually for additional elements, but you can use it to create an Ihandle to store private attributes.
//
// It is also a void container. Its children can be dynamically added using Append or Insert.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/elem/iup_user.md
func User() Ihandle {
	h := mkih(C.IupUser())
	return h
}

// Execute runs the executable with the given parameters.
// It is a non-synchronous operation, i.e., the function will return just after execute the command, and it will not wait for its result.
// In Windows, there is no need to add the ".exe" file extension.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_execute.md
func Execute(fileName, parameters string) int {
	cFileName := C.CString(fileName)
	cParameters := cStrOrNull(parameters)
	defer C.free(unsafe.Pointer(cFileName))
	defer cStrFree(cParameters)

	return int(C.IupExecute(cFileName, cParameters))
}

// ExecuteWait runs the executable with the given parameters.
// It is a synchronous operation, i.e., the function will wait the command to terminate before it returns.
// In Windows, there is no need to add the ".exe" file extension.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_executewait.md
func ExecuteWait(fileName, parameters string) int {
	cFileName := C.CString(fileName)
	cParameters := cStrOrNull(parameters)
	defer C.free(unsafe.Pointer(cFileName))
	defer cStrFree(cParameters)

	return int(C.IupExecuteWait(cFileName, cParameters))
}

// Help opens the given URL. Executes "xdg-open" (Linux/Unix) or "open" (macOS) passing the desired URL as a parameter.
// In Windows executes the shell "open" operation on the given URL.
// In UNIX, you can change the used browser setting the environment variable IUP_HELPAPP or using the global attribute "HELPAPP".
// It is a non-synchronous operation, i.e., the function will return just after execute the command, and it will not wait for its result.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_help.md
func Help(url string) int {
	cUrl := C.CString(url)
	defer C.free(unsafe.Pointer(cUrl))

	return int(C.IupHelp(cUrl))
}

// Log writes a message to the system log.
// In Windows, write to the Application event log. On Linux, write to the Syslog.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_log.md
func Log(_type, str string) {
	cType, cStr := C.CString(_type), C.CString(str)
	defer C.free(unsafe.Pointer(cType))
	defer C.free(unsafe.Pointer(cStr))

	C.Log(cType, cStr)
}
