import type {If} from './if.d.ts';
import type {IfNotAnyOrNever} from './internal/type.d.ts';
import type {IsNegative} from './numeric.d.ts';
import type {DigitCharacter} from './characters.d.ts';
import type {UnknownArray} from './unknown-array.d.ts';

/**
Create a tuple type of the specified length with elements of the specified type.

@example
```
import type {TupleOf} from 'type-fest';

type RGB = TupleOf<3, number>;
//=> [number, number, number]

type Line = TupleOf<2, {x: number; y: number}>;
//=> [{x: number; y: number}, {x: number; y: number}]

type TicTacToeBoard = TupleOf<3, TupleOf<3, 'X' | 'O' | null>>;
//=> [['X' | 'O' | null, 'X' | 'O' | null, 'X' | 'O' | null], ['X' | 'O' | null, 'X' | 'O' | null, 'X' | 'O' | null], ['X' | 'O' | null, 'X' | 'O' | null, 'X' | 'O' | null]]
```

@example
```
import type {TupleOf} from 'type-fest';

type Range<Start extends number, End extends number> = Exclude<keyof TupleOf<End>, keyof TupleOf<Start>>;

type ZeroToFour = Range<0, 5>;
//=> '0' | '1' | '2' | '3' | '4'

type ThreeToEight = Range<3, 9>;
//=> '3' | '4' | '5' | '6' | '7' | '8'
```

Note: If the specified length is the non-literal `number` type, the result will not be a tuple but a regular array.

@example
```
import type {TupleOf} from 'type-fest';

type StringArray = TupleOf<number, string>;
//=> string[]
```

Note: If the type for elements is not specified, it will default to `unknown`.

@example
```
import type {TupleOf} from 'type-fest';

type UnknownTriplet = TupleOf<3>;
//=> [unknown, unknown, unknown]
```

Note: If the specified length is negative, the result will be an empty tuple.

@example
```
import type {TupleOf} from 'type-fest';

type EmptyTuple = TupleOf<-3, string>;
//=> []
```

Note: If the specified length has a decimal part, the decimal part will be ignored.

@example
```
import type {TupleOf} from 'type-fest';

type DecimalLength = TupleOf<3.5, string>;
//=> [string, string, string]
```

Note: If you need a readonly tuple, simply wrap this type with `Readonly`, for example, to create `readonly [number, number, number]` use `Readonly<TupleOf<3, number>>`.

@category Array
*/
export type TupleOf<Length extends number, Fill = unknown> = IfNotAnyOrNever<Length, {
	ifNot: _TupleOf<If<IsNegative<Length>, 0, Length>, Fill>;
	ifAny: Fill[];
	ifNever: [];
}>;

type _TupleOf<Length extends number, Fill> = number extends Length
	? Fill[]
	: BuildTupleDigitByDigit<`${Length}`, Fill>;

type BuildTupleDigitByDigit<Length extends string, Fill, Accumulator extends UnknownArray = []> =
	Length extends `${infer First extends DigitCharacter}${infer Rest}`
		? BuildTupleDigitByDigit<Rest, Fill, [...RepeatTupleTenTimes<Accumulator>, ...DigitTupleOf<First, Fill>]>
		: Accumulator;

type RepeatTupleTenTimes<Tuple extends UnknownArray> = [
	...Tuple, ...Tuple, ...Tuple, ...Tuple, ...Tuple,
	...Tuple, ...Tuple, ...Tuple, ...Tuple, ...Tuple,
];

type DigitTupleOf<Digit extends DigitCharacter, Fill> = [
	[],
	[Fill],
	[Fill, Fill],
	[Fill, Fill, Fill],
	[Fill, Fill, Fill, Fill],
	[Fill, Fill, Fill, Fill, Fill],
	[Fill, Fill, Fill, Fill, Fill, Fill],
	[Fill, Fill, Fill, Fill, Fill, Fill, Fill],
	[Fill, Fill, Fill, Fill, Fill, Fill, Fill, Fill],
	[Fill, Fill, Fill, Fill, Fill, Fill, Fill, Fill, Fill],
][Digit];

export {};
