value Pair {
	Nat key;
	Str value;

	init() {
		init {
			key = 1;
			value = "default";
		}
	}

	init(Nat key, Str value) {
		init {
			key = key;
			value = value;
		}
	}

	void add(Nat toAdd) {
		key += toAdd;
	}

	void toS(StrBuf to) {
		to << "{ key: " << key << ", value: " << value << " }";
	}
}

void valueFunction(Pair p) {
	p.add(10);
	print("Inside the function: ${p}");
}

void valueMain() {
	Pair p(5, "test");
	print("Key: ${p.key}");
	print("Value: ${p.value}");
	print("Pair: ${p}");

	Pair copy = p;
	copy.add(5);

	print("Copied: ${copy}");
	print("Original: ${p}");

	valueFunction(p);
	print("After calling the function: ${p}");
}

class ClassPair {
	private Nat myKey;
	Str value;

	init(Nat key, Str value) {
		init {
			myKey = key;
			value = value;
		}
	}

	Nat key() { myKey; }

	assign key(Nat newKey) {
		myKey = newKey;
	}

	void add(Nat toAdd) {
		myKey += toAdd;
	}

	void toS(StrBuf to) {
		to << "{ key: " << myKey << ", value: " << value << " }";
	}
}

void classFunction(ClassPair p) {
	p.add(10);
}

void classMain() {
	ClassPair p(5, "test");
	print("Pair: ${p}");

	ClassPair copy = p;
	copy.add(5);

	print("Copied: ${copy}");
	print("Original: ${p}");
	if (p is copy) {
		print("p and copy are the same object");
	} else {
		print("p and copy are different objects");
	}

	copy = ClassPair(p); // Or: ClassPair copy(p);
	copy.add(5);
	print("Copied: ${copy}");
	print("Original: ${p}");
	if (p is copy) {
		print("p and copy are the same object");
	} else {
		print("p and copy are different objects");
	}

	classFunction(p);
	print("After calling function: ${p}");
}

class CountedPair extends ClassPair {
	Nat count;

	init(Nat key, Str value) {
		init(key, value) {
			count = 0;
		}
	}

	assign key(Nat newKey) : override {
		count++;
		super:key(newKey);
	}

	void add(Nat toAdd) : override {
		count++;
		super:add(toAdd);
	}

	void toS(StrBuf to) : override {
		super:toS(to);
		to << " (count = " << count << ")";
	}
}

void inheritanceMain() {
	CountedPair p(5, "test");
	classFunction(p);
	print("After calling the function: ${p}");
}

void refactorMain() {
	CountedPair p(1, "test");
	p.key = 2 * p.key + 1;
	print("Count is now: ${p.count}");
}

Nat count(Str s, Char ch) {
	Nat result = 0;
	for (c in s) {
		if (c == ch)
			result++;
	}
	return result;
}

void main() {
	print("-- Value --");
	valueMain();

	print("-- Class --");
	classMain();

	print("-- With inheritance --");
	inheritanceMain();

	print("-- Refactor --");
	refactorMain();

	print("-- Uniform call syntax ---");
	Nat c = "hello".count('l');
	print("Count: ${c}");
}
