/*
    File                 : IntervalAttribute.h
    Project              : LabPlot
    Description          : A class representing an interval-based attribute
    --------------------------------------------------------------------
    SPDX-FileCopyrightText: 2007 Knut Franke <knut.franke@gmx.de>
    SPDX-FileCopyrightText: 2007 Tilman Benkert <thzs@gmx.net>

    SPDX-License-Identifier: GPL-2.0-or-later
*/

#ifndef INTERVALATTRIBUTE_H
#define INTERVALATTRIBUTE_H

#include "Interval.h"
#include <QVector>

//! A class representing an interval-based attribute
template<class T> class IntervalAttribute {
public:
	void setValue(const Interval<int>& i, T value) {
		// first: subtract the new interval from all others
		QVector< Interval<int> > temp_list;
		for (int c = 0; c < m_intervals.size(); c++) {
			temp_list = Interval<int>::subtract(m_intervals.at(c), i);
			if (temp_list.isEmpty()) {
				m_intervals.removeAt(c);
				m_values.removeAt(c--);
			} else {
				m_intervals.replace(c, temp_list.at(0));
				if (temp_list.size() > 1) {
					m_intervals.insert(c, temp_list.at(1));
					m_values.insert(c, m_values.at(c));
				}
			}
		}

		// second: try to merge the new interval with an old one
		for (int c = 0; c < m_intervals.size(); c++) {
			if (m_intervals.at(c).touches(i) && m_values.at(c) == value) {
				m_intervals.replace(c, Interval<int>::merge(m_intervals.at(c), i));
				return;
			}
		}
		// if it could not be merged, just append it
		m_intervals.append(i);
		m_values.append(value);
	}

	// overloaded for convenience
	void setValue(int row, T value)
	{
		setValue(Interval<int>(row, row), value);
	}

	T value(int row) const
	{
		for(int c=m_intervals.size()-1; c>=0; c--)
		{
			if(m_intervals.at(c).contains(row))
				return m_values.at(c);
		}
		return T();
	}

	void insertRows(int before, int count)
	{
		QVector< Interval<int> > temp_list;
		// first: split all intervals that contain 'before'
		for(int c=0; c<m_intervals.size(); c++)
		{
			if(m_intervals.at(c).contains(before))
			{
				temp_list = Interval<int>::split(m_intervals.at(c), before);
				m_intervals.replace(c, temp_list.at(0));
				if(temp_list.size()>1)
				{
					m_intervals.insert(c, temp_list.at(1));
					m_values.insert(c, m_values.at(c));
					c++;
				}

			}
		}
		// second: translate all intervals that start at 'before' or later
		for(int c=0; c<m_intervals.size(); c++)
		{
			if(m_intervals.at(c).start() >= before)
				m_intervals[c].translate(count);
		}

	}

	void removeRows(int first, int count)
	{
		QVector< Interval<int> > temp_list;
		Interval<int> i(first, first+count-1);
		// first: remove the relevant rows from all intervals
		for(int c=0; c<m_intervals.size(); c++)
		{
			temp_list = Interval<int>::subtract(m_intervals.at(c), i);
			if(temp_list.isEmpty())
			{
				m_intervals.removeAt(c);
				m_values.removeAt(c--);
			}
			else
			{
				m_intervals.replace(c, temp_list.at(0));
				if(temp_list.size()>1)
				{
					m_intervals.insert(c, temp_list.at(1));
					m_values.insert(c, m_values.at(c));
					c++;
				}
			}
		}
		// second: translate all intervals that start at 'first+count' or later
		for(int c=0; c<m_intervals.size(); c++)
		{
			if(m_intervals.at(c).start() >= first+count)
				m_intervals[c].translate(-count);
		}
		// third: merge as many intervals as possible
		QVector<T> values_copy = m_values;
		QVector< Interval<int> > intervals_copy = m_intervals;
		m_values.clear();
		m_intervals.clear();
		for(int c=0; c<intervals_copy.size(); c++)
		{
			i = intervals_copy.at(c);
			T value = values_copy.at(c);
			for(int cc=0; cc<m_intervals.size(); cc++)
			{
				if( m_intervals.at(cc).touches(i) &&
						m_values.at(cc) == value )
				{
					m_intervals.replace(cc, Interval<int>::merge(m_intervals.at(cc),i));
					return;
				}
			}
			// if it could not be merged, just append it
			m_intervals.append(i);
			m_values.append(value);
		}
	}

	void clear() { m_values.clear(); m_intervals.clear(); }

	QVector< Interval<int> > intervals() const { return m_intervals; }
	QVector<T> values() const { return m_values; }

private:
	QVector<T> m_values;
	QVector< Interval<int> > m_intervals;
};

//! A class representing an interval-based attribute (bool version)
template<> class IntervalAttribute<bool>
{
	public:
		IntervalAttribute<bool>() {}
		IntervalAttribute<bool>(const QVector< Interval<int> >& intervals) : m_intervals(intervals) {}

		void setValue(const Interval<int>& i, bool value=true)
		{
			if(value)
			{
				foreach(const Interval<int>& iv, m_intervals)
					if(iv.contains(i))
						return;

				Interval<int>::mergeIntervalIntoList(&m_intervals, i);
			} else { // unset
				Interval<int>::subtractIntervalFromList(&m_intervals, i);
			}
		}

		void setValue(int row, bool value)
		{
			setValue(Interval<int>(row, row), value);
		}

		bool isSet(int row) const
		{
			foreach(Interval<int> iv, m_intervals)
				if(iv.contains(row))
					return true;
			return false;
		}

		bool isSet(const Interval<int>& i) const
		{
			foreach(Interval<int> iv, m_intervals)
				if(iv.contains(i))
					return true;
			return false;
		}

		void insertRows(int before, int count)
		{
			QVector< Interval<int> > temp_list;
			int c;
			// first: split all intervals that contain 'before'
			for(c=0; c<m_intervals.size(); c++)
			{
				if(m_intervals.at(c).contains(before))
				{
					temp_list = Interval<int>::split(m_intervals.at(c), before);
					m_intervals.replace(c, temp_list.at(0));
					if(temp_list.size()>1)
						m_intervals.insert(c++, temp_list.at(1));

				}
			}
			// second: translate all intervals that start at 'before' or later
			for(c=0; c<m_intervals.size(); c++)
			{
				if(m_intervals.at(c).start() >= before)
					m_intervals[c].translate(count);
			}

		}

		void removeRows(int first, int count)
		{
			int c;
			// first: remove the relevant rows from all intervals
			Interval<int>::subtractIntervalFromList(&m_intervals, Interval<int>(first, first+count-1));
			// second: translate all intervals that start at 'first+count' or later
			for(c=0; c<m_intervals.size(); c++)
			{
				if(m_intervals.at(c).start() >= first+count)
					m_intervals[c].translate(-count);
			}
			// third: merge as many intervals as possible
			for(c=m_intervals.size()-1; c>=0; c--)
			{
				Interval<int> iv = m_intervals.takeAt(c);
				int size_before = m_intervals.size();
				Interval<int>::mergeIntervalIntoList(&m_intervals, iv);
				if(size_before == m_intervals.size()) // merge successful
					c--;
			}
		}

		QVector< Interval<int> > intervals() const { return m_intervals; }

		void clear() { m_intervals.clear(); }

	private:
		QVector< Interval<int> > m_intervals;
};

#endif
