1 module dud.resolve.confs;
2 
3 import std.algorithm.iteration : each, filter, map;
4 import std.algorithm.searching : all, any, canFind;
5 import std.array : array, empty, front;
6 import std.format : format;
7 import std.typecons : Flag;
8 import std.range : chain;
9 import std.stdio;
10 
11 import dud.resolve.positive;
12 import dud.resolve.conf;
13 import dud.semver.versionrange;
14 
15 @safe pure:
16 
17 private immutable nothing = Conf("", IsPositive.no);
18 
19 struct Confs {
20 	import std.algorithm.iteration : map;
21 
22 @safe pure:
23 	Conf[] confs;
24 
25 	this(const(Conf)[] input) {
26 		import std.algorithm.iteration : each;
27 		this.insert(input);
28 	}
29 
30 	Confs dup() const {
31 		import std.array : array;
32 		return Confs(this.confs.map!(it => it.dup).array);
33 	}
34 
35 	void insert(const(Conf[]) cs) {
36 		cs.each!(it => this.insert(it));
37 	}
38 
39 	void insert(const(Conf) c) {
40 		import std.algorithm.sorting : sort;
41 
42 		// Negativ all means nothing can be added anymore
43 		if(!this.confs.empty && this.confs.front == nothing) {
44 			return;
45 		}
46 
47 		// Got a new nothing
48 		if(c == nothing) {
49 			this.confs = [ nothing.dup() ];
50 			return;
51 		}
52 
53 		// Already in ignore
54 		if(canFind(this.confs, c)) {
55 			return;
56 		}
57 
58 		this.confs ~= Conf(c.conf, c.isPositive);
59 		this.confs = normalize(this.confs);
60 	}
61 
62 	Confs invert() const {
63 		return this.confs.map!(it => it.invert()).array.Confs;
64 	}
65 
66 	bool opEquals(const(Confs) other) const {
67 		return this.confs.length == other.confs.length
68 			&& this.confs.all!(c => canFind(other.confs, c));
69 	}
70 }
71 
72 private Conf[] normalize(Conf[] toNorm) {
73 	Conf[] ret;
74 	outer: foreach(idx, it; toNorm) {
75 		Conf inv = it.invert();
76 		foreach(jdx, jt; toNorm) {
77 			if(idx != jdx && inv == jt) {
78 				continue outer;
79 			}
80 		}
81 		ret ~= it;
82 	}
83 	const everything = Conf("", IsPositive.yes);
84 	if(canFind(ret, everything)) {
85 		ret = ret.filter!(it => it == everything
86 				|| it.isPositive == IsPositive.no)
87 			.array;
88 	}
89 	return ret;
90 }
91 
92 /*
93 private bool isOkayToInsert(const(Conf[]) arr, const(Conf) it) {
94 	const isNeg = it.isPositive == IsPositive.no;
95 	const negPr = !isNegativPresent(arr, it);
96 	const cf = !canFind(arr, it);
97 
98 	return (isNeg || negPr) && cf;
99 }
100 
101 private bool isNegativPresent(const(Conf[]) arr, const(Conf) it) {
102 	return arr
103 		.filter!(a => a.isPositive == IsPositive.no)
104 		.any!(a => a.conf == it.conf);
105 }
106 
107 private Conf[] normalize(Conf[] arr) {
108 	return arr
109 		.filter!(
110 			it => !isNegativPresent(arr, it) || it.isPositive == IsPositive.no)
111 		.array;
112 }*/
113 
114 bool allowsAll(const(Confs) a, const(Confs) b) {
115 	return b.confs.all!(it => allowsAny(a, it));
116 }
117 
118 bool allowsAny(const(Confs) a, const(Confs) b) {
119 	return b.confs.empty || b.confs.any!(it => allowsAny(a, it));
120 }
121 
122 bool allowsAll(const(Confs) a, const(Conf) b) {
123 	static import dud.resolve.conf;
124 	return !a.confs.empty
125 		&& a.confs.any!(it => dud.resolve.conf.allowsAll(it, b));
126 }
127 
128 bool allowsAny(const(Confs) a, const(Conf) b) {
129 	static import dud.resolve.conf;
130 	const t = !a.confs.empty
131 		&& a.confs.any!(it => dud.resolve.conf.allowsAny(it, b));
132 	return t;
133 }
134 
135 // TODO this one is having some problems
136 Confs intersectionOf(const(Confs) a, const(Confs) b) {
137 	import std.algorithm.setops : cartesianProduct;
138 	import std.algorithm.iteration : joiner;
139 
140 	Confs ret;
141 	foreach(aIt; a.confs) {
142 		foreach(bIt; b.confs) {
143 			Confs i = dud.resolve.conf.intersectionOf(aIt, bIt);
144 			foreach(abIt; i.confs) {
145 				ret.insert(abIt);
146 			}
147 		}
148 	}
149 	return ret;
150 }
151 
152 Confs differenceOf(const(Confs) a, const(Confs) b) {
153 	Conf[] neg = b.confs
154 		.map!(it => it.isPositive == IsPositive.no
155 				? it.dup
156 				: Conf(it.conf, IsPositive.no))
157 		.array;
158 
159 	Conf[] ret = a.confs.map!(it => it.dup).array ~ neg;
160 	return Confs(ret);
161 }
162 
163 /** Return if a is a subset of b, or if a and b are disjoint, or
164 if a and b overlap
165 */
166 SetRelation relation(const(Confs) a, const(Confs) b) pure {
167 	if(b.allowsAll(a)) {
168 		return SetRelation.subset;
169 	}
170 	if(b.allowsAny(a)) {
171 		return SetRelation.overlapping;
172 	}
173 	return SetRelation.disjoint;
174 }