import { default as Big_2 } from 'big.js';

/** A logical `and` condition which takes two or more {@link FilterCondition} arguments. */
export declare type AndCondition = MultiaryFunctionExpression<"and", FilterCondition>;

/**
 * An expression which represents an association.
 */
export 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;
}

export declare type AssociationType = "Reference" | "ReferenceSet";

/**
 * An expression which represents an attribute.
 */
export 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 | ">" | ">=" | "<" | "<=";

/** An `contains` condition which takes a {@link AttributeExpression} or a {@link AssociationExpression} and a {@link LiteralExpression} as arguments. */
export declare type ContainsCondition = BinaryFunctionExpression<"contains", AttributeExpression | AssociationExpression, LiteralExpression>;

declare type DayComparisonFunction = "day:=" | "day:!=" | "day:>" | "day:>=" | "day:<" | "day:<=";

/** An `dayEquals` (`day:=`) condition which takes a {@link AttributeExpression} and a {@link LiteralExpression} as arguments. */
export declare type DayEqualsCondition = BinaryFunctionExpression<"day:=", AttributeExpression, LiteralExpression>;

/** A `dayGreaterThan` (`day:>`) condition which takes a {@link AttributeExpression} and a {@link LiteralExpression} as arguments. */
export declare type DayGreaterThanCondition = BinaryFunctionExpression<"day:>", AttributeExpression, LiteralExpression>;

/** A `dayGreaterThanOrEqual` (`day:>=`) condition which takes a {@link AttributeExpression} and a {@link LiteralExpression} as arguments. */
export declare type DayGreaterThanOrEqualCondition = BinaryFunctionExpression<"day:>=", AttributeExpression, LiteralExpression>;

/** A `dayLessThan` (`day:<`) condition which takes a {@link AttributeExpression} and a {@link LiteralExpression} as arguments. */
export declare type DayLessThanCondition = BinaryFunctionExpression<"day:<", AttributeExpression, LiteralExpression>;

/** A `dayLessThanOrEqual` (`day:<=`) condition which takes a {@link AttributeExpression} and a {@link LiteralExpression} as arguments. */
export declare type DayLessThanOrEqualCondition = BinaryFunctionExpression<"day:<=", AttributeExpression, LiteralExpression>;

/** A `dayNotEqual` (`day:!=`) condition which takes a {@link AttributeExpression} and a {@link LiteralExpression} as arguments. */
export declare type DayNotEqualCondition = BinaryFunctionExpression<"day:!=", AttributeExpression, LiteralExpression>;

/** An `endsWith` condition which takes a {@link AttributeExpression} and a {@link LiteralExpression} as arguments. */
export declare type EndsWithCondition = BinaryFunctionExpression<"ends-with", AttributeExpression, LiteralExpression>;

declare type EqualityFunction = "=" | "!=";

/** An `equals` (`=`) condition which takes two {@link ValueExpression} arguments. */
export 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;
 *         ...
 *     }
 * }
 */
export 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}.
 */
export declare type FilterExpression = FilterCondition | ValueExpression;

declare type FilterFunction = BooleanFunction | ComparisonFunction | DayComparisonFunction | StringFunction;

declare interface FunctionExpression<TName extends FilterFunction> {
    readonly type: "function";
    readonly name: TName;
}

/** A `greaterThan` (`>`) condition which takes two {@link ValueExpression} arguments. */
export declare type GreaterThanCondition = BinaryFunctionExpression<">", ValueExpression>;

/** A `greaterThanOrEqual` (`>=`) condition which takes two {@link ValueExpression} arguments. */
export declare type GreaterThanOrEqualCondition = BinaryFunctionExpression<">=", ValueExpression>;

declare type GUID_2 = string & {
    __guidTag: any;
};

/** A `lessThan` (`<`) condition which takes two {@link ValueExpression} arguments. */
export declare type LessThanCondition = BinaryFunctionExpression<"<", ValueExpression>;

/** A `lessThanOrEqual` (`<=`) condition which takes two {@link ValueExpression} arguments. */
export 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;
};

/** Allowed literal value when calling {@link literal}. */
export 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();
 *     ...
 * }
 */
export 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}. */
export declare type LiteralType = "undefined" | "string" | "boolean" | "DateTime" | "Numeric" | AssociationType;

/** Allowed literal value in a {@link LiteralExpression}. */
export declare type LiteralValue = PrimitiveLiteralValue | GUID_2 | GUID_2[];

declare interface MultiaryFunctionExpression<TName extends FilterFunction, TArg extends FilterExpression> extends FunctionExpression<TName> {
    readonly args: readonly TArg[];
}

/** A logical `not` (invert) condition which takes a single {@link FilterCondition} argument. */
export declare type NotCondition = UnaryFunctionExpression<"not", FilterCondition>;

/** A `notEqual` (`!=`) condition which takes two {@link ValueExpression} arguments. */
export 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;
}

/** A logical `or` condition which takes two or more {@link FilterCondition} arguments. */
export declare type OrCondition = MultiaryFunctionExpression<"or", FilterCondition>;

declare type PrimitiveLiteralValue = undefined | string | boolean | Date | BigJS_2;

/** An `startsWith` condition which takes a {@link AttributeExpression} and a {@link LiteralExpression} as arguments. */
export 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}.
 */
export declare type ValueExpression = AttributeExpression | LiteralExpression | AssociationExpression;

export { }
