import { default as Big_2 } from 'big.js';

/**
 * Creates a logical `and` filter condition from two or more filter conditions.
 *
 * @param args The {@link FilterCondition}s to be combined with `and`.
 *
 * @return An {@link AndCondition} combining the given filter conditions.
 *
 * @throws Error if `args` contains fewer than two elements, or if one of the elements is not a valid
 * {@link FilterCondition} object.
 */
export declare function and(...args: FilterCondition[]): AndCondition;

/** A logical `and` condition which takes two or more {@link FilterCondition} arguments. */
declare type AndCondition = MultiaryFunctionExpression<"and", FilterCondition>;

/**
 * Creates an association expression.
 *
 * @param associationId The id of the association as specified in the {@link ListAssociationValue.id} property.
 *
 * @return An {@link AssociationExpression} representing the association with the given id.
 *
 * @throws Error if the association id is not valid.
 */
export declare function association(associationId: ListAssociationId): AssociationExpression;

/**
 * An expression which represents an association.
 */
declare interface AssociationExpression {
    /** Identifies this expression as an association expression. */
    readonly type: "association";
    /** The association id as specified in the {@link ListAssociationValue.id} property. */
    readonly associationId: ListAssociationId;
}

declare type AssociationType = "Reference" | "ReferenceSet";

/**
 * Creates an attribute expression.
 *
 * @param attributeId The id of the attribute as specified in the {@link ListAttributeValue.id} property.
 *
 * @return An {@link AttributeExpression} representing the attribute with the given id.
 *
 * @throws Error if the attribute id is not valid.
 */
export declare function attribute(attributeId: ListAttributeId): AttributeExpression;

/**
 * An expression which represents an attribute.
 */
declare interface AttributeExpression {
    /** Identifies this expression as an attribute expression. */
    readonly type: "attribute";
    /** The attribute id as specified in the {@link ListAttributeValue.id} property. */
    readonly attributeId: ListAttributeId;
}

declare type BigJS_2 = Big_2;

declare interface BinaryFunctionExpression<TName extends FilterFunction, TArg1 extends FilterExpression, TArg2 = TArg1> extends FunctionExpression<TName> {
    readonly arg1: TArg1;
    readonly arg2: TArg2;
}

declare type BooleanFunction = "and" | "or" | "not";

declare type ComparisonFunction = EqualityFunction | ">" | ">=" | "<" | "<=";

/**
 * Creates a `contains` filter condition from a attribute or association expression and a literal expression.
 * This type of filter condition is only available for attributes of type `String`, `Integer`, `Long`, `Decimal`
 * and `AutoNumber` (in which case the literal must be of type `String`) and for associations of type `ReferenceSet`
 * (in which case the literal must be of type `Reference` or `ReferenceSet`).
 *
 * @param arg1 The {@link AttributeExpression} or {@link AssociationExpression} to compare.
 * @param arg2 The {@link LiteralExpression} to compare.
 *
 * @return A {@link ContainsCondition} which checks if the attribute or association contains the given literal.
 *
 * @throws Error if `arg1` is not a valid {@link ValueExpression}, if `arg2` is not a valid {@link LiteralExpression},
 * or if the types of the arguments are invalid or incompatible with each other.
 */
export declare function contains(arg1: AttributeExpression | AssociationExpression, arg2: LiteralExpression): ContainsCondition;

/** An `contains` condition which takes a {@link AttributeExpression} or a {@link AssociationExpression} and a {@link LiteralExpression} as arguments. */
declare type ContainsCondition = BinaryFunctionExpression<"contains", AttributeExpression | AssociationExpression, LiteralExpression>;

declare type DayComparisonFunction = "day:=" | "day:!=" | "day:>" | "day:>=" | "day:<" | "day:<=";

