/* package input is a thing Go wrapper around the Linux kernel input event 
 * system. */
package input

import "gitlab.com/beoran/galago/os/linux"
import "strings"
import "os"
import "unsafe"
import syscall "golang.org/x/sys/unix"
import "fmt"
import "path/filepath"
import "time"
import "sync"
import "runtime"


// Device models an input device
type Device struct {
    FileName string 
    *os.File
    // Cached device information
    Info struct { 
        Events          []SupportedEvent
        Keys            []SupportedKey
        Axes            []AbsoluteAxis
        Rolls           []RelativeAxis
        Name            string
        ID              string
    }
}

const Directory = "/dev/input"

func (d * Device) KeepAlive() {
    runtime.KeepAlive(d.File)
}

func (d * Device) String() string {
    return fmt.Sprintf("%s: %s (%s):%v,%v,%v,%v", 
                        d.FileName, d.Info.Name, d.Info.ID,
                        d.Info.Events, d.Info.Axes, d.Info.Keys, 
                        d.Info.Rolls)
}

func Open(name string) (*Device, error) {
    f, err := os.OpenFile(filepath.Join(Directory, name), 
        syscall.O_ASYNC|syscall.O_NONBLOCK|os.O_RDWR, 0666)
    if err != nil {
        return nil, err
    }
    if err = syscall.SetNonblock(int(f.Fd()), true); err != nil {
        return nil, err
    }
    flags, err := syscall.FcntlInt(f.Fd(), syscall.F_GETFL, 0);
    if flags, err = syscall.FcntlInt(f.Fd(), syscall.F_SETFL, flags | syscall.O_NONBLOCK); err != nil {
        return nil, err
    }
    
    return &Device{FileName: name, File: f}, nil
}

func (d *Device) GatherInfo() error {
    var err error
    if d.Info.Events, err = d.SupportedEvents(); err != nil { 
        return err 
    }
    if d.Info.Keys, err = d.SupportedKeys(); err != nil { 
        return err 
    }
    if d.Info.Axes, err = d.SupportedAxes(); err != nil { 
        return err 
    }
    if d.Info.Rolls, err = d.SupportedRelatives(); err != nil { 
        return err 
    }
    if id, err := d.Id(); err != nil { 
        return err 
    } else {
        d.Info.ID = fmt.Sprintf("%v", id)
    }
    if d.Info.Name, err = d.Name(); err != nil { 
        return err 
    }
    return nil
}

func OpenAndGatherInfo(name string) (*Device, error) {
    dev, err := Open(name)
    if err != nil {
        return dev, err
    }
    err = dev.GatherInfo()
    return dev, err
} 

func List() ([]string, error) {
    dir, err := os.Open(Directory)
    if err != nil {
        return nil, err
    }
    
    names, err := dir.Readdirnames(-1)
    if err != nil {
        return names, err
    }
    results := names[:0]
    for _, name := range names {
        if strings.HasPrefix(name, "event") {
            results = append(results, name)
        }
    }
    return results, nil
}


// Icotl performs an ioctl on the given device
func (d * Device) Ioctl(code uint32, pointer unsafe.Pointer) error {
    fmt.Printf("ioctl: %d %d %d\n", uintptr(d.Fd()), uintptr(code), uintptr(pointer))
    _, _, errno := syscall.Syscall(
        syscall.SYS_IOCTL,
        uintptr(d.Fd()),
        uintptr(code),
        uintptr(pointer))
    if (errno != 0) {
        return errno
    }
    d.KeepAlive()
    return nil
}


func (d * Device) DriverVersion() (int32, error) {
    res := int32(0) 
    data := unsafe.Pointer(&res)
    err := d.Ioctl(linux.EVIOCGVERSION, data)
    return res, err
}

const NAME_MAX = 256

func (d * Device) Name() (string, error) {
    buffer := [NAME_MAX]byte{}
    err := d.Ioctl(linux.EVIOCGNAME(uintptr(len(buffer))), unsafe.Pointer(&buffer))
    return string(buffer[0:len(buffer)]), err
}


func (d * Device) Id() (linux.INPUT_id, error) {
    var result linux.INPUT_id 
    err := d.Ioctl(linux.EVIOCGID, unsafe.Pointer(&result))
    return result, err
}

// The Linux developers thought it was a great idea a to use an array of 
// unsigned longs for the bit flags of input devices. Of course, the length 
// of an unsigned long is platform dependent which then entails all sorts of
// gymnastics to extract the bits from the array.
// To avoid this, we use a byte array instead, which might lessen performance,
// but is much easier to use.

