package iup

import (
	"bytes"
	"fmt"
	"image/color"
	"reflect"
	"runtime"
	"strings"
	"sync"
	"sync/atomic"
	"unsafe"
)

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

var attrMu reentrantRWMutex

type reentrantRWMutex struct {
	mu      sync.RWMutex
	owner   atomic.Int64
	recurse int32
}

func goroutineID() int64 {
	var buf [64]byte
	n := runtime.Stack(buf[:], false)
	var id int64
	for i := len("goroutine "); i < n; i++ {
		if buf[i] < '0' || buf[i] > '9' {
			break
		}
		id = id*10 + int64(buf[i]-'0')
	}
	return id
}

func (m *reentrantRWMutex) Lock() {
	gid := goroutineID()
	if m.owner.Load() == gid {
		m.recurse++
		return
	}
	m.mu.Lock()
	m.owner.Store(gid)
	m.recurse = 1
}

func (m *reentrantRWMutex) Unlock() {
	m.recurse--
	if m.recurse == 0 {
		m.owner.Store(0)
		m.mu.Unlock()
	}
}

func (m *reentrantRWMutex) RLock() {
	gid := goroutineID()
	if m.owner.Load() == gid {
		m.recurse++
		return
	}
	m.mu.RLock()
}

func (m *reentrantRWMutex) RUnlock() {
	gid := goroutineID()
	if m.owner.Load() == gid {
		m.recurse--
		if m.recurse == 0 {
			m.owner.Store(0)
			m.mu.Unlock()
		}
		return
	}
	m.mu.RUnlock()
}

// SetAttribute sets an interface element attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_setattribute.md
func SetAttribute(ih Ihandle, name string, value interface{}) {
	attrMu.Lock()
	defer attrMu.Unlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	switch val := value.(type) {
	case nil:
		C.IupSetAttribute(ih.ptr(), cName, nil)
	case Ihandle:
		C.IupSetAttribute(ih.ptr(), cName, cih(val))
	case uintptr:
		C.IupSetAttribute(ih.ptr(), cName, cih(value.(Ihandle)))
	case string:
		cValue := cStrOrNull(val)
		defer cStrFree(cValue)
		C.IupSetStrAttribute(ih.ptr(), cName, cValue)
	case bool:
		s := "NO"
		if val {
			s = "YES"
		}
		cValue := C.CString(s)
		defer C.free(unsafe.Pointer(cValue))
		C.IupSetStrAttribute(ih.ptr(), cName, cValue)
	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
		C.IupSetInt(ih.ptr(), cName, C.int(reflect.ValueOf(value).Int()))
	case float32:
		C.IupSetFloat(ih.ptr(), cName, C.float(val))
	case float64:
		C.IupSetDouble(ih.ptr(), cName, C.double(val))
	case [3]uint8:
		C.IupSetRGB(ih.ptr(), cName, C.uchar(val[0]), C.uchar(val[1]), C.uchar(val[2]))
	case [4]uint8:
		C.IupSetRGBA(ih.ptr(), cName, C.uchar(val[0]), C.uchar(val[1]), C.uchar(val[2]), C.uchar(val[3]))
	case color.RGBA:
		C.IupSetRGBA(ih.ptr(), cName, C.uchar(val.R), C.uchar(val.G), C.uchar(val.B), C.uchar(val.A))
	case color.NRGBA:
		C.IupSetRGBA(ih.ptr(), cName, C.uchar(val.R), C.uchar(val.G), C.uchar(val.B), C.uchar(val.A))
	default:
		panic("bad argument passed to SetAttribute")
	}
}

// SetAttributes sets several attributes of an interface element.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_setattributes.md
func SetAttributes(ih Ihandle, str string) Ihandle {
	attrMu.Lock()
	defer attrMu.Unlock()

	cStr := C.CString(str)
	defer C.free(unsafe.Pointer(cStr))

	return mkih(C.IupSetAttributes(ih.ptr(), cStr))
}