/**
 * Creates an `dayEquals` filter condition from an attribute expression and a literal expression.
 * This type of filter condition is available for date/time attributes only.
 *
 * @param arg1 The {@link AttributeExpression} to compare.
 * @param arg2 The {@link LiteralExpression} to compare.
 *
 * @return An {@link DayEqualsCondition} which checks if the attribute falls within the same day as the literal value.
 *
 * @throws Error if `arg1` is not a valid {@link AttributeExpression} or if `arg2` is not a valid
 * {@link LiteralExpression}, or if the types of the arguments are not date/time.
 */
export declare function dayEquals(arg1: AttributeExpression, arg2: LiteralExpression): DayEqualsCondition;

/** An `dayEquals` (`day:=`) condition which takes a {@link AttributeExpression} and a {@link LiteralExpression} as arguments. */
declare type DayEqualsCondition = BinaryFunctionExpression<"day:=", AttributeExpression, LiteralExpression>;

/**
 * Creates an `dayGreaterThan` filter condition from an attribute expression and a literal expression.
 * This type of filter condition is available for date/time attributes only.
 *
 * @param arg1 The {@link AttributeExpression} to compare.
 * @param arg2 The {@link LiteralExpression} to compare.
 *
 * @return An {@link DayGreaterThanCondition} which checks if the attribute falls on a later date than the literal value.
 *
 * @throws Error if `arg1` is not a valid {@link AttributeExpression} or if `arg2` is not a valid
 * {@link LiteralExpression}, or if the types of the arguments are not date/time.
 */
export declare function dayGreaterThan(arg1: AttributeExpression, arg2: LiteralExpression): DayGreaterThanCondition;

/** A `dayGreaterThan` (`day:>`) condition which takes a {@link AttributeExpression} and a {@link LiteralExpression} as arguments. */
declare type DayGreaterThanCondition = BinaryFunctionExpression<"day:>", AttributeExpression, LiteralExpression>;

/**
 * Creates an `dayGreaterThanOrEqual` filter condition from an attribute expression and a literal expression.
 * This type of filter condition is available for date/time attributes only.
 *
 * @param arg1 The {@link AttributeExpression} to compare.
 * @param arg2 The {@link LiteralExpression} to compare.
 *
 * @return An {@link DayGreaterThanOrEqualCondition} which checks if the attribute falls on a day which is the same as
 * or later than the literal value.
 *
 * @throws Error if `arg1` is not a valid {@link AttributeExpression} or if `arg2` is not a valid
 * {@link LiteralExpression}, or if the types of the arguments are not date/time.
 */
export declare function dayGreaterThanOrEqual(arg1: AttributeExpression, arg2: LiteralExpression): DayGreaterThanOrEqualCondition;

/** A `dayGreaterThanOrEqual` (`day:>=`) condition which takes a {@link AttributeExpression} and a {@link LiteralExpression} as arguments. */
declare type DayGreaterThanOrEqualCondition = BinaryFunctionExpression<"day:>=", AttributeExpression, LiteralExpression>;

/**
 * Creates an `dayLessThan` filter condition from an attribute expression and a literal expression.
 * This type of filter condition is available for date/time attributes only.
 *
 * @param arg1 The {@link AttributeExpression} to compare.
 * @param arg2 The {@link LiteralExpression} to compare.
 *
 * @return An {@link DayLessThanCondition} which checks if the attribute falls on an earlier date than the literal value.
 *
 * @throws Error if `arg1` is not a valid {@link AttributeExpression} or if `arg2` is not a valid
 * {@link LiteralExpression}, or if the types of the arguments are not date/time.
 */
export declare function dayLessThan(arg1: AttributeExpression, arg2: LiteralExpression): DayLessThanCondition;

/** A `dayLessThan` (`day:<`) condition which takes a {@link AttributeExpression} and a {@link LiteralExpression} as arguments. */
declare type DayLessThanCondition = BinaryFunctionExpression<"day:<", AttributeExpression, LiteralExpression>;

