package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"regexp"
	"strings"
)

var loadedConfiguration = &Config{}
var currentConfiguration *AppConfig

type Config struct {
	Apps []*AppConfig `json:"apps"`
}

type AppConfig struct {
	Name               string   `json:"name"`
	MatchWindowTitles  []string `json:"match_window_titles"`
	SlowJog            *int     `json:"slow_jog"` // Time in millisecond to use slow jog
	windowTitleRegexps []*regexp.Regexp
	Bindings           map[string]string `json:"bindings"`
	bindings           []*deviceBinding
}

func (ac *AppConfig) parse() error {
	if len(ac.MatchWindowTitles) == 0 {
		ac.windowTitleRegexps = []*regexp.Regexp{
			regexp.MustCompile(`.*`),
		}
		return nil
	}

	for _, window := range ac.MatchWindowTitles {
		re, err := regexp.Compile(window)
		if err != nil {
			return fmt.Errorf("Invalid regexp in window match %q: %s", window, err)
		}

		ac.windowTitleRegexps = append(ac.windowTitleRegexps, re)
	}

	return nil
}

type deviceBinding struct {
	// Input
	heldButtons map[int]bool
	buttonDown  int
	otherKey    string

	// Output
	holdButtons []string
	pressButton string
	original    string
	description string
}

func (ac *AppConfig) parseBindings() error {
	for key, value := range ac.Bindings {
		binding, description := bindingAndDescription(value)
		newBinding := &deviceBinding{heldButtons: make(map[int]bool), original: binding, description: description}

		// Input
		input := strings.Split(key, "+")
		for idx, part := range input {
			cleanPart := strings.TrimSpace(part)
			key := strings.ToUpper(cleanPart)
			if shuttleKeys[key] == 0 && !otherShuttleKeysUpper[key] {
				return fmt.Errorf("invalid shuttle device key map: %q doesn't exist", cleanPart)
			}
			if idx == len(input)-1 {
				if shuttleKeys[key] != 0 {
					newBinding.buttonDown = shuttleKeys[key]
				} else {
					newBinding.otherKey = key
				}
			} else {
				keyID := shuttleKeys[key]
				if keyID == 0 {
					return fmt.Errorf("binding %q, expects a button press, not a shuttle or jog movement")
				}
				newBinding.heldButtons[keyID] = true
			}
		}

		// Output
		// output := strings.Split(value, "+")
		// for idx, part := range output {
		// 	cleanPart := strings.TrimSpace(part)
		// 	buttonName := strings.ToUpper(cleanPart)
		// 	if keyboardKeysUpper[buttonName] == 0 {
		// 		return fmt.Errorf("keyboard key unknown: %q", cleanPart)
		// 	}
		// 	if idx == len(output)-1 {
		// 		newBinding.pressButton = buttonName
		// 	} else {
		// 		newBinding.holdButtons = append(newBinding.holdButtons, buttonName)
		// 	}
		// }

		ac.bindings = append(ac.bindings, newBinding)

		fmt.Printf("BINDING: %#v\n", newBinding)
	}

	return nil
}

var descriptionRE = regexp.MustCompile(`([^/]*)(\s*// *(.+))?`)

func bindingAndDescription(input string) (string, string) {
	matches := descriptionRE.FindStringSubmatch(input)
	if matches == nil {
		return input, ""
	}
	return strings.TrimSpace(matches[1]), strings.TrimSpace(matches[3])
}

func LoadConfig(filename string) error {
	cnt, err := ioutil.ReadFile(filename)
	if err != nil {
		return err
	}

	newConfig := &Config{}
	err = json.Unmarshal(cnt, &newConfig)
	if err != nil {
		return err
	}

	for _, app := range newConfig.Apps {
		if err := app.parse(); err != nil {
			return fmt.Errorf("Error parsing app %q's matchers: %s", app.Name, err)
		}

		if err := app.parseBindings(); err != nil {
			return fmt.Errorf("Error parsing app %q's bindings: %s", app.Name, err)
		}

	}

	loadedConfiguration = newConfig

	return nil
}
