/* SPDX-License-Identifier: BSD-2-Clause */
/*
 * Copyright (C) 2019, Raspberry Pi Ltd
 *
 * Piecewise linear functions interface
 */
#pragma once

#include <algorithm>
#include <functional>
#include <string>
#include <utility>
#include <vector>

#include "vector.h"

namespace libcamera {

namespace ipa {

class Pwl
{
public:
	using Point = Vector<double, 2>;

	struct Interval {
		Interval(double _start, double _end)
			: start(_start), end(_end) {}

		bool contains(double value)
		{
			return value >= start && value <= end;
		}

		double clamp(double value)
		{
			return std::clamp(value, start, end);
		}

		double length() const { return end - start; }

		double start, end;
	};

	Pwl();
	Pwl(const std::vector<Point> &points);
	Pwl(std::vector<Point> &&points);

	void append(double x, double y, double eps = 1e-6);

	bool empty() const { return points_.empty(); }
	size_t size() const { return points_.size(); }

	Interval domain() const;
	Interval range() const;

	double eval(double x, int *span = nullptr,
		    bool updateSpan = true) const;

	std::pair<Pwl, bool> inverse(double eps = 1e-6) const;
	Pwl compose(const Pwl &other, double eps = 1e-6) const;

	void map(std::function<void(double x, double y)> f) const;

	static Pwl
	combine(const Pwl &pwl0, const Pwl &pwl1,
		std::function<double(double x, double y0, double y1)> f,
		double eps = 1e-6);

	Pwl &operator*=(double d);

	std::string toString() const;

private:
	static void map2(const Pwl &pwl0, const Pwl &pwl1,
			 std::function<void(double x, double y0, double y1)> f);
	void prepend(double x, double y, double eps = 1e-6);
	int findSpan(double x, int span) const;

	std::vector<Point> points_;
};

} /* namespace ipa */

} /* namespace libcamera */