const SIZEOF_LONG = uint(unsafe.Sizeof(*((*linux.UnsignedLong)(nil))))      
const BITS_PER_LONG = SIZEOF_LONG * 8

func BitsToLong(bits uint) uint {
    return ((bits) + (8 * SIZEOF_LONG) - 1) / (8 * SIZEOF_LONG)
}

const TOPOLOGY_MAX = 256

func (d * Device) Topology() (string, error) {
    buffer := [TOPOLOGY_MAX]byte{}
    err := d.Ioctl(linux.EVIOCGPHYS(uintptr(len(buffer))), unsafe.Pointer(&buffer))
    return string(buffer[0:len(buffer)]), err
}

func TestBit(array []uint8, bit uint) bool {
    elem := uint(array[ bit / 8 ])
    flag := uint(1) << uint(bit % 8)
    return (elem & flag ) != 0
}

type SupportedEvent uint 

func (se SupportedEvent) Name() string {
    return linux.EvToString(uint(se))
}

func (se SupportedEvent) String() string {
    return se.Name()
}

func (d * Device) SupportedEvents() ([]SupportedEvent, error) {
    var bits [linux.EV_MAX / 8 + 1]uint8
    size := unsafe.Sizeof(bits)
	err := d.Ioctl(linux.EVIOCGBIT(0, uintptr(size)), unsafe.Pointer(&bits));
    if err != nil {
        return nil, err
    }
    fmt.Printf("size %d, bits: %v\n", size, bits)
    result := []SupportedEvent{}
    for i := uint(0); i < uint(linux.EV_MAX); i++ {
        if (TestBit(bits[0:len(bits)],i)) {
            result = append(result, SupportedEvent(uint(i)))
		}
    }
    
    return result, nil
}

// More Go-like names for the low leve kernel api structs


type InputAbsinfo linux.INPUT_absinfo
type InputEvent linux.INPUT_event

type AbsoluteAxis struct {
    Index uint
    InputAbsinfo
}


func (ax AbsoluteAxis) Name() string {
    return linux.AbsToString(ax.Index)
}

func (ax AbsoluteAxis) String() string {
    return fmt.Sprintf("%s %d (min:%d max:%d flat:%d fuzz:%d)",
                ax.Name(),
                ax.Value,
                ax.Minimum,
                ax.Maximum,
                ax.Flat,
                ax.Fuzz)
}

func (ev InputEvent) Time() time.Time {
    return time.Unix(ev.Timeval.Tv_sec, ev.Timeval.Tv_usec)
}


func (ev InputEvent) String() string {
    return fmt.Sprintf("Event: Time: %s Type: %s, Code: %d, Value:%d",
        ev.Time().Format("2006-01-02 15:04:05.000000"), 
        linux.EvToString(uint(ev.Type)),
        ev.Code,
        ev.Value)
}


// Read performs a read syscall on the given device
func (d * Device) Read(pointer unsafe.Pointer, size uintptr) (uintptr, error) {
    fmt.Printf("read: %d %d %d\n", uintptr(d.Fd()), uintptr(pointer), uintptr(size))
    read, _, errno := syscall.Syscall(
        syscall.SYS_READ,
        uintptr(d.Fd()),
        uintptr(pointer),
        uintptr(size))
    d.KeepAlive()

    if (errno != 0) {
        return read, errno
    }
    return read, nil
}

// Write performs a write syscall on the given device
func (d * Device) Write(pointer unsafe.Pointer, size uintptr) (uintptr, error) {
    fmt.Printf("write: %d %d %d\n", uintptr(d.Fd()), uintptr(pointer), uintptr(size))
    wrote, _, errno := syscall.Syscall(
        syscall.SYS_WRITE,
        uintptr(d.Fd()),
        uintptr(pointer),
        uintptr(size))
    d.KeepAlive()

    if (errno != 0) {
        return wrote, errno
    }
    return wrote, nil
}


const READ_EVENTS_MAX = 64

func (d * Device) ReadEvents() ([]InputEvent, error) {
    var events [READ_EVENTS_MAX]InputEvent
    size := unsafe.Sizeof(events)
    read, err := d.Read(unsafe.Pointer(&events), size) 
    if err != nil {
        return nil, err
    } 
    len := read / unsafe.Sizeof(events[0])
    return events[0:len], nil
}

const WRITE_EVENTS_MAX = 64

