#include <QPainter>
#include <QStyleOption>
#include <QApplication>
#include <QBoxLayout>
#include <QPainterPath>
#include <QLayout>
#include <qmath.h>
#include "light.h"
#include "qad_types.h"

template <typename T>
inline T qClamp(T v, T min, T max) {return qMin<T>(max, qMax<T>(min, v));}


SHSLight::SHSLight(QWidget * parent): QWidget(parent) {
	init();
}


SHSLight::~SHSLight() {
}


double SHSLight::level() const {
	if (binary())
		return (level_ > 0.5) ? highLevel() : lowLevel();
	return level_;
}


bool SHSLight::isOff() const {
	return qAbs(level_ - lowLevel()) < 1.E-3;
}


bool SHSLight::isOn() const {
	return qAbs(level_ - highLevel()) < 1.E-3;
}


int SHSLight::heightForWidth(int) const {
	return width();
}


bool SHSLight::hasHeightForWidth() const {
	return false;
}


void SHSLight::init() {
	icon_type = Bulb;
	setIconType(Bulb);
	anim.setTargetObject(this);
	anim.setPropertyName("h__anim");
	anim.setDuration(200);
	anim.setEasingCurve(QEasingCurve::OutQuad);
	fill_color = QColor(255, 245, 60);
	back_color = QColor(216, 216, 216);
	level_ = ap_ = 0.;
	ang_shift = 20.;
	bar_ = 30.;
	click_thrsh = 75.;
	m_lowLevel = last_click_level = 0.;
	m_highLevel = m_onLevel = 100.;
	track_ = ro_ = bin_ = mouse_inner_ = false;
	m_restoreLastOnLevel = m_lowLevelIsNull = true;
}


QSize SHSLight::minimumSizeHint() const {
	return QSize(40, 40) * appScale();
}


QSize SHSLight::sizeHint() const {
	return QSize(120, 120) * appScale();
}


void SHSLight::mousePressEvent(QMouseEvent * e) {
	if (ro_) {
		QWidget::mousePressEvent(e);
		return;
	}
	float w = width(), h = height(), r = qMin<float>(w, h) / 2.f, lr = bin_ ? r / 80. : r * bar_ / 100.;
	float dx = e->x() - w / 2., dy = e->y() - h / 2.;
	float mr = qSqrt((dx*dx) + (dy*dy));
	if (mr > r) {
		QWidget::mousePressEvent(e);
		return;
	}
	if (mr < r - lr) {
		mouse_inner_ = true;
		click();
		emit clicked();
		return;
	}
	track_ = true;
	mouseMoveEvent(e);
	//qDebug() << mr;
}


void SHSLight::mouseReleaseEvent(QMouseEvent * e) {
	if (!track_ && !mouse_inner_)
		QWidget::mouseReleaseEvent(e);
	track_ = mouse_inner_ = false;
}


void SHSLight::mouseMoveEvent(QMouseEvent * e) {
	if (mouse_inner_) return;
	if (!track_ || ro_ || bin_) {
		QWidget::mouseMoveEvent(e);
		return;
	}
	float w = width(), h = height();
	float dx = e->x() - w / 2., dy = e->y() - h / 2.;
	float ma = qAtan2(dx, dy) * 180. / M_PI;
	if (ma < 0) ma = -ma;
	else ma = 360. - ma;
	double lr = highLevel() - lowLevel();
	setLevel(qClamp<double>((ma - ang_shift) / (360. - ang_shift - ang_shift), 0., 1.) * lr + lowLevel());
}


