import zeroFill from '../utils/zero-fill';

import isFunction from '../utils/is-function';



export var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g;



var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g;



var formatFunctions = {};



export var formatTokenFunctions = {};



// token:    'M'

// padded:   ['MM', 2]

// ordinal:  'Mo'

// callback: function () { this.month() + 1 }

export function addFormatToken (token, padded, ordinal, callback) {

    var func = callback;

    if (typeof callback === 'string') {

        func = function () {

            return this[callback]();

        };

    }

    if (token) {

        formatTokenFunctions[token] = func;

    }

    if (padded) {

        formatTokenFunctions[padded[0]] = function () {

            return zeroFill(func.apply(this, arguments), padded[1], padded[2]);

        };

    }

    if (ordinal) {

        formatTokenFunctions[ordinal] = function () {

            return this.localeData().ordinal(func.apply(this, arguments), token);

        };

    }

}



function removeFormattingTokens(input) {

    if (input.match(/\[[\s\S]/)) {

        return input.replace(/^\[|\]$/g, '');

    }

    return input.replace(/\\/g, '');

}



function makeFormatFunction(format) {

    var array = format.match(formattingTokens), i, length;



    for (i = 0, length = array.length; i < length; i++) {

        if (formatTokenFunctions[array[i]]) {

            array[i] = formatTokenFunctions[array[i]];

        } else {

            array[i] = removeFormattingTokens(array[i]);

        }

    }



    return function (mom) {

        var output = '', i;

        for (i = 0; i < length; i++) {

            output += isFunction(array[i]) ? array[i].call(mom, format) : array[i];

        }

        return output;

    };

}



// format date using native date object

export function formatMoment(m, format) {

    if (!m.isValid()) {

        return m.localeData().invalidDate();

    }



    format = expandFormat(format, m.localeData());

    formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format);



    return formatFunctions[format](m);

}



export function expandFormat(format, locale) {

    var i = 5;



    function replaceLongDateFormatTokens(input) {

        return locale.longDateFormat(input) || input;

    }



    localFormattingTokens.lastIndex = 0;

    while (i >= 0 && localFormattingTokens.test(format)) {

        format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);

        localFormattingTokens.lastIndex = 0;

        i -= 1;

    }



    return format;

}

