


//
// fieldRef, staticIndex
//

[T, FIELD | RecordWithField?(T, FIELD)]
inline overload fieldRef(x:T, static FIELD) {
    return ref recordFieldRefByName(x, static FIELD);
}

[T, i | Record?(T)]
inline overload staticIndex(x:T, static i) =
    ref recordFieldRef(x, static SizeT(i));



//
// RecordFieldType, RecordFieldTypes
//

[T,i | Record?(T)]
RecordFieldType(static T, static i) = Type(staticIndex(null(T)^, static i));

[T | Record?(T)]
RecordFieldTypes(static T) = ..Type(..recordFields(null(T)^));



//
// RecordFields
//

RecordFields(R) {
    alias n = RecordFieldCount(R);
    var f = i => [RecordFieldName(R,i), RecordFieldType(R,i)];
    return ..mapValues(f, ..staticIntegers(static n));
}

RecordFieldNames(R) = ..mapValues(
    i => RecordFieldName(R, i),
    ..staticIntegers(static RecordFieldCount(R))
);



//
// RegularRecord?
//
// true if record's value semantics is defined in terms of its fields
//

[T]
RegularRecord?(static T) = false;

[T | Record?(T)]
overload RegularRecord?(static T) = true;



//
// basic constructor
//

[T, ..A | Record?(T) and (Tuple[..RecordFieldTypes(T)] == Tuple[..A])]
alias overload T(..args:A) --> returned:T {
    ..recordFields(returned) <-- ..args;
}



//
// default constructor
//

[T | RegularRecord?(T)]
overload T() --> returned:T {
    initializeMulti(..recordFields(returned));
}

[..A]
private initializeMulti(..args:A) {
    ..args <-- ..initializeTypes(..A);
}

private define initializeTypes;
inline overload initializeTypes(A, ..B) = A(), ..initializeTypes(..B);
inline overload initializeTypes() = ;



//
// copy constructor
//

[T | RegularRecord?(T) and not BitwiseCopiedType?(T)]
overload T(src:T) --> returned:T {
    ..recordFields(returned) <-- ..recordFields(src);
}



//
// moveUnsafe, resetUnsafe
//

[T | RegularRecord?(T) and not BitwiseMovedType?(T)]
overload moveUnsafe(src:T) --> returned:T {
    alias n = RecordFieldCount(T);
    ..for (i in staticIntegers(static n))
        staticIndex(returned, i) <-- moveUnsafe(staticIndex(src, i));
}

[T | RegularRecord?(T) and not NotResetType?(T)]
overload resetUnsafe(a:T) {
    ..for (x in recordFields(a))
        resetUnsafe(x);
}



//
// destroy
//

[T | RegularRecord?(T) and not NotDestroyedType?(T)]
overload destroy(a:T) {
    ..for (x in recordFields(a))
        destroy(x);
}



//
// assign
//

[T | RegularRecord?(T) and not BitwiseAssignedType?(T)]
overload assign(ref dest:T, ref src:T) {
    alias n = RecordFieldCount(T);
    ..for (i in staticIntegers(static n))
        assign(staticIndex(dest, i), staticIndex(src, i));
}



//
// equals?
//

[T | RegularRecord?(T)]
overload equals?(a:T, b:T) {
    alias n = RecordFieldCount(T);
    ..for (i in staticIntegers(static n)) {
        if (not equals?(staticIndex(a, i), staticIndex(b, i)))
            return false;
    }
    return true;
}



//
// lesser?
//

[T | RegularRecord?(T)]
overload lesser?(a:T, b:T) {
    alias n = RecordFieldCount(T);
    ..for (i in staticIntegers(static n)) {
        ref av = staticIndex(a, i);
        ref bv = staticIndex(b, i);
        if (lesser?(av, bv))
            return true;
        if (lesser?(bv, av))
            return false;
    }
    return false;
}



//
// RecordWithProperties
//
// special type known to the compiler.
// can be used in computed records to set custom properties.
//

record RecordWithProperties[Properties, Fields]();

recordWithProperties(properties, ..fields) {
    var fieldsTuple = Tuple(..fields);

    // ensure they're all static
    static properties;
    static fieldsTuple;

    return RecordWithProperties[Type(properties), Type(fieldsTuple)]();
}

recordWithProperty(property, ..fields) =
    recordWithProperties(Tuple(property), ..fields);

recordWithPredicate(predicate, ..fields) =
    recordWithProperty([predicate, static true], ..fields);
