'use strict';

var isNumeric = require('fast-isnumeric');

var Registry = require('../../registry');
var Lib = require('../../lib');
var Template = require('../../plot_api/plot_template');

var handleArrayContainerDefaults = require('../array_container_defaults');

var layoutAttributes = require('./layout_attributes');
var handleTickValueDefaults = require('./tick_value_defaults');
var handleTickMarkDefaults = require('./tick_mark_defaults');
var handleTickLabelDefaults = require('./tick_label_defaults');
var handlePrefixSuffixDefaults = require('./prefix_suffix_defaults');
var handleCategoryOrderDefaults = require('./category_order_defaults');
var handleLineGridDefaults = require('./line_grid_defaults');
var handleRangeDefaults = require('./range_defaults');
var setConvert = require('./set_convert');

var DAY_OF_WEEK = require('./constants').WEEKDAY_PATTERN;
var HOUR = require('./constants').HOUR_PATTERN;

/**
 * options: object containing:
 *
 *  letter: 'x' or 'y'
 *  title: name of the axis (ie 'Colorbar') to go in default title
 *  font: the default font to inherit
 *  outerTicks: boolean, should ticks default to outside?
 *  showGrid: boolean, should gridlines be shown by default?
 *  noHover: boolean, this axis doesn't support hover effects?
 *  noTickson: boolean, this axis doesn't support 'tickson'
 *  data: the plot data, used to manage categories
 *  bgColor: the plot background color, to calculate default gridline colors
 *  calendar:
 *  splomStash:
 *  visibleDflt: boolean
 *  reverseDflt: boolean
 *  automargin: boolean
 */
module.exports = function handleAxisDefaults(containerIn, containerOut, coerce, options, layoutOut) {
    var letter = options.letter;
    var font = options.font || {};
    var splomStash = options.splomStash || {};

    var visible = coerce('visible', !options.visibleDflt);

    var axTemplate = containerOut._template || {};
    var axType = containerOut.type || axTemplate.type || '-';

    var ticklabelmode;
    if(axType === 'date') {
        var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleDefaults');
        handleCalendarDefaults(containerIn, containerOut, 'calendar', options.calendar);

        if(!options.noTicklabelmode) {
            ticklabelmode = coerce('ticklabelmode');
        }
    }

    if(!options.noTicklabelindex && (axType === 'date' || axType === 'linear')) {
        coerce('ticklabelindex');
    }

    var ticklabelposition = '';
    if(!options.noTicklabelposition || axType === 'multicategory') {
        ticklabelposition = Lib.coerce(containerIn, containerOut, {
            ticklabelposition: {
                valType: 'enumerated',
                dflt: 'outside',
                values: ticklabelmode === 'period' ? ['outside', 'inside'] :
                letter === 'x' ? [
                    'outside', 'inside',
                    'outside left', 'inside left',
                    'outside right', 'inside right'
                ] : [
                    'outside', 'inside',
                    'outside top', 'inside top',
                    'outside bottom', 'inside bottom'
                ]
            }
        }, 'ticklabelposition');
    }

    if(!options.noTicklabeloverflow) {
        coerce('ticklabeloverflow',
            ticklabelposition.indexOf('inside') !== -1 ?
                'hide past domain' :
            axType === 'category' ||
            axType === 'multicategory' ?
                'allow' :
                'hide past div'
        );
    }

    setConvert(containerOut, layoutOut);

    handleRangeDefaults(containerIn, containerOut, coerce, options);

    handleCategoryOrderDefaults(containerIn, containerOut, coerce, options);

    if(axType !== 'category' && !options.noHover) coerce('hoverformat');

    var dfltColor = coerce('color');
    // if axis.color was provided, use it for fonts too; otherwise,
    // inherit from global font color in case that was provided.
    // Compare to dflt rather than to containerIn, so we can provide color via
    // template too.
    var dfltFontColor = (dfltColor !== layoutAttributes.color.dflt) ? dfltColor : font.color;
    // try to get default title from splom trace, fallback to graph-wide value
    var dfltTitle = splomStash.label || layoutOut._dfltTitle[letter];

    handlePrefixSuffixDefaults(containerIn, containerOut, coerce, axType, options);
    if(!visible) return containerOut;

    coerce('title.text', dfltTitle);
    Lib.coerceFont(coerce, 'title.font', font, { overrideDflt: {
        size: Lib.bigFont(font.size),
        color: dfltFontColor
    }});

    // major ticks
    handleTickValueDefaults(containerIn, containerOut, coerce, axType);

    var hasMinor = options.hasMinor;
    if(hasMinor) {
        // minor ticks
        Template.newContainer(containerOut, 'minor');
        handleTickValueDefaults(containerIn, containerOut, coerce, axType, { isMinor: true });
    }

    handleTickLabelDefaults(containerIn, containerOut, coerce, axType, options);

    // major and minor ticks
    handleTickMarkDefaults(containerIn, containerOut, coerce, options);
    if(hasMinor) {
        var keepIsMinor = options.isMinor;
        options.isMinor = true;
        handleTickMarkDefaults(containerIn, containerOut, coerce, options);
        options.isMinor = keepIsMinor;
    }

    handleLineGridDefaults(containerIn, containerOut, coerce, {
        dfltColor: dfltColor,
        bgColor: options.bgColor,
        showGrid: options.showGrid,
        hasMinor: hasMinor,
        attributes: layoutAttributes
    });

    // delete minor when no minor ticks or gridlines
    if(
        hasMinor &&
        !containerOut.minor.ticks &&
        !containerOut.minor.showgrid
    ) {
        delete containerOut.minor;
    }

    // mirror
    if(containerOut.showline || containerOut.ticks) coerce('mirror');

    var isMultiCategory = axType === 'multicategory';

    if(!options.noTickson &&
        (axType === 'category' || isMultiCategory) &&
        (containerOut.ticks || containerOut.showgrid)
    ) {
        var ticksonDflt;
        if(isMultiCategory) ticksonDflt = 'boundaries';
        var tickson = coerce('tickson', ticksonDflt);
        if(tickson === 'boundaries') {
            delete containerOut.ticklabelposition;
        }
    }

    if(isMultiCategory) {
        var showDividers = coerce('showdividers');
        if(showDividers) {
            coerce('dividercolor');
            coerce('dividerwidth');
        }
    }

    if(axType === 'date') {
        handleArrayContainerDefaults(containerIn, containerOut, {
            name: 'rangebreaks',
            inclusionAttr: 'enabled',
            handleItemDefaults: rangebreaksDefaults
        });

        if(!containerOut.rangebreaks.length) {
            delete containerOut.rangebreaks;
        } else {
            for(var k = 0; k < containerOut.rangebreaks.length; k++) {
                if(containerOut.rangebreaks[k].pattern === DAY_OF_WEEK) {
                    containerOut._hasDayOfWeekBreaks = true;
                    break;
                }
            }

            setConvert(containerOut, layoutOut);

            if(layoutOut._has('scattergl') || layoutOut._has('splom')) {
                for(var i = 0; i < options.data.length; i++) {
                    var trace = options.data[i];
                    if(trace.type === 'scattergl' || trace.type === 'splom') {
                        trace.visible = false;
                        Lib.warn(trace.type +
                            ' traces do not work on axes with rangebreaks.' +
                            ' Setting trace ' + trace.index + ' to `visible: false`.');
                    }
                }
            }
        }
    }

    return containerOut;
};