// ResetAttribute removes an attribute from the hash table of the element, and its children if the attribute is inheritable.
// It is useful to reset the state of inheritable attributes in a tree of elements.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_resetattribute.md
func ResetAttribute(ih Ihandle, name string) {
	attrMu.Lock()
	defer attrMu.Unlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	C.IupResetAttribute(ih.ptr(), cName)
}

// SetAtt sets several attributes of an interface element and optionally sets its name.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_setatt.md
func SetAtt(ih Ihandle, handle_name string, args ...string) Ihandle {
	attrs := bytes.NewBufferString("")
	for i := 0; i < len(args); i += 2 {
		if i > 0 {
			attrs.WriteString(",")
		}
		attrs.WriteString(fmt.Sprintf("%s=\"%s\"", args[i], args[i+1]))
	}

	SetAttributes(ih, attrs.String())

	return ih
}

// SetAttrs method does not exist in C Iup. It has been provided as a convenience function to allow code such as:
//
//     box := iup.Hbox(button1, button2).SetAttrs("GAP", "5", "MARGIN", "8x8")
//
// C Iup provides SetAtt for this purpose but in Go Iup SetAttrs is an easier method to
// accomplish this task due to no necessity of handle_name.
func SetAttrs(ih Ihandle, args ...string) Ihandle {
	return SetAtt(ih, "", args...)
}

// SetAttributeHandle instead of using SetHandle and SetAttribute with a new creative name,
// this function automatically creates a non conflict name and associates the name with the attribute.
//
// It is very useful for associating images and menus.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_setattributehandle.md
func SetAttributeHandle(ih Ihandle, name string, ihNamed Ihandle) {
	attrMu.Lock()
	defer attrMu.Unlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	C.IupSetAttributeHandle(ih.ptr(), cName, ihNamed.ptr())
}

// GetAttribute returns the name of an interface element attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getattribute.md
func GetAttribute(ih Ihandle, name string) string {
	attrMu.RLock()
	defer attrMu.RUnlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	return C.GoString(C.IupGetAttribute(ih.ptr(), cName))
}

// GetAllAttributes returns the names of all attributes of an element that are set in its internal hash table only.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getallattributes.md
func GetAllAttributes(ih Ihandle) (ret []string) {
	attrMu.RLock()
	defer attrMu.RUnlock()

	n := int(C.IupGetAllAttributes(ih.ptr(), nil, 0))
	if n > 0 {
		ret = make([]string, n)
		pRets := make([]*C.char, n)
		C.IupGetAllAttributes(ih.ptr(), (**C.char)(unsafe.Pointer(&pRets[0])), C.int(n))
		for i := 0; i < n; i++ {
			ret[i] = C.GoString(pRets[i])
		}
	}
	return
}

// GetAttributes returns all attributes of a given element that are set in the internal hash table.
// The known attributes that are pointers (not strings) are returned as integers.
//
// The internal attributes are not returned (attributes prefixed with "_IUP").
//
// Before calling this function the application must ensure that there is no pointer attributes
// set for that element, although all known pointers attributes are handled.
//
// This function should be avoided. Use iup.GetAllAttributes instead.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getattributes.md
func GetAttributes(ih Ihandle) string {
	attrMu.RLock()
	defer attrMu.RUnlock()

	return C.GoString(C.IupGetAttributes(ih.ptr()))
}