/**
 * Creates an `dayLessThanOrEqual` filter condition from an attribute expression and a literal expression.
 * This type of filter condition is available for date/time attributes only.
 *
 * @param arg1 The {@link AttributeExpression} to compare.
 * @param arg2 The {@link LiteralExpression} to compare.
 *
 * @return An {@link DayLessThanOrEqualCondition} which checks if the attribute falls on a day which is the same as
 * or earlier than the literal value.
 *
 * @throws Error if `arg1` is not a valid {@link AttributeExpression} or if `arg2` is not a valid
 * {@link LiteralExpression}, or if the types of the arguments are not date/time.
 */
export declare function dayLessThanOrEqual(arg1: AttributeExpression, arg2: LiteralExpression): DayLessThanOrEqualCondition;

/** A `dayLessThanOrEqual` (`day:<=`) condition which takes a {@link AttributeExpression} and a {@link LiteralExpression} as arguments. */
declare type DayLessThanOrEqualCondition = BinaryFunctionExpression<"day:<=", AttributeExpression, LiteralExpression>;

/**
 * Creates an `dayNotEqual` filter condition from an attribute expression and a literal expression.
 * This type of filter condition is available for date/time attributes only.
 *
 * @param arg1 The {@link AttributeExpression} to compare.
 * @param arg2 The {@link LiteralExpression} to compare.
 *
 * @return An {@link DayNotEqualCondition} which checks if the attribute does not fall within the same day as the literal value.
 *
 * @throws Error if `arg1` is not a valid {@link AttributeExpression} or if `arg2` is not a valid
 * {@link LiteralExpression}, or if the types of the arguments are not date/time.
 */
export declare function dayNotEqual(arg1: AttributeExpression, arg2: LiteralExpression): DayNotEqualCondition;

/** A `dayNotEqual` (`day:!=`) condition which takes a {@link AttributeExpression} and a {@link LiteralExpression} as arguments. */
declare type DayNotEqualCondition = BinaryFunctionExpression<"day:!=", AttributeExpression, LiteralExpression>;

/**
 * Returns a literal expression which represents empty, i.e. the value `undefined`.
 * @return A {@link LiteralExpression} with value `undefined`.
 */
export declare function empty(): LiteralExpression;

/**
 * Creates an `endsWith` filter condition from an attribute expression and a literal expression.
 * This type of filter condition is only available for attributes of type `String`, `Integer`, `Long`, `Decimal` and `AutoNumber`.
 *
 * @param arg1 The {@link AttributeExpression} to compare.
 * @param arg2 The {@link LiteralExpression} to compare.
 *
 * @return A {@link EndsWithCondition} which checks if the attribute ends with the given literal.
 *
 * @throws Error if `arg1` is not a valid {@link AttributeExpression}, if `arg2` is not a valid {@link LiteralExpression},
 * or if the types of the arguments are invalid or incompatible with each other.
 */
export declare function endsWith(arg1: AttributeExpression, arg2: LiteralExpression): EndsWithCondition;

/** An `endsWith` condition which takes a {@link AttributeExpression} and a {@link LiteralExpression} as arguments. */
declare type EndsWithCondition = BinaryFunctionExpression<"ends-with", AttributeExpression, LiteralExpression>;

declare type EqualityFunction = "=" | "!=";

/**
 * Creates an `equals` filter condition from two value expressions (an attribute expression, a literal
 * expression or an association expression). This type of filter condition is available for all value types.
 * For reference sets, only a comparison with {@link empty} is supported.
 *
 * @param arg1 The first {@link ValueExpression} to compare.
 * @param arg2 The second {@link ValueExpression} to compare.
 *
 * @return An {@link EqualsCondition} which checks if the two value expressions are equal.
 *
 * @throws Error if one of the arguments is not a valid {@link ValueExpression}, if the types of the arguments are
 * incompatible with each other, or if an enumeration attribute is being compared with a value which is not compatible
 * with the attribute's {@link ListAttributeValue.universe enum universe}.
 */
