#include "bh1750_sensor.h"

Bh1750Sensor::Bh1750Sensor() {
}

bool Bh1750Sensor::begin(const Bh1750Config &config) {
	_config = config;

	if (_config.lux_max <= 0.0f) _config.lux_max = 1000.0f;

	Wire.begin(_config.sda_pin, _config.scl_pin);

	/*
		Пробуем перевести датчик в выбранный режим измерения.
	*/
	if (!send_command(_config.measurement_mode)) {
		_initialized = false;
		_online = false;
		return false;
	}

	_initialized = true;
	_online = true;
	return true;
}

void Bh1750Sensor::update() {
	if (!_initialized) {
		return;
	}

	uint32_t now_ms = millis();

	if (now_ms - _last_read_attempt_ms < _config.read_interval_ms) {
		return;
	}

	_last_read_attempt_ms = now_ms;

	uint16_t raw_value = 0;
	if (!read_measurement(raw_value)) {
		_online = false;
		return;
	}

	_online = true;
	_has_valid_data = true;
	_last_success_read_ms = now_ms;
	_last_raw_value = raw_value;

	/*
		Согласно даташиту:
		lux = raw / 1.2
	*/
	_raw_lux = static_cast<float>(raw_value) / 1.2f;

	update_filtered_lux(_raw_lux);
}

float Bh1750Sensor::get_raw_lux() const {
	return _raw_lux;
}

float Bh1750Sensor::get_filtered_lux() const {
	return _filtered_lux;
}

uint8_t Bh1750Sensor::get_level() const {
	if (!_has_valid_data) return 0;

	float normalized = _filtered_lux / _config.lux_max;
	if (normalized < 0.0f) normalized = 0.0f;
	if (normalized > 1.0f) normalized = 1.0f;

	return (uint8_t)(normalized * 10.0f + 0.5f);
}

uint8_t Bh1750Sensor::get_percent() const {
	if (!_has_valid_data) return 0;

	float normalized = _filtered_lux / _config.lux_max;
	if (normalized < 0.0f) normalized = 0.0f;
	if (normalized > 1.0f) normalized = 1.0f;

	return (uint8_t)(normalized * 100.0f + 0.5f);
}

bool Bh1750Sensor::has_valid_data() const {
	return _has_valid_data;
}

bool Bh1750Sensor::is_stale() const {
	if (!_has_valid_data) {
		return true;
	}

	uint32_t now_ms = millis();
	return (now_ms - _last_success_read_ms) > _config.stale_after_ms;
}

bool Bh1750Sensor::is_online() const {
	return _online;
}

String Bh1750Sensor::get_state_json() const {
	String json = "{";
	json += "\"online\":"  + String(_online ? "true" : "false") + ",";
	json += "\"level\":"   + String(get_level()) + ",";
	json += "\"lux\":"     + String(_filtered_lux, 2) + ",";
	json += "\"percent\":" + String(get_percent());
	json += "}";

	return json;
}

bool Bh1750Sensor::send_command(uint8_t command) {
	Wire.beginTransmission(_config.i2c_address);
	Wire.write(command);
	return (Wire.endTransmission() == 0);
}

bool Bh1750Sensor::read_measurement(uint16_t &raw_out) {
	Wire.requestFrom((int)_config.i2c_address, 2);

	if (Wire.available() != 2) {
		return false;
	}

	uint8_t msb = Wire.read();
	uint8_t lsb = Wire.read();

	raw_out = (static_cast<uint16_t>(msb) << 8) | lsb;
	return true;
}

void Bh1750Sensor::update_filtered_lux(float new_lux) {
	if (!_has_valid_data || _filtered_lux == 0.0f) {
		_filtered_lux = new_lux;
		return;
	}

	_filtered_lux =
		(_filtered_lux * (1.0f - _config.lux_ema_alpha)) +
		(new_lux * _config.lux_ema_alpha);
}