package main

import "time"
import "log"
import "fmt"
import "os"
import "os/signal"
import "syscall"
import "github.com/stianeikeland/go-rpio/v4"
import "github.com/hypebeast/go-osc/osc"

const (
	// TX series codes
	AMP_Input_CD    = 0x020 // ATEM
	AMP_Input_MD    = 0x030
	AMP_Input_Tape  = 0x070
	AMP_Input_DVD   = 0x120
	AMP_Input_CDR   = 0x130 // Assigned to tape by holding button for 3 secs
	AMP_Input_HDD   = 0x170 // Assigned to video or tape by holding button for 3 secs
	AMP_Input_Video = 0x1a0

	AMP_PowerOn_OrMask = 0x0f

	// These work only when video input is selected
	AMP_Vol_up     = 0x1a2
	AMP_Vol_down   = 0x1a3
	AMP_Vol_mute   = 0x1a4
	AMP_Vol_unmute = 0x1a5
	AMP_Off        = 0x1ae
	AMP_On         = 0x1af

	// Dimmer
	AMP_DimmerHi  = 0x2b0
	AMP_DimmerMid = 0x2b1
	AMP_DimmerLo  = 0x2b2

	// Test modes
	Test_mode = 0x421
)

type IR struct {
	pin rpio.Pin
}

func NewIR() *IR {
	if err := rpio.Open(); err != nil {
		log.Fatal(err)
	}
	//defer rpio.Close()

	pin := rpio.Pin(26)
	pin.Output() // Output mode
	return &IR{pin}
}

// Encode a bit of IR information
func (ir *IR) sendHiLo(hi time.Duration, lo time.Duration) {
	ir.pin.High()
	time.Sleep(hi * time.Millisecond)

	ir.pin.Low()
	time.Sleep(lo * time.Millisecond)
}

func (ir *IR) sendCode(code int) {
	// Send header
	ir.sendHiLo(3, 1)

	// Send 12 bits of command
	for mask := 0x800; mask != 0; mask >>= 1 {
		if code&mask != 0 {
			ir.sendHiLo(1, 2) // 1
		} else {
			ir.sendHiLo(1, 1) // 0
		}
	}

	// Send end gap
	ir.sendHiLo(1, 21)
	fmt.Printf("Sent code: %03x\n", code)
}

func getOSCInt(msg *osc.Message) int32 {
	for _, arg := range msg.Arguments {
		switch arg.(type) {
		case int32:
			return arg.(int32)
		}
	}
	return 0
}

func main() {
	ir := NewIR()

	// Setup and spawn the OSC server
	addr := "0.0.0.0:9137"
	d := osc.NewStandardDispatcher()

	d.AddMsgHandler("/8bus/amp/input/tape", func(msg *osc.Message) { ir.sendCode(AMP_Input_Tape) })
	d.AddMsgHandler("/8bus/amp/input/md", func(msg *osc.Message) { ir.sendCode(AMP_Input_MD) })
	d.AddMsgHandler("/8bus/amp/input/cd", func(msg *osc.Message) { ir.sendCode(AMP_Input_CD) })
	d.AddMsgHandler("/8bus/amp/input/cdr", func(msg *osc.Message) { ir.sendCode(AMP_Input_CDR) })
	d.AddMsgHandler("/8bus/amp/input/dvd", func(msg *osc.Message) { ir.sendCode(AMP_Input_DVD) })
	d.AddMsgHandler("/8bus/amp/input/hdd", func(msg *osc.Message) { ir.sendCode(AMP_Input_HDD) })
	d.AddMsgHandler("/8bus/amp/input/video", func(msg *osc.Message) { ir.sendCode(AMP_Input_Video) })
	d.AddMsgHandler("/8bus/amp/vol/up", func(msg *osc.Message) { ir.sendCode(AMP_Vol_up) })
	d.AddMsgHandler("/8bus/amp/vol/down", func(msg *osc.Message) { ir.sendCode(AMP_Vol_down) })
	d.AddMsgHandler("/8bus/amp/vol/mute", func(msg *osc.Message) { ir.sendCode(AMP_Vol_mute) })
	d.AddMsgHandler("/8bus/amp/vol/unmute", func(msg *osc.Message) { ir.sendCode(AMP_Vol_unmute) })
	d.AddMsgHandler("/8bus/amp/power/on", func(msg *osc.Message) { ir.sendCode(AMP_On) })
	d.AddMsgHandler("/8bus/amp/power/off", func(msg *osc.Message) { ir.sendCode(AMP_Off) })
	d.AddMsgHandler("/8bus/amp/code", func(msg *osc.Message) { ir.sendCode(int(getOSCInt(msg))) })

	// Set up and start OSC server
	go func() {
		server := &osc.Server{Addr: addr, Dispatcher: d}
		if err := server.ListenAndServe(); err != nil {
			log.Fatal("error: ", err.Error())
		}
	}()

	// Wait for signal
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT)

	d.AddMsgHandler("/8bus/amp/halt", func(msg *osc.Message) { ch <- syscall.SIGINT })
	<-ch

	fmt.Println("sent")
}