export declare function equals(arg1: ValueExpression, arg2: ValueExpression): EqualsCondition;

/** An `equals` (`=`) condition which takes two {@link ValueExpression} arguments. */
declare type EqualsCondition = BinaryFunctionExpression<"=", ValueExpression>;

/**
 * Base type for all expression objects which can be used as a filter condition on a data source.
 *
 * @property {"function"} type Identifies the expression as a filter function.
 * @property {string} name The name of the function, which can be used to narrow the type to a specific filter condition
 * type.
 *
 * @example
 * if (filter.type === "function") {
 *     switch (filter.name) {
 *         case "=": // Equals
 *             ...
 *             break;
 *         case "!=": // Not equal
 *             ...
 *             break;
 *         ...
 *     }
 * }
 */
declare type FilterCondition = AndCondition | OrCondition | NotCondition | EqualsCondition | NotEqualCondition | GreaterThanCondition | GreaterThanOrEqualCondition | LessThanCondition | LessThanOrEqualCondition | ContainsCondition | StartsWithCondition | EndsWithCondition | DayEqualsCondition | DayNotEqualCondition | DayGreaterThanCondition | DayGreaterThanOrEqualCondition | DayLessThanCondition | DayLessThanOrEqualCondition;

/**
 * Base type for all expression objects for data source filtering, which can be either a {@link FilterCondition} or a
 * {@link ValueExpression}.
 */
declare type FilterExpression = FilterCondition | ValueExpression;

declare type FilterFunction = BooleanFunction | ComparisonFunction | DayComparisonFunction | StringFunction;

declare interface FunctionExpression<TName extends FilterFunction> {
    readonly type: "function";
    readonly name: TName;
}

/**
 * Creates an `greaterThan` filter condition from two value expressions (either an attribute expression or a literal
 * expression). This type of filter condition is only available for values of type string, number and date/time.
 *
 * @param arg1 The first {@link ValueExpression} to compare.
 * @param arg2 The second {@link ValueExpression} to compare.
 *
 * @return A {@link GreaterThanCondition} which checks if the first value expression is greater than the second.
 *
 * @throws Error if one of the arguments is not a valid {@link ValueExpression}, or if the types of the arguments are
 * invalid or incompatible with each other.
 */
export declare function greaterThan(arg1: ValueExpression, arg2: ValueExpression): GreaterThanCondition;

/** A `greaterThan` (`>`) condition which takes two {@link ValueExpression} arguments. */
declare type GreaterThanCondition = BinaryFunctionExpression<">", ValueExpression>;

/**
 * Creates an `greaterThanOrEqual` filter condition from two value expressions (either an attribute expression or a
 * literal expression). This type of filter condition is only available for values of type string, number and date/time.
 *
 * @param arg1 The first {@link ValueExpression} to compare.
 * @param arg2 The second {@link ValueExpression} to compare.
 *
 * @return A {@link GreaterThanOrEqualCondition} which checks if the first value expression is greater than or equal to the second.
 *
 * @throws Error if one of the arguments is not a valid {@link ValueExpression}, or if the types of the arguments are
 * invalid or incompatible with each other.
 */
export declare function greaterThanOrEqual(arg1: ValueExpression, arg2: ValueExpression): GreaterThanOrEqualCondition;

/** A `greaterThanOrEqual` (`>=`) condition which takes two {@link ValueExpression} arguments. */
declare type GreaterThanOrEqualCondition = BinaryFunctionExpression<">=", ValueExpression>;

declare type GUID_2 = string & {
    __guidTag: any;
};

/**
 * Creates an `lessThan` filter condition from two value expressions (either an attribute expression or a literal
 * expression). This type of filter condition is only available for values of type string, number and date/time.
 *
 * @param arg1 The first {@link ValueExpression} to compare.
 * @param arg2 The second {@link ValueExpression} to compare.
 *
 * @return A {@link LessThanCondition} which checks if the first value expression is less than the second.
 *
 * @throws Error if one of the arguments is not a valid {@link ValueExpression}, or if the types of the arguments are
 * invalid or incompatible with each other.
 */
