

//
// type predicates
//

[T]
overload ContiguousCoordinate?(static Pointer[T]) = true;



//
// null?
//

define null?;



//
// Pointer - constructor
//

[T]
inline overload Pointer[T]() = null(T);



//
// Pointer - convert to/from integers and other pointers
//

[I,T|Integer?(I)]
inline overload I(a:Pointer[T]) = pointerToInt(I, a);

[I,T|Integer?(I)]
inline overload Pointer[T](a:I) = intToPointer(T, a);

[DEST, SRC]
inline overload Pointer[DEST](a:Pointer[SRC]) =
    pointerCast(Pointer[DEST], a);



//
// Pointer - assign, equals?, lesser?
//

[T]
inline overload equals?(a:Pointer[T], b:Pointer[T]) {
    return pointerEquals?(a, b);
}

[T]
inline overload lesser?(a:Pointer[T], b:Pointer[T]) {
    return pointerLesser?(a, b);
}



//
// Pointer - arithmetic
//

[T,I|Integer?(I)]
inline overload add(p:Pointer[T], i:I) {
    return pointerOffset(p, i);
}

[T,I|Integer?(I)]
inline overload add(i:I, p:Pointer[T]) {
    return pointerOffset(p, i);
}

[T,I|Integer?(I)]
inline overload subtract(p:Pointer[T], i:I) {
    return pointerOffset(p, -PtrInt(i));
}

[T]
inline overload subtract(a:Pointer[T], b:Pointer[T]) {
    return (PtrInt(a) - PtrInt(b)) / PtrInt(TypeSize(T));
}

[T]
inline overload inc(a:Pointer[T]) {
    a += 1;
}

[T]
inline overload dec(a:Pointer[T]) {
    a -= 1;
}



//
// Pointer - null, null?, dereference, index
//

[T]
inline null(static T) = Pointer[T](0);

[T]
inline overload null?(x:Pointer[T]) = (x == null(T));

[T]
inline overload dereference(p:Pointer[T]) = ref pointerDereference(p);

[T, I | Integer?(I)]
inline overload index(p:Pointer[T], i:I) = ref (p + i)^;



//
// CodePointer?
//

CodePointer?(x) = false;
[In,Out] overload CodePointer?(static CodePointer[In,Out]) = true;



//
// CodePointer - constructor
//

[In, Out]
inline overload CodePointer[In,Out]() = CodePointer[In,Out](0);



//
// CodePointer - convert to/from other pointers/integers
//

[T, In, Out]
inline overload Pointer[T](a:CodePointer[In,Out]) = pointerCast(Pointer[T], a);

[T, In, Out]
inline overload CodePointer[In,Out](a:Pointer[T]) =
    pointerCast(CodePointer[In,Out], a);

[In1, Out1, In2, Out2]
inline overload CodePointer[In1,Out1](a:CodePointer[In2,Out2]) =
    pointerCast(CodePointer[In1,Out1], a);

[In, Out, I | Integer?(I)]
inline overload CodePointer[In,Out](i:I) = CodePointer[In,Out](Pointer[Int8](i));

[In, Out, I | Integer?(I)]
inline overload I(a:CodePointer[In,Out]) = I(Pointer[Int8](a));



//
// CodePointer - equals?, lesser?, null?

[In, Out]
inline overload equals?(a:CodePointer[In,Out], b:CodePointer[In,Out]) {
    return equals?(RawPointer(a), RawPointer(b));
}

[In, Out]
inline overload lesser?(a:CodePointer[In,Out], b:CodePointer[In,Out]) {
    return lesser?(RawPointer(a), RawPointer(b));
}

[In, Out]
inline overload null?(p:CodePointer[In,Out]) = null?(RawPointer(p));



//
// CCodePointer - constructor
//

[CP | CCodePointer?(CP)]
inline overload CP() = CP(0);



//
// CCodePointer - convert to/from other pointers/integers
//

[T, CP | CCodePointer?(CP)]
inline overload Pointer[T](a:CP) = pointerCast(Pointer[T], a);

[T, CP | CCodePointer?(CP)]
inline overload CP(a:Pointer[T]) = pointerCast(CP, a);

[CP1, CP2 | CCodePointer?(CP1) and CCodePointer?(CP2)]
inline overload CP1(a:CP2) = pointerCast(CP1, a);

[CP,I | CCodePointer?(CP) and Integer?(I)]
inline overload CP(i:I) = CP(Pointer[Int8](i));

[CP,I | CCodePointer?(CP) and Integer?(I)]
inline overload I(a:CP) = I(Pointer[Int8](a));



//
// CCodePointer - equals?, lesser?, null?
//

[CP | CCodePointer?(CP)]
inline overload equals?(a:CP, b:CP) {
    return equals?(RawPointer(a), RawPointer(b));
}

[CP | CCodePointer?(CP)]
inline overload lesser?(a:CP, b:CP) {
    return lesser?(RawPointer(a), RawPointer(b));
}

[CP | CCodePointer?(CP)]
inline overload null?(p:CP) = null?(RawPointer(p));



//
// CCodePointer - call
//

[..In, ..Out]
inline overload call(ptr:CCodePointer[[..In], [..Out]], ..args:In) =
    ..callCCodePointer(ptr, ..args);

[..In, ..Out, ..A | (countValues(..A) >= countValues(..In))
                       and (Tuple[..takeValues(static countValues(..In), ..A)] == Tuple[..In])]
inline overload call(ptr:VarArgsCCodePointer[[..In], [..Out]], ..args:A) =
    ..callCCodePointer(ptr, ..args);

[..In, ..Out]
inline overload call(ptr:FastCallCodePointer[[..In], [..Out]], ..args:In) =
    ..callCCodePointer(ptr, ..args);

[..In, ..Out]
inline overload call(ptr:StdCallCodePointer[[..In], [..Out]], ..args:In) =
    ..callCCodePointer(ptr, ..args);

[..In, ..Out]
inline overload call(ptr:LLVMCodePointer[[..In], [..Out]], ..args:In) =
    ..callCCodePointer(ptr, ..args);


//
// construct code pointers from static callables
//

[..In, ..Out, fn | CallDefined?(fn, ..In)]
inline overload CodePointer[[..In], [..Out]](static fn) : CodePointer[[..In], [..Out]]
    = makeCodePointer(fn, ..In);

[..In, ..Out, fn | CallDefined?(fn, ..In)]
inline overload CCodePointer[[..In], [..Out]](static fn) : CCodePointer[[..In], [..Out]]
    = makeCCodePointer(fn, ..In);