// GetAttributeHandle instead of using GetAttribute and GetHandle, this function directly returns the associated handle.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getattributehandle.md
func GetAttributeHandle(ih Ihandle, name string) Ihandle {
	attrMu.RLock()
	defer attrMu.RUnlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

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

// SetAttributeHandleId sets an attribute handle with an id.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_setattributehandle.md
func SetAttributeHandleId(ih Ihandle, name string, id int, ihNamed Ihandle) {
	attrMu.Lock()
	defer attrMu.Unlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	C.IupSetAttributeHandleId(ih.ptr(), cName, C.int(id), ihNamed.ptr())
}

// GetAttributeHandleId returns the handle attribute with an id.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getattributehandle.md
func GetAttributeHandleId(ih Ihandle, name string, id int) Ihandle {
	attrMu.RLock()
	defer attrMu.RUnlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	return mkih(C.IupGetAttributeHandleId(ih.ptr(), cName, C.int(id)))
}

// SetAttributeHandleId2 sets an attribute handle with lin and col.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_setattributehandle.md
func SetAttributeHandleId2(ih Ihandle, name string, lin, col int, ihNamed Ihandle) {
	attrMu.Lock()
	defer attrMu.Unlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	C.IupSetAttributeHandleId2(ih.ptr(), cName, C.int(lin), C.int(col), ihNamed.ptr())
}

// GetAttributeHandleId2 returns the handle attribute with lin and col.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getattributehandle.md
func GetAttributeHandleId2(ih Ihandle, name string, lin, col int) Ihandle {
	attrMu.RLock()
	defer attrMu.RUnlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	return mkih(C.IupGetAttributeHandleId2(ih.ptr(), cName, C.int(lin), C.int(col)))
}

// SetAttributeId sets an interface element attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_setattribute.md
func SetAttributeId(ih Ihandle, name string, id int, value interface{}) {
	attrMu.Lock()
	defer attrMu.Unlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	switch val := value.(type) {
	case nil:
		C.IupSetAttributeId(ih.ptr(), cName, C.int(id), nil)
	case Ihandle:
		C.IupSetAttributeId(ih.ptr(), cName, C.int(id), cih(value.(Ihandle)))
	case uintptr:
		C.IupSetAttributeId(ih.ptr(), cName, C.int(id), cih(value.(Ihandle)))
	case string:
		cValue := cStrOrNull(val)
		defer cStrFree(cValue)
		C.IupSetStrAttributeId(ih.ptr(), cName, C.int(id), cValue)
	case bool:
		s := "NO"
		if val {
			s = "YES"
		}
		cValue := C.CString(s)
		defer C.free(unsafe.Pointer(cValue))
		C.IupSetStrAttributeId(ih.ptr(), cName, C.int(id), cValue)
	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
		C.IupSetIntId(ih.ptr(), cName, C.int(id), C.int(reflect.ValueOf(value).Int()))
	case float32:
		C.IupSetFloatId(ih.ptr(), cName, C.int(id), C.float(val))
	case float64:
		C.IupSetDoubleId(ih.ptr(), cName, C.int(id), C.double(val))
	case [3]uint8:
		C.IupSetRGBId(ih.ptr(), cName, C.int(id), C.uchar(val[0]), C.uchar(val[1]), C.uchar(val[2]))
	default:
		panic("bad argument passed to SetAttributeId")
	}
}

// GetAttributeId returns the name of an interface element attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getattribute.md
func GetAttributeId(ih Ihandle, name string, id int) string {
	attrMu.RLock()
	defer attrMu.RUnlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	return C.GoString(C.IupGetAttributeId(ih.ptr(), cName, C.int(id)))
}

// SetAttributeId2 sets an interface element attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_setattribute.md
func SetAttributeId2(ih Ihandle, name string, lin, col int, value interface{}) {
	attrMu.Lock()
	defer attrMu.Unlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	switch val := value.(type) {
	case nil:
		C.IupSetAttributeId2(ih.ptr(), cName, C.int(lin), C.int(col), nil)
	case Ihandle:
		C.IupSetAttributeId2(ih.ptr(), cName, C.int(lin), C.int(col), cih(value.(Ihandle)))
	case uintptr:
		C.IupSetAttributeId2(ih.ptr(), cName, C.int(lin), C.int(col), cih(value.(Ihandle)))
	case string:
		cValue := cStrOrNull(val)
		defer cStrFree(cValue)
		C.IupSetStrAttributeId2(ih.ptr(), cName, C.int(lin), C.int(col), cValue)
	case bool:
		s := "NO"
		if val {
			s = "YES"
		}
		cValue := C.CString(s)
		defer C.free(unsafe.Pointer(cValue))
		C.IupSetStrAttributeId2(ih.ptr(), cName, C.int(lin), C.int(col), cValue)
	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
		C.IupSetIntId2(ih.ptr(), cName, C.int(lin), C.int(col), C.int(reflect.ValueOf(value).Int()))
	case float32:
		C.IupSetFloatId2(ih.ptr(), cName, C.int(lin), C.int(col), C.float(val))
	case float64:
		C.IupSetDoubleId2(ih.ptr(), cName, C.int(lin), C.int(col), C.double(val))
	case [3]uint8:
		C.IupSetRGBId2(ih.ptr(), cName, C.int(lin), C.int(col), C.uchar(val[0]), C.uchar(val[1]), C.uchar(val[2]))
	default:
		panic("bad argument passed to SetAttributeId2")
	}
}

// SetRGBId2 sets an interface element attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_setattribute.md
func SetRGBId2(ih Ihandle, name string, lin, col int, r, g, b uint8) {
	attrMu.Lock()
	defer attrMu.Unlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	C.IupSetRGBId2(ih.ptr(), cName, C.int(lin), C.int(col), C.uchar(r), C.uchar(g), C.uchar(b))
}

// GetAttributeId2 returns the name of an interface element attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getattribute.md
func GetAttributeId2(ih Ihandle, name string, lin, col int) string {
	attrMu.RLock()
	defer attrMu.RUnlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	return C.GoString(C.IupGetAttributeId2(ih.ptr(), cName, C.int(lin), C.int(col)))
}

// SetGlobal sets an attribute in the global environment.
// If the driver process the attribute then it will not be stored internally.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_setglobal.md
func SetGlobal(name string, value interface{}) {
	attrMu.Lock()
	defer attrMu.Unlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	switch val := value.(type) {
	case nil:
		C.IupSetStrGlobal(cName, nil)
	case string:
		cValue := cStrOrNull(val)
		defer cStrFree(cValue)
		C.IupSetStrGlobal(cName, cValue)
	case Ihandle:
		C.IupSetGlobal(cName, cih(val))
	case uintptr:
		C.IupSetGlobal(cName, cih(Ihandle(val)))
	default:
		panic("bad argument passed to SetGlobal")
	}
}

// GetGlobal returns an attribute value from the global environment.
// The value can be returned from the driver or from the internal storage.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getglobal.md
func GetGlobal(name string) string {
	attrMu.RLock()
	defer attrMu.RUnlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

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

// GetGlobalPtr returns an attribute value from the global environment.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getglobal.md
func GetGlobalPtr(name string) uintptr {
	attrMu.RLock()
	defer attrMu.RUnlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	return uintptr(unsafe.Pointer(C.IupGetGlobal(cName)))
}

// GetGlobalIh returns an attribute value from the global environment.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getglobal.md
func GetGlobalIh(name string) Ihandle {
	attrMu.RLock()
	defer attrMu.RUnlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	return Ihandle(unsafe.Pointer(C.IupGetGlobal(cName)))

}

// StringCompare is an utility function to compare strings lexicographically.
// Used internally in MatrixEx when sorting, but available in the main library.
//
// This means that numbers and text in the string are sorted separately (for ex: A1 A2 A11 A30 B1).
// Also natural alphabetic order is used: 123...aAáÁ...bBcC...
// The comparison will work only for Latin-1 characters, even if UTF8MODE is Yes.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_stringcompare.md
func StringCompare(str1, str2 string, caseSensitive, lexicographic bool) int {
	cStr1, cStr2 := C.CString(str1), C.CString(str2)
	defer C.free(unsafe.Pointer(cStr1))
	defer C.free(unsafe.Pointer(cStr2))

	return int(C.IupStringCompare(cStr1, cStr2, C.int(bool2int(caseSensitive)), C.int(bool2int(lexicographic))))
}

// SetRGB sets an interface element attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_setattribute.md
func SetRGB(ih Ihandle, name string, r, g, b uint8) {
	attrMu.Lock()
	defer attrMu.Unlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	C.IupSetRGB(ih.ptr(), cName, C.uchar(r), C.uchar(g), C.uchar(b))
}

// SetRGBA sets an interface element attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_setattribute.md
func SetRGBA(ih Ihandle, name string, r, g, b, a uint8) {
	attrMu.Lock()
	defer attrMu.Unlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	C.IupSetRGBA(ih.ptr(), cName, C.uchar(r), C.uchar(g), C.uchar(b), C.uchar(a))
}

// SetRGBId sets an interface element attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_setattribute.md
func SetRGBId(ih Ihandle, name string, id int, r, g, b uint8) {
	attrMu.Lock()
	defer attrMu.Unlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	C.IupSetRGBId(ih.ptr(), cName, C.int(id), C.uchar(r), C.uchar(g), C.uchar(b))
}

// GetInt returns the name of an interface element attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getattribute.md
func GetInt(ih Ihandle, name string) int {
	attrMu.RLock()
	defer attrMu.RUnlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	return int(C.IupGetInt(ih.ptr(), cName))
}

// GetInt2 returns the name of an interface element attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getattribute.md
func GetInt2(ih Ihandle, name string) (count, i1, i2 int) { // count = 0, 1 or 2
	attrMu.RLock()
	defer attrMu.RUnlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	count = int(C.IupGetIntInt(ih.ptr(), cName, (*C.int)(unsafe.Pointer(&i1)), (*C.int)(unsafe.Pointer(&i2))))
	return
}

// GetBool returns a boolean attribute value.
// Returns true for "YES", "ON", "TRUE", "1" (case insensitive), false otherwise.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getattribute.md
func GetBool(ih Ihandle, name string) bool {
	val := strings.ToUpper(GetAttribute(ih, name))
	return val == "YES" || val == "ON" || val == "TRUE" || val == "1"
}

// SetBool sets a boolean attribute value. Writes "YES" or "NO".
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_setattribute.md
func SetBool(ih Ihandle, name string, value bool) {
	SetAttribute(ih, name, value)
}

// SetBoolId sets a boolean attribute value for an id.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_setattribute.md
func SetBoolId(ih Ihandle, name string, id int, value bool) {
	SetAttributeId(ih, name, id, value)
}

// SetBoolId2 sets a boolean attribute value for a (lin, col) position.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_setattribute.md
func SetBoolId2(ih Ihandle, name string, lin, col int, value bool) {
	SetAttributeId2(ih, name, lin, col, value)
}

// GetFloat returns the name of an interface element attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getattribute.md
func GetFloat(ih Ihandle, name string) float32 {
	attrMu.RLock()
	defer attrMu.RUnlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	return float32(C.IupGetFloat(ih.ptr(), cName))
}

// GetDouble returns the name of an interface element attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getattribute.md
func GetDouble(ih Ihandle, name string) float64 {
	attrMu.RLock()
	defer attrMu.RUnlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	return float64(C.IupGetDouble(ih.ptr(), cName))
}

// GetRGB returns the name of an interface element attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getattribute.md
func GetRGB(ih Ihandle, name string) (r, g, b uint8) {
	attrMu.RLock()
	defer attrMu.RUnlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	C.IupGetRGB(ih.ptr(), cName, (*C.uchar)(unsafe.Pointer(&r)), (*C.uchar)(unsafe.Pointer(&g)), (*C.uchar)(unsafe.Pointer(&b)))
	return
}

// GetRGBA returns the name of an interface element attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getattribute.md
func GetRGBA(ih Ihandle, name string) (r, g, b, a uint8) {
	attrMu.RLock()
	defer attrMu.RUnlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	C.IupGetRGBA(ih.ptr(), cName, (*C.uchar)(unsafe.Pointer(&r)), (*C.uchar)(unsafe.Pointer(&g)), (*C.uchar)(unsafe.Pointer(&b)), (*C.uchar)(unsafe.Pointer(&a)))
	return
}

// GetIntId returns the name of an interface element attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getattribute.md
func GetIntId(ih Ihandle, name string, id int) int {
	attrMu.RLock()
	defer attrMu.RUnlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	return int(C.IupGetIntId(ih.ptr(), cName, C.int(id)))
}

// GetFloatId returns the name of an interface element attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getattribute.md
func GetFloatId(ih Ihandle, name string, id int) float32 {
	attrMu.RLock()
	defer attrMu.RUnlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	return float32(C.IupGetFloatId(ih.ptr(), cName, C.int(id)))
}

// GetDoubleId returns the name of an interface element attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getattribute.md
func GetDoubleId(ih Ihandle, name string, id int) float64 {
	attrMu.RLock()
	defer attrMu.RUnlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	return float64(C.IupGetDoubleId(ih.ptr(), cName, C.int(id)))
}

// GetRGBId returns the name of an interface element attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getattribute.md
func GetRGBId(ih Ihandle, name string, id int) (r, g, b uint8) {
	attrMu.RLock()
	defer attrMu.RUnlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	C.IupGetRGBId(ih.ptr(), cName, C.int(id), (*C.uchar)(unsafe.Pointer(&r)), (*C.uchar)(unsafe.Pointer(&g)), (*C.uchar)(unsafe.Pointer(&b)))
	return
}

// GetBoolId returns a boolean attribute value with an id.
// Returns true for "YES", "ON", "TRUE", "1" (case insensitive), false otherwise.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getattribute.md
func GetBoolId(ih Ihandle, name string, id int) bool {
	val := strings.ToUpper(GetAttributeId(ih, name, id))
	return val == "YES" || val == "ON" || val == "TRUE" || val == "1"
}

// GetIntId2 returns the name of an interface element attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getattribute.md
func GetIntId2(ih Ihandle, name string, lin, col int) int {
	attrMu.RLock()
	defer attrMu.RUnlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	return int(C.IupGetIntId2(ih.ptr(), cName, C.int(lin), C.int(col)))
}

// GetFloatId2 returns the name of an interface element attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getattribute.md
func GetFloatId2(ih Ihandle, name string, lin, col int) float32 {
	attrMu.RLock()
	defer attrMu.RUnlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	return float32(C.IupGetFloatId2(ih.ptr(), cName, C.int(lin), C.int(col)))
}

// GetDoubleId2 returns the name of an interface element attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getattribute.md
func GetDoubleId2(ih Ihandle, name string, lin, col int) float64 {
	attrMu.RLock()
	defer attrMu.RUnlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	return float64(C.IupGetDoubleId2(ih.ptr(), cName, C.int(lin), C.int(col)))
}

// GetRGBId2 returns the name of an interface element attribute.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getattribute.md
func GetRGBId2(ih Ihandle, name string, lin, col int) (r, g, b uint8) {
	attrMu.RLock()
	defer attrMu.RUnlock()

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	C.IupGetRGBId2(ih.ptr(), cName, C.int(lin), C.int(col), (*C.uchar)(unsafe.Pointer(&r)), (*C.uchar)(unsafe.Pointer(&g)), (*C.uchar)(unsafe.Pointer(&b)))
	return
}

// GetBoolId2 returns a boolean attribute value with lin and col.
// Returns true for "YES", "ON", "TRUE", "1" (case insensitive), false otherwise.
//
// https://github.com/gen2brain/iup-go/blob/main/docs/func/iup_getattribute.md
func GetBoolId2(ih Ihandle, name string, lin, col int) bool {
	val := strings.ToUpper(GetAttributeId2(ih, name, lin, col))
	return val == "YES" || val == "ON" || val == "TRUE" || val == "1"
}