export declare function lessThan(arg1: ValueExpression, arg2: ValueExpression): LessThanCondition;

/** A `lessThan` (`<`) condition which takes two {@link ValueExpression} arguments. */
declare type LessThanCondition = BinaryFunctionExpression<"<", ValueExpression>;

/**
 * Creates an `lessThanOrEqual` filter condition from two value expressions (either an attribute expression or literal
 * expression). This type of filter condition is only available for values of type string, number and date/time.
 *
 * @param arg1 The first {@link ValueExpression} to compare.
 * @param arg2 The second {@link ValueExpression} to compare.
 *
 * @return A {@link LessThanOrEqualCondition} which checks if the first value expression is less than or equal to the second.
 *
 * @throws Error if one of the arguments is not a valid {@link ValueExpression}, or if the types of the arguments are
 * invalid or incompatible with each other.
 */
export declare function lessThanOrEqual(arg1: ValueExpression, arg2: ValueExpression): LessThanOrEqualCondition;

/** A `lessThanOrEqual` (`<=`) condition which takes two {@link ValueExpression} arguments. */
declare type LessThanOrEqualCondition = BinaryFunctionExpression<"<=", ValueExpression>;

/**
 * The unique id of an association linked to a data source.
 */
declare type ListAssociationId = string & {
    __associationIdTag: never;
};

/**
 * The unique id of an attribute linked to a data source.
 */
declare type ListAttributeId = string & {
    __attributeIdTag: never;
};

/**
 * Creates a literal expression.
 *
 * @param value The {@link LiteralBuilderValue} of the expression.
 *
 * @return A {@link LiteralExpression} with the given value.
 *
 * @throws Error if the value is not a valid literal value type.
 */
export declare function literal(value: LiteralBuilderValue): LiteralExpression;

/** Allowed literal value when calling {@link literal}. */
declare type LiteralBuilderValue = PrimitiveLiteralValue | ObjectItem | ObjectItem[];

/**
 * An expression which represents a literal value.
 *
 * @property {"literal"} type Identifies the expression as an literal expression.
 * @property {LiteralValue} value The literal value of this expression.
 * @property {LiteralType} valueType The type of this expression, which narrows the type of the `value` property.
 *
 * @example
 * if (filter.type === "literal" && filter.valueType == "DateTime") {
 *     const time = filter.value.getTime();
 *     ...
 * }
 */
declare type LiteralExpression = TypedLiteralExpression<"undefined", undefined> | TypedLiteralExpression<"string", string> | TypedLiteralExpression<"boolean", boolean> | TypedLiteralExpression<"DateTime", Date> | TypedLiteralExpression<"Numeric", BigJS_2> | TypedLiteralExpression<"Reference", GUID_2> | TypedLiteralExpression<"ReferenceSet", GUID_2[]>;

/** Possible types for a {@link LiteralExpression}. */
declare type LiteralType = "undefined" | "string" | "boolean" | "DateTime" | "Numeric" | AssociationType;

/** Allowed literal value in a {@link LiteralExpression}. */
declare type LiteralValue = PrimitiveLiteralValue | GUID_2 | GUID_2[];

declare interface MultiaryFunctionExpression<TName extends FilterFunction, TArg extends FilterExpression> extends FunctionExpression<TName> {
    readonly args: readonly TArg[];
}

/**
 * Creates a logical `not` (inversion) filter condition from the given filter condition.
 *
 * @param arg The {@link FilterCondition} to be inverted.
 *
 * @return A {@link NotCondition} which inverts given filter condition.
 *
 * @throws Error if `arg` is not a valid {@link FilterCondition} object.
 */
export declare function not(arg: FilterCondition): NotCondition;