function rangebreaksDefaults(itemIn, itemOut, containerOut) {
    function coerce(attr, dflt) {
        return Lib.coerce(itemIn, itemOut, layoutAttributes.rangebreaks, attr, dflt);
    }

    var enabled = coerce('enabled');

    if(enabled) {
        var bnds = coerce('bounds');
        if(bnds && bnds.length >= 2) {
            var dfltPattern = '';
            var i, q;
            if(bnds.length === 2) {
                for(i = 0; i < 2; i++) {
                    q = indexOfDay(bnds[i]);
                    if(q) {
                        dfltPattern = DAY_OF_WEEK;
                        break;
                    }
                }
            }
            var pattern = coerce('pattern', dfltPattern);
            if(pattern === DAY_OF_WEEK) {
                for(i = 0; i < 2; i++) {
                    q = indexOfDay(bnds[i]);
                    if(q) {
                        // convert to integers i.e 'Sunday' --> 0
                        itemOut.bounds[i] = bnds[i] = q - 1;
                    }
                }
            }
            if(pattern) {
                // ensure types and ranges
                for(i = 0; i < 2; i++) {
                    q = bnds[i];
                    switch(pattern) {
                        case DAY_OF_WEEK :
                            if(!isNumeric(q)) {
                                itemOut.enabled = false;
                                return;
                            }
                            q = +q;

                            if(
                                q !== Math.floor(q) || // don't accept fractional days for mow
                                q < 0 || q >= 7
                            ) {
                                itemOut.enabled = false;
                                return;
                            }
                            // use number
                            itemOut.bounds[i] = bnds[i] = q;
                            break;

                        case HOUR :
                            if(!isNumeric(q)) {
                                itemOut.enabled = false;
                                return;
                            }
                            q = +q;

                            if(q < 0 || q > 24) { // accept 24
                                itemOut.enabled = false;
                                return;
                            }
                            // use number
                            itemOut.bounds[i] = bnds[i] = q;
                            break;
                    }
                }
            }

            if(containerOut.autorange === false) {
                var rng = containerOut.range;

                // if bounds are bigger than the (set) range, disable break
                if(rng[0] < rng[1]) {
                    if(bnds[0] < rng[0] && bnds[1] > rng[1]) {
                        itemOut.enabled = false;
                        return;
                    }
                } else if(bnds[0] > rng[0] && bnds[1] < rng[1]) {
                    itemOut.enabled = false;
                    return;
                }
            }
        } else {
            var values = coerce('values');

            if(values && values.length) {
                coerce('dvalue');
            } else {
                itemOut.enabled = false;
                return;
            }
        }
    }
}

// these numbers are one more than what bounds would be mapped to
var dayStrToNum = {
    sun: 1,
    mon: 2,
    tue: 3,
    wed: 4,
    thu: 5,
    fri: 6,
    sat: 7
};

function indexOfDay(v) {
    if(typeof v !== 'string') return;
    return dayStrToNum[
        v.substr(0, 3).toLowerCase()
    ];
}
