package mqttclient

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"math"
	"os/exec"
	"strconv"

	"github.com/AFASystems/presence/internal/pkg/model"
	"github.com/yosssi/gmq/mqtt"
	"github.com/yosssi/gmq/mqtt/client"
)

func GetBeaconID(incoming model.Incoming_json) string {
	unique_id := fmt.Sprintf("%s", incoming.MAC)
	return unique_id
}

func updateLatestList(incoming model.Incoming_json, now int64, latestList *model.LatestBeaconsList) {
	latestList.Lock.Lock()
	defer latestList.Lock.Unlock()

	b := model.Beacon{
		Beacon_id:       GetBeaconID(incoming),
		Beacon_type:     incoming.Beacon_type,
		Last_seen:       now,
		Incoming_JSON:   incoming,
		Beacon_location: incoming.Hostname,
		Distance:        getBeaconDistance(incoming),
	}

	latestList.LatestList[b.Beacon_id] = b

	for id, v := range latestList.LatestList {
		if now-v.Last_seen > 10 {
			delete(latestList.LatestList, id)
		}
	}
}

func updateBeaconData(beacon *model.Beacon, incoming model.Incoming_json, now int64, cl *client.Client, settings *model.SettingsVal) {
	beacon.Incoming_JSON = incoming
	beacon.Last_seen = now
	beacon.Beacon_type = incoming.Beacon_type
	beacon.HB_ButtonCounter = incoming.HB_ButtonCounter
	beacon.HB_Battery = incoming.HB_Battery
	beacon.HB_RandomNonce = incoming.HB_RandomNonce
	beacon.HB_ButtonMode = incoming.HB_ButtonMode

	m := model.BeaconMetric{
		Distance:  getBeaconDistance(incoming),
		Timestamp: now,
		Rssi:      int64(incoming.RSSI),
		Location:  incoming.Hostname,
	}

	beacon.Beacon_metrics = append(beacon.Beacon_metrics, m)
	if len(beacon.Beacon_metrics) > settings.Beacon_metrics_size {
		beacon.Beacon_metrics = beacon.Beacon_metrics[1:]
	}

	if beacon.HB_ButtonCounter_Prev != beacon.HB_ButtonCounter {
		beacon.HB_ButtonCounter_Prev = incoming.HB_ButtonCounter
		sendButtonPressed(*beacon, cl)
	}
}

func sendButtonPressed(beacon model.Beacon, cl *client.Client) {
	btn_msg, err := json.Marshal(beacon)
	if err != nil {
		panic(err)
	}

	err = cl.Publish(&client.PublishOptions{
		QoS:       mqtt.QoS1,
		TopicName: []byte("afa-systems/presence/button/" + beacon.Beacon_id),
		Message:   btn_msg,
	})
	if err != nil {
		panic(err)
	}
	s := fmt.Sprintf("/usr/bin/php /usr/local/presence/alarm_handler.php  --idt=%s --idr=%s --st=%d", beacon.Beacon_id, beacon.Incoming_JSON.Hostname, beacon.HB_ButtonCounter)
	err, out, errout := Shellout(s)
	if err != nil {
		log.Printf("error: %v\n", err)
	}
	fmt.Println("--- stdout ---")
	fmt.Println(out)
	fmt.Println("--- stderr ---")
	fmt.Println(errout)
}

func getBeaconDistance(incoming model.Incoming_json) float64 {
	distance := 1000.0
	distance = getiBeaconDistance(incoming.RSSI, incoming.TX_power)

	return distance
}

func getiBeaconDistance(rssi int64, power string) float64 {
	ratio := float64(rssi) * (1.0 / float64(twos_comp(power)))
	distance := 100.0
	if ratio < 1.0 {
		distance = math.Pow(ratio, 10)
	} else {
		distance = (0.89976)*math.Pow(ratio, 7.7095) + 0.111
	}
	return distance
}

func twos_comp(inp string) int64 {
	i, _ := strconv.ParseInt("0x"+inp, 0, 64)

	return i - 256
}

func Shellout(command string) (error, string, string) {
	var stdout bytes.Buffer
	var stderr bytes.Buffer
	cmd := exec.Command("bash", "-c", command)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err := cmd.Run()
	return err, stdout.String(), stderr.String()
}