func (d * Device) WriteEvents(events []InputEvent) (uint, error) {
    size := unsafe.Sizeof(events[0]) * uintptr(len(events)) 
    wrote, err := d.Write(unsafe.Pointer(&events[0]), size) 
    len := uint(wrote / size) 
    return len, err
}


func (d * Device) SupportedAxes() ([]AbsoluteAxis, error) { 
    var abs_feat InputAbsinfo;
    var abs_b [linux.ABS_MAX/8 + 1]uint8;
    size := unsafe.Sizeof(abs_b)
    err := d.Ioctl(linux.EVIOCGBIT(linux.EV_ABS, size), unsafe.Pointer(&abs_b))
    if err != nil { 
        return nil, err
    }
    result := []AbsoluteAxis{}
    fmt.Printf("Supported Absolute axes:\n");
    for yalv := uint(0); yalv < linux.ABS_MAX; yalv++ {
        if TestBit(abs_b[0:len(abs_b)], uint(yalv)) {
            fmt.Printf("  Absolute axis 0x%02x %s", yalv, linux.AbsToString(yalv))
            err = d.Ioctl(linux.EVIOCGABS(uint32(yalv)), unsafe.Pointer(&abs_feat))
            if err != nil {
                return result, err
            }
            axis := AbsoluteAxis{Index: yalv, InputAbsinfo: abs_feat}
            result = append(result, axis) 
        }
    }
    return result, nil
}


type SupportedKey uint

func (se SupportedKey) Name() string {
    return linux.KeyString(uint(se))
}

func (se SupportedKey) String() string {
    return se.Name()
}

func (d * Device) SupportedKeys() ([]SupportedKey, error) { 
    var bits [linux.KEY_MAX / 8 + 1]uint8
    size := unsafe.Sizeof(bits)
	err := d.Ioctl(linux.EVIOCGBIT(linux.EV_KEY, uintptr(size)), unsafe.Pointer(&bits));
    if err != nil {
        return nil, err
    }
    fmt.Printf("size %d, bits: %v\n", size, bits)
    result := []SupportedKey{}
    for i := uint(0); i < uint(linux.KEY_MAX); i++ {
        if (TestBit(bits[0:len(bits)],i)) {
            result = append(result, SupportedKey(uint(i)))
		}
    }
    return result, nil
}

type RelativeAxis uint

func (se RelativeAxis) Name() string {
    return linux.RelString(uint(se))
}

func (se RelativeAxis) String() string {
    return se.Name()
}

func (d * Device) SupportedRelatives() ([]RelativeAxis, error) { 
    var bits [linux.REL_MAX / 8 + 1]uint8
    size := unsafe.Sizeof(bits)
	err := d.Ioctl(linux.EVIOCGBIT(linux.EV_REL, uintptr(size)), unsafe.Pointer(&bits));
    if err != nil {
        return nil, err
    }
    fmt.Printf("size %d, bits: %v\n", size, bits)
    result := []RelativeAxis{}
    for i := uint(0); i < uint(linux.REL_MAX); i++ {
        if (TestBit(bits[0:len(bits)],i)) {
            result = append(result, RelativeAxis(uint(i)))
		}
    }
    return result, nil
}




// MANAGER_DEVICE_SCAN_DELAY is the delay between device scans for the device manager.  
const MANAGER_DEVICE_SCAN_DELAY = 3 * time.Second

type Manager struct {
    sync.Mutex
    Connect chan(*Device) 
    Disconnect chan(*Device)
    Errors chan(error)
    Events chan(InputEvent) 
    Done bool
    Devices []*Device
    DevicesByName map[string] *Device
    DevicesByFd map[int] *Device
    Verbose bool
    fds []syscall.PollFd
}


func NewManager(connect chan(*Device), disconnect chan(*Device), event chan(InputEvent), errors chan(error), done chan (struct{})) * Manager {
    manager := &Manager{Connect: connect, Disconnect: disconnect, Errors: errors, Done: false}
    manager.Events = event
    manager.Devices = []*Device{}
    manager.DevicesByName = make(map[string] *Device)
    manager.DevicesByFd = make(map[int] *Device)
    manager.fds = []syscall.PollFd{}
    return manager
} 

func (manager *Manager) connectDevice(device * Device) {
    manager.Lock()
    defer manager.Unlock()
    manager.DevicesByName[device.FileName] = device
    manager.DevicesByFd[int(device.Fd())] = device
    manager.Devices = append(manager.Devices, device)
    pollfd := syscall.PollFd{Fd: int32(device.Fd()), Events : syscall.POLLIN}
    manager.fds = append(manager.fds, pollfd) 
    manager.Connect <- device
}

