package board

import (
	"fmt"
	"log"
	"strings"

	PahoMQTT "github.com/eclipse/paho.mqtt.golang"
	"github.com/spf13/viper"
)

type DummyChannel struct {
	ID                       string
	Num                      uint
	name                     string
	mqttStateTopic           string
	mqttCommandTopic         string
	value                    bool
	onboot                   string
	parent                   *DummyBoard
	onChannelUpdateFunctions map[string]onChannelUpdateFunction
}

type DummyBoard struct {
	ID           string
	Name         string
	ChannelCount uint
	channels     []*DummyChannel
}

func newDummyChannel(v *viper.Viper, channelID string) DummyChannel {
	v.SetDefault("name", "unknown")
	v.SetDefault("lastValue", false)
	v.SetDefault("onboot", "off")
	v.SetDefault("mqtt.statetopic", v.GetString("name"))

	value := false
	switch v.GetString("onboot") {
	case "on":
		value = true
	case "last":
		value = v.GetBool("lastValue")
	}
	// newUUID := UUID.New().String()
	// v.SetDefault("id", newUUID)
	return DummyChannel{
		ID:               channelID,
		Num:              v.GetUint("num"),
		name:             v.GetString("name"),
		mqttStateTopic:   v.GetString("mqtt.statetopic"),
		mqttCommandTopic: v.GetString("mqtt.commandtopic"),
		value:            value,
		onboot:           v.GetString("onboot"),
	}
}

func newDummyBoard(v *viper.Viper, id string) *Board {
	var b *DummyBoard

	v.SetDefault("name", "board "+id)
	v.SetDefault("type", "dummy")
	v.SetDefault("channelCount", 0)
	v.SetDefault("channels", "")

	if v.GetInt("channelCount") > 0 {
		for i := 0; i < v.GetInt("channelCount"); i++ {
			v.SetDefault("channels."+fmt.Sprint(i)+".num", i)
		}
	}

	b = &DummyBoard{
		ID:           id,
		Name:         v.GetString("name"),
		ChannelCount: v.GetUint("channelCount"),
	}

	channels := make([]*DummyChannel, v.GetInt("channelCount"))
	if v.GetInt("channelCount") > 0 {
		channelsConfig := v.Sub("channels")
		if channelsConfig != nil {
			for channelid1 := range channelsConfig.AllSettings() {
				channelid := strings.ToLower(channelid1)
				channelConfig := channelsConfig.Sub(channelid)
				c := newDummyChannel(channelConfig, channelid)
				c.parent = b
				if c.Num >= v.GetUint("channelCount") {
					continue
				}
				channels[c.Num] = &c
				AllChannels[c.ID] = &c
			}
		}
	}
	b.channels = channels
	var b1 Board = b
	return &b1
}

func (c *DummyChannel) Toggle() (bool, error) {
	c.value = !c.value
	c.SaveLastState()
	for f := range c.onChannelUpdateFunctions {
		c.onChannelUpdateFunctions[f](!c.value, c)
	}
	return c.value, nil
}

func (c *DummyChannel) On() error {
	oldval := c.value
	c.value = true
	c.SaveLastState()
	for f := range c.onChannelUpdateFunctions {
		c.onChannelUpdateFunctions[f](oldval, c)
	}
	return nil
}

func (c *DummyChannel) Off() error {
	oldval := c.value
	c.value = true
	c.SaveLastState()
	for f := range c.onChannelUpdateFunctions {
		c.onChannelUpdateFunctions[f](oldval, c)
	}
	return nil
}

func (c *DummyChannel) ToString() string {
	if !c.value {
		return "off"
	}
	return "on"
}

func (c *DummyChannel) SaveLastState() {
	if c.onboot == "last" {
		s := fmt.Sprintf("boards.%s.channels.%s.lastvalue", c.parent.ID, c.ID)
		viper.Set(s, c.value)
		viper.WriteConfig()
	}
}

func (c *DummyChannel) Status() bool {
	return c.value
}

func (c *DummyChannel) Parent() Board {
	return c.parent
}

func (c *DummyChannel) Dump() {
	log.Printf(" Channel %d (on boot: %s): %s \n", c.Num, c.onboot, c.name)
}

func (b DummyBoard) Dump() {
	log.Printf("Board '%s' (id: %s): %d channels\n", b.Name, b.ID, b.ChannelCount)
	for c := range b.channels {
		b.channels[c].Dump()
	}
}

func (b *DummyBoard) Initialize() {
	return
}

func (b *DummyBoard) Channel(num uint64) Channel {
	return b.channels[num]
}

func (c *DummyChannel) Name() string {
	return c.name
}

func (c *DummyChannel) OnBoot() string {
	return c.onboot
}

func (c *DummyChannel) SetOnBoot(str string) {
	c.onboot = str
	s := fmt.Sprintf("boards.%s.channels.%s.onboot", c.parent.ID, c.ID)
	viper.Set(s, str)
}

func (c *DummyChannel) SetMQTTStateTopic(str string) {
	c.mqttStateTopic = str
	s := fmt.Sprintf("boards.%s.channels.%s.mqtt.statetopic", c.parent.ID, c.ID)
	viper.Set(s, str)
}

func (c *DummyChannel) SetMQTTCommandTopic(str string) {
	c.mqttCommandTopic = str
	s := fmt.Sprintf("boards.%s.channels.%s.mqtt.commandtopic", c.parent.ID, c.ID)
	viper.Set(s, str)
}

func (c *DummyChannel) MQTTHandler(client PahoMQTT.Client, msg PahoMQTT.Message) {
	switch string(msg.Payload()) {
	case "on":
		if !c.value {
			c.value = true
			c.SaveLastState()
		}
	case "off":
		if c.value {
			c.value = false
			c.SaveLastState()
		}
	}
}

func (c *DummyChannel) MQTTStateTopic() string {
	return c.mqttStateTopic
}

func (c *DummyChannel) MQTTCommandTopic() string {
	return c.mqttCommandTopic
}

func init() {
	RegisterBoardType("dummy", newDummyBoard)
}

func (c *DummyChannel) AddOnChannelUpdateFunction(name string, f onChannelUpdateFunction) {
	if c.onChannelUpdateFunctions == nil {
		c.onChannelUpdateFunctions = make(map[string]onChannelUpdateFunction)
	}
	c.onChannelUpdateFunctions[name] = f
}
