package de.westnordost.streetcomplete.quests.opening_hours;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/** aka number range / Zahlenraum */
public class NumberSystem
{
private int min, max;
public NumberSystem(int min, int max)
{
if(max < min) throw new IllegalArgumentException("min must be smaller or equal to max");
this.min = min;
this.max = max;
}
public int getSize(CircularSection section)
{
int s = Math.max(min,section.getStart());
int e = Math.min(section.getEnd(), max);
if(s <= e) return e-s+1;
return max-s+1 + e-min+1;
}
/** @return the complemented of the given ranges */
public List<CircularSection> complemented(Collection<CircularSection> ranges)
{
List<CircularSection> rangeList = canonicalize(ranges);
List<CircularSection> complementList = new ArrayList<>();
int start = min;
for(CircularSection range : rangeList)
{
if (range.getStart() > start)
{
complementList.add(new CircularSection(start, range.getStart() - 1));
}
start = Math.max(start, range.getEnd() + 1);
if(start > max) break;
}
if(start <= max) complementList.add(new CircularSection(start, max));
mergeFirstAndLastSection(complementList);
return complementList;
}
public List<CircularSection> merged(List<CircularSection> ranges)
{
List<CircularSection> result = new ArrayList<>(ranges);
Collections.sort(result);
mergeFirstAndLastSection(result);
return result;
}
private void mergeFirstAndLastSection(List<CircularSection> ranges)
{
if(ranges.size() > 1)
{
int lastIndex = ranges.size() - 1;
CircularSection first = ranges.get(0);
CircularSection last = ranges.get(lastIndex);
if(first.getStart() == min && last.getEnd() == max)
{
ranges.remove(lastIndex);
ranges.remove(0);
ranges.add(mergeAlongBounds(first, last));
}
}
}
private CircularSection mergeAlongBounds(CircularSection lowerSection, CircularSection upperSection)
{
return new CircularSection(upperSection.getStart(), lowerSection.getEnd());
}
private List<CircularSection> splitAlongBounds(CircularSection range)
{
ArrayList<CircularSection> result = new ArrayList<>(2);
CircularSection upperSection = new CircularSection(range.getStart(), max);
if(!upperSection.loops()) result.add(upperSection);
CircularSection lowerSections = new CircularSection(min, range.getEnd());
if(!lowerSections.loops()) result.add(lowerSections);
return result;
}
private List<CircularSection> canonicalize(Collection<CircularSection> ranges)
{
// to calculate with circular StartEnds is so complicated, lets dumb it down here
ArrayList<CircularSection> rangeList = new ArrayList<>();
for(CircularSection range : ranges)
{
if(range.loops())
{
rangeList.addAll(splitAlongBounds(range));
}
// leave out those which are not in the max range anyway
else if(min <= range.getEnd() || max >= range.getStart())
{
rangeList.add(range);
}
}
Collections.sort(rangeList);
return rangeList;
}
}