import pad from '../number/pad';
import lpad from '../string/lpad';
import i18n from './i18n_';
import dayOfTheYear from './dayOfTheYear';
import timezoneOffset from './timezoneOffset';
import timezoneAbbr from './timezoneAbbr';
import weekOfTheYear from './weekOfTheYear';

const _combinations = {
    D: '%m/%d/%y',
    F: '%Y-%m-%d',
    r: '%I:%M:%S %p',
    R: '%H:%M',
    T: '%H:%M:%S',
    x: 'locale',
    X: 'locale',
    c: 'locale'
};

/**
 * format date based on strftime format
 */
function strftime(date, format, localeData = i18n) {
    const reToken = /%([a-z%])/gi;

    function makeIterator(fn) {
        return function(match, token) {
            return fn(date, token, localeData);
        };
    }

    return format
        .replace(reToken, makeIterator(expandCombinations))
        .replace(reToken, makeIterator(convertToken));
}

function expandCombinations(date, token, l10n) {
    if (token in _combinations) {
        const expanded = _combinations[token];
        return expanded === 'locale' ? l10n[token] : expanded;
    } else {
        return `%${token}`;
    }
}

function convertToken(date, token, l10n) {
    let day;
    switch (token) {
        case 'a':
            return l10n.days_abbr[date.getDay()];
        case 'A':
            return l10n.days[date.getDay()];
        case 'h':
        case 'b':
            return l10n.months_abbr[date.getMonth()];
        case 'B':
            return l10n.months[date.getMonth()];
        case 'C':
            return pad(Math.floor(date.getFullYear() / 100), 2);
        case 'd':
            return pad(date.getDate(), 2);
        case 'e':
            return pad(date.getDate(), 2, ' ');
        case 'H':
            return pad(date.getHours(), 2);
        case 'I':
            return pad(date.getHours() % 12, 2);
        case 'j':
            return pad(dayOfTheYear(date), 3);
        case 'l':
            return lpad(date.getHours() % 12, 2);
        case 'L':
            return pad(date.getMilliseconds(), 3);
        case 'm':
            return pad(date.getMonth() + 1, 2);
        case 'M':
            return pad(date.getMinutes(), 2);
        case 'n':
            return '\n';
        case 'p':
            return date.getHours() >= 12 ? l10n.pm : l10n.am;
        case 'P':
            return convertToken(date, 'p', l10n).toLowerCase();
        case 's':
            return date.getTime() / 1000;
        case 'S':
            return pad(date.getSeconds(), 2);
        case 't':
            return '\t';
        case 'u':
            day = date.getDay();
            return day === 0 ? 7 : day;
        case 'U':
            return pad(weekOfTheYear(date), 2);
        case 'w':
            return date.getDay();
        case 'W':
            return pad(weekOfTheYear(date, 1), 2);
        case 'y':
            return pad(date.getFullYear() % 100, 2);
        case 'Y':
            return pad(date.getFullYear(), 4);
        case 'z':
            return timezoneOffset(date);
        case 'Z':
            return timezoneAbbr(date);
        case '%':
            return '%';
        default:
            // keep unrecognized tokens
            return `%${token}`;
    }
}

export default strftime;