void SHSLight::paintEvent(QPaintEvent * ) {
	QPainter p(this);
	p.setRenderHint(QPainter::Antialiasing);
	p.setRenderHint(QPainter::SmoothPixmapTransform);
	float w = width(), h = height(), r = qMin<float>(w, h) / 2.f, thick = qMax<double>(1., r / 80.);
	r -= thick;
	float thin = qMax<double>(1., thick / 3.), lr = bin_ ? r / 80. : r * bar_ / 100.;
	p.translate(w / 2.f, h / 2.f);
	p.save();
	p.rotate(90.);
	QRectF rct(-r, -r, r + r, r + r);
	double range = highLevel() - lowLevel();
	double nv = qClamp<double>((ap_ - lowLevel()) / qMax(range, 1.E-3), 0., 1.) * 100.;
	if (bin_) {
		p.setPen(QPen(Qt::darkGray, thick));
		p.setBrush(Qt::NoBrush);
		p.drawEllipse(rct);
	} else {
		QPainterPath path, path2;
		QPointF tp;
		path.arcMoveTo(rct, ang_shift);
		tp = path.currentPosition();
		path.arcTo(rct, ang_shift, 360 - ang_shift - ang_shift);
		rct.adjust(lr, lr, -lr, -lr);
		path2.arcMoveTo(rct, -ang_shift);
		path2.arcTo(rct, -ang_shift, -360 + ang_shift + ang_shift);
		path2.lineTo(tp);
		path.connectPath(path2);
		p.setPen(QPen(Qt::darkGray, thick));
		QConicalGradient grad;
		double v = (1. - nv / (100. - thin / 3.6)) * ((360. - ang_shift - ang_shift) / 360.) + (ang_shift / 360.);
		grad.setColorAt(1., fill_color);
		grad.setColorAt(qMin<double>(1., v + thin / 360.), fill_color);
		grad.setColorAt(v, back_color);
		grad.setColorAt(0., back_color);
		p.setBrush(grad);
		p.drawPath(path);
	}
	
	float hs = (r - lr) / 1.2;
	QRect ir(-hs, -hs, hs + hs, hs + hs);
	p.restore();
	p.drawImage(ir, im_0);
	
	float wa = 0.f;
	if (!im_100.isNull()) {
		wa = qMin<double>(1., qPow(nv / 75., 0.5));
		p.setOpacity(wa);
		p.drawImage(ir, im_100);
	}

	if (!im_w.isNull()) {
		wa = qMin<double>(1., qPow(nv / 20., 0.4));
		p.setOpacity(wa);
		p.drawImage(ir, im_w);
	}

	wa = qMin<double>(1., qPow(qMax(0., (nv - 70.) / 30.), 1.5));
	if (wa > 0. && !im_h.isNull()) {
		p.setOpacity(wa);
		p.drawImage(ir, im_h);
	}
}


void SHSLight::animate() {
	anim.stop();
	anim.setStartValue(g_anim());
	anim.setEndValue(level_);
	anim.start();
}


void SHSLight::s_anim(double v) {
	ap_ = v;
	update();
}


void SHSLight::setBinary(bool y) {
	bin_ = y;
	double l = level_;
	level_ = -1.;
	setLevel(l);
	update();
}


void SHSLight::setLevel(double l) {
	if (level_ == l) return;
	if (binary())
		level_ = (l > clickThreshold()) ? highLevel() : lowLevel();
	else
		level_ = qClamp(l, lowLevel(), highLevel());
	animate();
	if (level_ <= lowLevel() && m_lowLevelIsNull)
		level_ = 0.;
	emit levelChanged(level_);
	if (level_ <= lowLevel()) emit turnedOff();
	if (level_ >= highLevel()) emit turnedOn();
}


void SHSLight::setLowLevel(double ll) {
	m_lowLevel = ll;
	if (m_onLevel < m_lowLevel) m_onLevel = m_lowLevel;
	setClickThreshold(qClamp(clickThreshold(), lowLevel(), onLevel()));
	setLevel(qClamp(level(), lowLevel(), highLevel()));
	update();
}


void SHSLight::setHighLevel(double hl) {
	m_highLevel = hl;
	if (m_onLevel > m_highLevel) m_onLevel = m_highLevel;
	setClickThreshold(qClamp(clickThreshold(), lowLevel(), onLevel()));
	setLevel(qClamp(level(), lowLevel(), highLevel()));
	update();
}


void SHSLight::setOnLevel(double ol) {
	m_onLevel = qClamp(ol, lowLevel(), highLevel());
	setClickThreshold(qClamp(clickThreshold(), lowLevel(), onLevel()));
}


void SHSLight::setLowLevelIsNull(bool yes) {
	m_lowLevelIsNull = yes;
}


void SHSLight::setFillColor(const QColor & c) {
	fill_color = c;
	update();
}


void SHSLight::setBackColor(const QColor & c) {
	back_color = c;
	update();
}


void SHSLight::setAngleShift(double a) {
	ang_shift = qClamp<double>(a, 0., 180.);
	update();
}


void SHSLight::setBarSize(double a) {
	bar_ = qClamp<double>(a, 0., 100.);
	update();
}


void SHSLight::setClickThreshold(double a) {
	click_thrsh = qClamp(a, lowLevel(), onLevel());
}


void SHSLight::setReadOnly(bool y) {
	ro_ = y;
}


void SHSLight::setIconType(SHSLight::IconType it) {
	icon_type = it;
	QString ip = ":/icons/light_";
	switch (it) {
	case Bulb: ip += "bulb"; break;
	case LED:  ip += "led"; break;
	case Spot: ip += "spot"; break;
	default: break;
	}
	im_0   = QImage(ip + "_0.png");
	im_100 = QImage(ip + "_100.png");
	im_w   = QImage(ip + "_w.png");
	im_h   = QImage(ip + "_h.png");
	update();
}


void SHSLight::click() {
	if (binary()) {
		setLevel(isOn() ? lowLevel() : highLevel());
	} else {
		if (level_ >= click_thrsh) {
			last_click_level = level();
			setLevel(lowLevel());
		} else {
			if (last_click_level < click_thrsh)
				last_click_level = highLevel();
			setLevel(m_restoreLastOnLevel ? last_click_level : onLevel());
		}
	}
}
