/*!
 * Custom elements for Nanopore-Digital Web applications
 */
const ISO_DATE_FORMAT = /^(\d{4})-(\d{2})-(\d{2})/;
export var DaysOfWeek;
(function (DaysOfWeek) {
    DaysOfWeek[DaysOfWeek["Sunday"] = 0] = "Sunday";
    DaysOfWeek[DaysOfWeek["Monday"] = 1] = "Monday";
    DaysOfWeek[DaysOfWeek["Tuesday"] = 2] = "Tuesday";
    DaysOfWeek[DaysOfWeek["Wednesday"] = 3] = "Wednesday";
    DaysOfWeek[DaysOfWeek["Thursday"] = 4] = "Thursday";
    DaysOfWeek[DaysOfWeek["Friday"] = 5] = "Friday";
    DaysOfWeek[DaysOfWeek["Saturday"] = 6] = "Saturday";
})(DaysOfWeek || (DaysOfWeek = {}));
/**
 * @param year
 * @param month
 * @param day
 * @returns a new Date object
 */
export function createDate(year, month, day) {
    const dayInt = parseInt(day, 10);
    const monthInt = parseInt(month, 10);
    const yearInt = parseInt(year, 10);
    const isValid = Number.isInteger(yearInt) && // all parts should be integers
        Number.isInteger(monthInt) &&
        Number.isInteger(dayInt) &&
        monthInt > 0 && // month must be 1-12
        monthInt <= 12 &&
        dayInt > 0 && // day must be 1-31
        dayInt <= 31 &&
        yearInt > 0;
    if (isValid) {
        return new Date(yearInt, monthInt - 1, dayInt);
    }
    return undefined;
}
/**
 * @param value date string in ISO format YYYY-MM-DD
 * @returns an ISO Date object
 */
export function parseISODate(value) {
    if (!value)
        return undefined;
    const matches = value.match(ISO_DATE_FORMAT);
    if (matches) {
        return createDate(matches[1], matches[2], matches[3]);
    }
    return undefined;
}
/**
 * print date in format YYYY-MM-DD
 * @param date
 * @returns an iso date string
 */
export function printISODate(date) {
    if (!date)
        return '';
    let d = date.getDate().toString(10);
    let m = (date.getMonth() + 1).toString(10);
    const y = date.getFullYear().toString(10);
    // days are not zero-indexed, so pad if less than 10
    if (date.getDate() < 10) {
        d = `0${d}`;
    }
    // months *are* zero-indexed, pad if less than 9!
    if (date.getMonth() < 9) {
        m = `0${m}`;
    }
    return `${y}-${m}-${d}`;
}
/**
 * Compare if two dates are equal in terms of day, month, and year
 * @param a date to compare
 * @param b date to compare
 * @returns boolean
 */
export function isEqual(a, b) {
    if (a == null || b == null)
        return false;
    return (a.getFullYear() === b.getFullYear() &&
        a.getMonth() === b.getMonth() &&
        a.getDate() === b.getDate());
}
/**
 * Add days to a date
 * @param date
 * @param days
 * @returns a new Date
 */
export function addDays(date, days) {
    const d = new Date(date);
    d.setDate(d.getDate() + days);
    return d;
}
/**
 * Add months to a date
 * @param date
 * @param months
 * @returns a new Date
 */
export function addMonths(date, months) {
    const d = new Date(date);
    d.setMonth(date.getMonth() + months);
    return d;
}
/**
 * Add years to a date
 * @param date
 * @param years
 * @returns a new Date
 */
export function addYears(date, years) {
    const d = new Date(date);
    d.setFullYear(date.getFullYear() + years);
    return d;
}
/**
 * Takes a date and a day enum and finds the first day of that date's week
 * @param date
 * @param firstDayOfWeek
 * @returns the first day of a date's week
 */
export function startOfWeek(date, firstDayOfWeek = DaysOfWeek.Monday) {
    /* eslint-disable @typescript-eslint/no-unsafe-enum-comparison */
    const d = new Date(date);
    const day = d.getDay();
    const diff = (day < firstDayOfWeek ? 7 : 0) + day - firstDayOfWeek;
    d.setDate(d.getDate() - diff);
    return d;
}
/**
 * Takes a date and a day enum and finds the last day of that date's week
 * @param date
 * @param firstDayOfWeek
 * @returns the last day of that date's week
 */
export function endOfWeek(date, firstDayOfWeek = DaysOfWeek.Monday) {
    /* eslint-disable @typescript-eslint/no-unsafe-enum-comparison */
    const d = new Date(date);
    const day = d.getDay();
    const diff = (day < firstDayOfWeek ? -7 : 0) + 6 - (day - firstDayOfWeek);
    d.setDate(d.getDate() + diff);
    return d;
}
/**
 * Get the date of the start of the month
 * @param date
 * @returns start of the month Date
 */
export function startOfMonth(date) {
    return new Date(date.getFullYear(), date.getMonth(), 1);
}
/**
 * Get the date of the end of the month
 * @param date
 * @returns end of the month Date
 */
export function endOfMonth(date) {
    return new Date(date.getFullYear(), date.getMonth() + 1, 0);
}
/**
 * Changes a dates' month
 * @param date
 * @param month
 * @returns a new Date with the new month set
 */
export function setMonth(date, month) {
    const d = new Date(date);
    d.setMonth(month);
    return d;
}
/**
 * Changes a dates' year
 * @param date
 * @param year
 * @returns a new Date with the new year set
 */
export function setYear(date, year) {
    const d = new Date(date);
    d.setFullYear(year);
    return d;
}
/**
 * Check if date is within a min and max
 * @param date
 * @param min
 * @param max
 * @returns boolean
 */
export function inRange(date, min, max) {
    return dateClamp(date, min, max) === date;
}
/**
 * Ensures date is within range, returns min or max if out of bounds
 * @param date
 * @param min
 * @param max
 * @returns the clamped date
 */
export function dateClamp(date, min, max) {
    const time = date.getTime();
    if (min && min instanceof Date && time < min.getTime()) {
        return min;
    }
    if (max && max instanceof Date && time > max.getTime()) {
        return max;
    }
    return date;
}
/**
 * Given start and end date, return an (inclusive) array of all dates in between
 * @param start
 * @param end
 * @returns an array of Date objects in the range
 */
function getDaysInRange(start, end) {
    const days = [];
    let current = start;
    while (!isEqual(current, end)) {
        days.push(current);
        current = addDays(current, 1);
    }
    days.push(current);
    return days;
}
/**
 * Given a date, return an array of dates from a calendar perspective
 * @param date
 * @param firstDayOfWeek
 * @returns an array of Date objects in the month range
 */
export function getViewOfMonth(date, firstDayOfWeek = DaysOfWeek.Monday) {
    const start = startOfWeek(startOfMonth(date), firstDayOfWeek);
    const end = endOfWeek(endOfMonth(date), firstDayOfWeek);
    return getDaysInRange(start, end);
}