/** A logical `not` (invert) condition which takes a single {@link FilterCondition} argument. */
declare type NotCondition = UnaryFunctionExpression<"not", FilterCondition>;

/**
 * Creates an `notEqual` filter condition from two value expressions (either an attribute expression or a literal
 * expression). This type of filter condition is available for all value types. For reference sets, only a
 * comparison with {@link empty} is supported.
 *
 * @param arg1 The first {@link ValueExpression} to compare.
 * @param arg2 The second {@link ValueExpression} to compare.
 *
 * @return An {@link NotEqualCondition} which checks if the two value expressions are not equal.
 *
 * @throws Error if one of the arguments is not a valid {@link ValueExpression}, if the types of the arguments are
 * incompatible with each other, or if an enumeration attribute is being compared with a value which is not compatible
 * with the attribute's {@link ListAttributeValue.universe enum universe}.
 */
export declare function notEqual(arg1: ValueExpression, arg2: ValueExpression): NotEqualCondition;

/** A `notEqual` (`!=`) condition which takes two {@link ValueExpression} arguments. */
declare type NotEqualCondition = BinaryFunctionExpression<"!=", ValueExpression>;

/**
 * An object item returned by a data source. This object does not provide direct access to any data attributes, but it
 * can be passed to various API functions (like a widgets template).
 *
 * @property id The {@link GUID} of the object.
 */
declare interface ObjectItem {
    id: GUID_2;
}

/**
 * Creates a logical `or` filter condition from two or more filter conditions.
 *
 * @param args The {@link FilterCondition}s to be combined with `or`.
 *
 * @return An {@link OrCondition} combining the given filter conditions.
 *
 * @throws Error if `args` contains fewer than two elements, or if one of the elements is not a valid
 * {@link FilterCondition} object.
 */
export declare function or(...args: FilterCondition[]): OrCondition;

/** A logical `or` condition which takes two or more {@link FilterCondition} arguments. */
declare type OrCondition = MultiaryFunctionExpression<"or", FilterCondition>;

declare type PrimitiveLiteralValue = undefined | string | boolean | Date | BigJS_2;

/**
 * Creates an `startsWith` filter condition from an attribute expression and a literal expression.
 * This type of filter condition is only available for attributes of type `String`, `Integer`, `Long`, `Decimal` and `AutoNumber`.
 *
 * @param arg1 The {@link AttributeExpression} to compare.
 * @param arg2 The {@link LiteralExpression} to compare.
 *
 * @return A {@link StartsWithCondition} which checks if the attribute starts with the given literal.
 *
 * @throws Error if `arg1` is not a valid {@link AttributeExpression}, if `arg2` is not a valid {@link LiteralExpression},
 * or if the types of the arguments are invalid or incompatible with each other.
 */
export declare function startsWith(arg1: AttributeExpression, arg2: LiteralExpression): StartsWithCondition;

/** An `startsWith` condition which takes a {@link AttributeExpression} and a {@link LiteralExpression} as arguments. */
declare type StartsWithCondition = BinaryFunctionExpression<"starts-with", AttributeExpression, LiteralExpression>;

declare type StringFunction = "contains" | "starts-with" | "ends-with";

declare interface TypedLiteralExpression<TType extends LiteralType, TVal extends LiteralValue> {
    readonly type: "literal";
    readonly value: TVal;
    readonly valueType: TType;
}

declare interface UnaryFunctionExpression<TName extends FilterFunction, TArg extends FilterExpression> extends FunctionExpression<TName> {
    readonly arg: TArg;
}

/**
 * A value expression can be used as an argument for various comparison filter functions.
 *
 * @property {"attribute" | "literal" | "association"} type Identifies the expression as an {@link AttributeExpression}, {@link LiteralExpression} or
 * {@link AssociationExpression}.
 */
declare type ValueExpression = AttributeExpression | LiteralExpression | AssociationExpression;

export { }