func (manager *Manager) disconnectDevice(device * Device) {
    manager.Lock()
    defer manager.Unlock()
    delete(manager.DevicesByName, device.FileName)
    delete(manager.DevicesByFd, int(device.Fd()))
    found := false
    var i int
    var pollfd syscall.PollFd
    for i, pollfd = range manager.fds {
        if pollfd.Fd == int32(device.Fd()) {
            found = true 
            break
        }
    }
    if found { 
        manager.fds = append(manager.fds[:i], manager.fds[(i+1):]...)
        manager.Devices = append(manager.Devices[:i], manager.Devices[(i+1):]...)
        manager.Disconnect <- device
    }
}

func (manager * Manager) ManageNewDevicesOnce() {
    fmt.Println("ManageNewDevicesOnce")
    names, err := List()
    if err != nil { 
        manager.Errors <- err
    } else {
        for _, name := range names {
            if _, found := manager.DevicesByName[name] ; !found {
                device, err := OpenAndGatherInfo(name)
                if err == nil {
                    manager.connectDevice(device)
                } else if manager.Verbose {
                    manager.Errors <- err
                }
            }
        }
    }
}

func (manager *Manager) ReadEventsOnce(device * Device) {
    events, err := device.ReadEvents()
    if err != nil {
        manager.Errors <- err
        manager.disconnectDevice(device)
    } else {
        for _, event := range events {
            manager.Events <- event
        }
    }
}

func (manager *Manager) ManageInputOnce() {
    res, err := syscall.Poll(manager.fds, 0) 
    if err != nil {
        manager.Errors <- err
    }
    if res > 0 { 
        for _, fd := range manager.fds {
            if (fd.Revents & syscall.POLLIN) != 0 {
                device, ok := manager.DevicesByFd[int(fd.Fd)] 
                if ok && device != nil {
                    manager.ReadEventsOnce(device)
                }
            }
            if (fd.Revents & syscall.POLLHUP) != 0 {
                device, ok := manager.DevicesByFd[int(fd.Fd)] 
                if ok && device != nil {
                    manager.disconnectDevice(device)
                }
            }

        }
    }
}

func (manager *Manager) ManageDevices() {
    for !manager.Done {
        manager.ManageNewDevicesOnce()
        time.Sleep(MANAGER_DEVICE_SCAN_DELAY)
    }
}

func (manager *Manager) ManageInput() {
    for !manager.Done {
        manager.ManageInputOnce()
    }
}

func (manager *Manager) Start() {
    go manager.ManageDevices()
    go manager.ManageInput()
}

func (manager *Manager) Stop() {
    manager.Lock() ; defer manager.Unlock()
    manager.Done = true
}


/*    
	printf("Supported events:\n");
	for (i = 0; i < EV_MAX; i++)
		if (TestBit(i, bit[0])) {
            
		}
		

	printf("Testing ... (interrupt to exit)\n");

	while (1) {
		rd = read(fd, ev, sizeof(struct input_event) * 64);

		if (rd < (int) sizeof(struct input_event)) {
			printf("yyy\n");
			perror("\nevtest: error reading");
			return 1;
		}

		for (i = 0; i < rd / sizeof(struct input_event); i++)

			if (ev[i].type == EV_SYN) {
				printf("Event: time %ld.%06ld, -------------- %s ------------\n",
					ev[i].time.tv_sec, ev[i].time.tv_usec, ev[i].code ? "Config Sync" : "Report Sync" );
			} else if (ev[i].type == EV_MSC && (ev[i].code == MSC_RAW || ev[i].code == MSC_SCAN)) {
				printf("Event: time %ld.%06ld, type %d (%s), code %d (%s), value %02x\n",
					ev[i].time.tv_sec, ev[i].time.tv_usec, ev[i].type,
					events[ev[i].type] ? events[ev[i].type] : "?",
					ev[i].code,
					names[ev[i].type] ? (names[ev[i].type][ev[i].code] ? names[ev[i].type][ev[i].code] : "?") : "?",
					ev[i].value);
			} else {
				printf("Event: time %ld.%06ld, type %d (%s), code %d (%s), value %d\n",
					ev[i].time.tv_sec, ev[i].time.tv_usec, ev[i].type,
					events[ev[i].type] ? events[ev[i].type] : "?",
					ev[i].code,
					names[ev[i].type] ? (names[ev[i].type][ev[i].code] ? names[ev[i].type][ev[i].code] : "?") : "?",
					ev[i].value);
			}	

	}
}
*/