package main

import (
	hue "github.com/collinux/gohue"
	log "github.com/golang/glog"
	"github.com/prometheus/client_golang/prometheus"
)

type sensorCollector struct {
	bridge            *hue.Bridge
	ignoreTypes       []string
	sensorValue       *prometheus.GaugeVec
	sensorLastUpdated *prometheus.GaugeVec
	sensorOn          *prometheus.GaugeVec
	sensorBattery     *prometheus.GaugeVec
	sensorReachable   *prometheus.GaugeVec
}

var variableSensorLabelNames = []string{
	"name",
	"type",
	"model_id",
	"manufacturer_name",
	"product_name",
	"unique_id",
}

func contains(a []string, x string) bool {
	for _, n := range a {
		if x == n {
			return true
		}
	}
	return false
}

// NewSensorCollector Create a new Hue collector for sensors
func NewSensorCollector(namespace string, bridge *hue.Bridge, ignoreTypes []string) prometheus.Collector {
	c := sensorCollector{
		bridge:      bridge,
		ignoreTypes: ignoreTypes,
		sensorValue: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: namespace,
				Subsystem: "sensor",
				Name:      "value",
				Help:      "Sensor values",
			},
			variableSensorLabelNames,
		),
		sensorBattery: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: namespace,
				Subsystem: "sensor",
				Name:      "battery",
				Help:      "Sensor battery levels (%)",
			},
			variableSensorLabelNames,
		),
		sensorLastUpdated: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: namespace,
				Subsystem: "sensor",
				Name:      "last_updated",
				Help:      "Sensor last updated time",
			},
			variableSensorLabelNames,
		),
		sensorOn: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: namespace,
				Subsystem: "sensor",
				Name:      "on",
				Help:      "Sensor on/off (1/0)",
			},
			variableSensorLabelNames,
		),
		sensorReachable: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: namespace,
				Subsystem: "sensor",
				Name:      "reachable",
				Help:      "Sensor reachability (1/0)",
			},
			variableSensorLabelNames,
		),
	}

	return c
}

func (c sensorCollector) Describe(ch chan<- *prometheus.Desc) {
	c.sensorValue.Describe(ch)
	c.sensorBattery.Describe(ch)
	c.sensorLastUpdated.Describe(ch)
	c.sensorOn.Describe(ch)
	c.sensorReachable.Describe(ch)
}

func (c sensorCollector) Collect(ch chan<- prometheus.Metric) {
	c.sensorValue.Reset()
	c.sensorBattery.Reset()
	c.sensorLastUpdated.Reset()
	c.sensorOn.Reset()
	c.sensorReachable.Reset()

	sensors, err := c.bridge.GetAllSensors()
	if err != nil {
		log.Errorf("Failed to update sensors: %v", err)
	}

	for _, sensor := range sensors {
		var sensorValue float64
		if contains(c.ignoreTypes, sensor.Type) {
			continue
		} else if sensor.Type == "Daylight" {
			// bridge daylight (sunrise / sunset) sensor
			if sensor.State.Daylight {
				sensorValue = 1
			}
		} else if sensor.Type == "ZGPSwitch" {
			// Hue tap switch
			sensorValue = float64(sensor.State.ButtonEvent)
		} else if sensor.Type == "ZLLSwitch" {
			// Hue dimmer switch
			sensorValue = float64(sensor.State.ButtonEvent)
		} else if sensor.Type == "ClipGenericStatus" {
			sensorValue = float64(sensor.State.Status)
		} else if sensor.Type == "ZLLTemperature" {
			sensorValue = float64(sensor.State.Temperature)
		} else if sensor.Type == "ZLLPresence" {
			if sensor.State.Presence {
				sensorValue = 1
			}
		} else if sensor.Type == "ZLLLightLevel" {
			sensorValue = float64(sensor.State.LightLevel)
		}

		sensorLabels := prometheus.Labels{
			"name":              sensor.Name,
			"model_id":          sensor.ModelID,
			"manufacturer_name": sensor.ManufacturerName,
			"type":              sensor.Type,
			"unique_id":         sensor.UniqueID,
			"product_name":      sensor.ProductName,
		}

		c.sensorValue.With(sensorLabels).Set(sensorValue)
		c.sensorBattery.With(sensorLabels).Set(float64(sensor.Config.Battery))
		c.sensorLastUpdated.With(sensorLabels).Set(float64(sensor.State.LastUpdated.Unix()))
		if sensor.Config.On {
			c.sensorOn.With(sensorLabels).Set(1)
		} else {
			c.sensorOn.With(sensorLabels).Set(0)
		}
		if sensor.Config.Reachable {
			c.sensorReachable.With(sensorLabels).Set(1)
		} else {
			c.sensorReachable.With(sensorLabels).Set(0)
		}
	}

	c.sensorValue.Collect(ch)
	c.sensorBattery.Collect(ch)
	c.sensorLastUpdated.Collect(ch)
	c.sensorOn.Collect(ch)
	c.sensorReachable.Collect(ch)
}