import { dispatch as d3_dispatch } from 'd3-dispatch';
import {
    select as d3_select
} from 'd3-selection';

import { presetManager } from '../../presets';
import { localizer, t } from '../../core/localizer';
import { actionChangePreset } from '../../actions/change_preset';
import { modeBrowse } from '../../modes/browse';
import { modeSelect } from '../../modes/select';
import { utilRebind } from '../../util/rebind';
import { helpHtml, icon, pointBox, pad, selectMenuItem, transitionTime } from './helper';


export function uiIntroPoint(context, reveal) {
    var dispatch = d3_dispatch('done');
    var timeouts = [];
    var intersection = [-85.63279, 41.94394];
    var building = [-85.632422, 41.944045];
    var cafePreset = presetManager.item('amenity/cafe');
    var _pointID = null;


    var chapter = {
        title: 'intro.points.title'
    };


    function timeout(f, t) {
        timeouts.push(window.setTimeout(f, t));
    }


    function eventCancel(d3_event) {
        d3_event.stopPropagation();
        d3_event.preventDefault();
    }


    function addPoint() {
        context.enter(modeBrowse(context));
        context.history().reset('initial');

        var msec = transitionTime(intersection, context.map().center());
        if (msec) { reveal(null, null, { duration: 0 }); }
        context.map().centerZoomEase(intersection, 19, msec);

        timeout(function() {
            var tooltip = reveal('button.add-point',
                helpHtml('intro.points.points_info') + '{br}' + helpHtml('intro.points.add_point'));

            _pointID = null;

            tooltip.selectAll('.popover-inner')
                .insert('svg', 'span')
                .attr('class', 'tooltip-illustration')
                .append('use')
                .attr('xlink:href', '#iD-graphic-points');

            context.on('enter.intro', function(mode) {
                if (mode.id !== 'add-point') return;
                continueTo(placePoint);
            });
        }, msec + 100);

        function continueTo(nextStep) {
            context.on('enter.intro', null);
            nextStep();
        }
    }


    function placePoint() {
        if (context.mode().id !== 'add-point') {
            return chapter.restart();
        }

        var pointBox = pad(building, 150, context);
        var textId = context.lastPointerType() === 'mouse' ? 'place_point' : 'place_point_touch';
        reveal(pointBox, helpHtml('intro.points.' + textId));

        context.map().on('move.intro drawn.intro', function() {
            pointBox = pad(building, 150, context);
            reveal(pointBox, helpHtml('intro.points.' + textId), { duration: 0 });
        });

        context.on('enter.intro', function(mode) {
            if (mode.id !== 'select') return chapter.restart();
            _pointID = context.mode().selectedIDs()[0];

            if (context.graph().geometry(_pointID) === 'vertex'){

                //disallow all
                context.map().on('move.intro drawn.intro', null);
                context.on('enter.intro', null);

                reveal(pointBox, helpHtml('intro.points.place_point_error'), {
                    buttonText: localizer.t_html('intro.ok'),
                    buttonCallback: function() { return chapter.restart(); }
                });
            } else {
                continueTo(searchPreset);
            }
        });

        function continueTo(nextStep) {
            context.map().on('move.intro drawn.intro', null);
            context.on('enter.intro', null);
            nextStep();
        }
    }


    function searchPreset() {
        if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
            return addPoint();
        }

        // disallow scrolling
        context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);

        context.container().select('.preset-search-input')
            .on('keydown.intro', null)
            .on('keyup.intro', checkPresetSearch);

        reveal('.preset-search-input',
            helpHtml('intro.points.search_cafe', { preset: cafePreset.name() })
        );

        context.on('enter.intro', function(mode) {
            if (!_pointID || !context.hasEntity(_pointID)) {
                return continueTo(addPoint);
            }

            var ids = context.selectedIDs();
            if (mode.id !== 'select' || !ids.length || ids[0] !== _pointID) {
                // keep the user's point selected..
                context.enter(modeSelect(context, [_pointID]));

                // disallow scrolling
                context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);

                context.container().select('.preset-search-input')
                    .on('keydown.intro', null)
                    .on('keyup.intro', checkPresetSearch);

                reveal('.preset-search-input',
                    helpHtml('intro.points.search_cafe', { preset: cafePreset.name() })
                );

                context.history().on('change.intro', null);
            }
        });


        function checkPresetSearch() {
            var first = context.container().select('.preset-list-item:first-child');

            if (first.classed('preset-amenity-cafe')) {
                context.container().select('.preset-search-input')
                    .on('keydown.intro', eventCancel, true)
                    .on('keyup.intro', null);

                reveal(first.select('.preset-list-button').node(),
                    helpHtml('intro.points.choose_cafe', { preset: cafePreset.name() }),
                    { duration: 300 }
                );

                context.history().on('change.intro', function() {
                    continueTo(aboutFeatureEditor);
                });
            }
        }

        function continueTo(nextStep) {
            context.on('enter.intro', null);
            context.history().on('change.intro', null);
            context.container().select('.inspector-wrap').on('wheel.intro', null);
            context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
            nextStep();
        }
    }


    function aboutFeatureEditor() {
        if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
            return addPoint();
        }

        timeout(function() {
            reveal('.entity-editor-pane', helpHtml('intro.points.feature_editor'), {
                tooltipClass: 'intro-points-describe',
                buttonText: localizer.t_html('intro.ok'),
                buttonCallback: function() { continueTo(addName); }
            });
        }, 400);

        context.on('exit.intro', function() {
            // if user leaves select mode here, just continue with the tutorial.
            continueTo(reselectPoint);
        });

        function continueTo(nextStep) {
            context.on('exit.intro', null);
            nextStep();
        }
    }


    function addName() {
        if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
            return addPoint();
        }

        // reset pane, in case user happened to change it..
        context.container().select('.inspector-wrap .panewrap').style('right', '0%');

        var addNameString = helpHtml('intro.points.fields_info') + '{br}' + helpHtml('intro.points.add_name') + '{br}' + helpHtml('intro.points.add_reminder');

        timeout(function() {
            // It's possible for the user to add a name in a previous step..
            // If so, don't tell them to add the name in this step.
            // Give them an OK button instead.
            var entity = context.entity(_pointID);
            if (entity.tags.name) {
                var tooltip = reveal('.entity-editor-pane', addNameString, {
                    tooltipClass: 'intro-points-describe',
                    buttonText: localizer.t_html('intro.ok'),
                    buttonCallback: function() { continueTo(addCloseEditor); }
                });
                tooltip.select('.instruction').style('display', 'none');

            } else {
                reveal('.entity-editor-pane', addNameString,
                    { tooltipClass: 'intro-points-describe' }
                );
            }
        }, 400);

        context.history().on('change.intro', function() {
            continueTo(addCloseEditor);
        });

        context.on('exit.intro', function() {
            // if user leaves select mode here, just continue with the tutorial.
            continueTo(reselectPoint);
        });

        function continueTo(nextStep) {
            context.on('exit.intro', null);
            context.history().on('change.intro', null);
            nextStep();
        }
    }


    function addCloseEditor() {
        // reset pane, in case user happened to change it..
        context.container().select('.inspector-wrap .panewrap').style('right', '0%');

        var selector = '.entity-editor-pane button.close svg use';
        var href = d3_select(selector).attr('href') || '#iD-icon-close';

        context.on('exit.intro', function() {
            continueTo(reselectPoint);
        });

        reveal('.entity-editor-pane',
            helpHtml('intro.points.add_close', { button: { html: icon(href, 'inline') } })
        );

        function continueTo(nextStep) {
            context.on('exit.intro', null);
            nextStep();
        }
    }


    function reselectPoint() {
        if (!_pointID) return chapter.restart();
        var entity = context.hasEntity(_pointID);
        if (!entity) return chapter.restart();

        // make sure it's still a cafe, in case user somehow changed it..
        var oldPreset = presetManager.match(entity, context.graph());
        context.replace(actionChangePreset(_pointID, oldPreset, cafePreset));

        context.enter(modeBrowse(context));

        var msec = transitionTime(entity.loc, context.map().center());
        if (msec) { reveal(null, null, { duration: 0 }); }
        context.map().centerEase(entity.loc, msec);

        timeout(function() {
            var box = pointBox(entity.loc, context);
            reveal(box, helpHtml('intro.points.reselect'), { duration: 600 });

            timeout(function() {
                context.map().on('move.intro drawn.intro', function() {
                    var entity = context.hasEntity(_pointID);
                    if (!entity) return chapter.restart();
                    var box = pointBox(entity.loc, context);
                    reveal(box, helpHtml('intro.points.reselect'), { duration: 0 });
                });
            }, 600); // after reveal..

            context.on('enter.intro', function(mode) {
                if (mode.id !== 'select') return;
                continueTo(updatePoint);
            });

        }, msec + 100);

        function continueTo(nextStep) {
            context.map().on('move.intro drawn.intro', null);
            context.on('enter.intro', null);
            nextStep();
        }
    }


    function updatePoint() {
        if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
            return continueTo(reselectPoint);
        }

        // reset pane, in case user happened to untag the point..
        context.container().select('.inspector-wrap .panewrap').style('right', '0%');

        context.on('exit.intro', function() {
            continueTo(reselectPoint);
        });

        context.history().on('change.intro', function() {
            continueTo(updateCloseEditor);
        });

        timeout(function() {
            reveal('.entity-editor-pane', helpHtml('intro.points.update'),
                { tooltipClass: 'intro-points-describe' }
            );
        }, 400);

        function continueTo(nextStep) {
            context.on('exit.intro', null);
            context.history().on('change.intro', null);
            nextStep();
        }
    }


    function updateCloseEditor() {
        if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
            return continueTo(reselectPoint);
        }

        // reset pane, in case user happened to change it..
        context.container().select('.inspector-wrap .panewrap').style('right', '0%');

        context.on('exit.intro', function() {
            continueTo(rightClickPoint);
        });

        timeout(function() {
            reveal('.entity-editor-pane',
                helpHtml('intro.points.update_close', { button: { html: icon('#iD-icon-close', 'inline') } })
            );
        }, 500);

        function continueTo(nextStep) {
            context.on('exit.intro', null);
            nextStep();
        }
    }


    function rightClickPoint() {
        if (!_pointID) return chapter.restart();
        var entity = context.hasEntity(_pointID);
        if (!entity) return chapter.restart();

        context.enter(modeBrowse(context));

        var box = pointBox(entity.loc, context);
        var textId = context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch';
        reveal(box, helpHtml('intro.points.' + textId), { duration: 600 });

        timeout(function() {
            context.map().on('move.intro', function() {
                var entity = context.hasEntity(_pointID);
                if (!entity) return chapter.restart();
                var box = pointBox(entity.loc, context);
                reveal(box, helpHtml('intro.points.' + textId), { duration: 0 });
            });
        }, 600); // after reveal

        context.on('enter.intro', function(mode) {
            if (mode.id !== 'select') return;
            var ids = context.selectedIDs();
            if (ids.length !== 1 || ids[0] !== _pointID) return;

            timeout(function() {
                var node = selectMenuItem(context, 'delete').node();
                if (!node) return;
                continueTo(enterDelete);
            }, 50);  // after menu visible
        });

        function continueTo(nextStep) {
            context.on('enter.intro', null);
            context.map().on('move.intro', null);
            nextStep();
        }
    }


    function enterDelete() {
        if (!_pointID) return chapter.restart();
        var entity = context.hasEntity(_pointID);
        if (!entity) return chapter.restart();

        var node = selectMenuItem(context, 'delete').node();
        if (!node) { return continueTo(rightClickPoint); }

        reveal('.edit-menu',
            helpHtml('intro.points.delete'),
            { padding: 50 }
        );

        timeout(function() {
            context.map().on('move.intro', function() {
                if (selectMenuItem(context, 'delete').empty()) {
                    return continueTo(rightClickPoint);
                }
                reveal('.edit-menu',
                    helpHtml('intro.points.delete'),
                    { duration: 0,  padding: 50 }
                );
            });
        }, 300); // after menu visible

        context.on('exit.intro', function() {
            if (!_pointID) return chapter.restart();
            var entity = context.hasEntity(_pointID);
            if (entity) return continueTo(rightClickPoint);  // point still exists
        });

        context.history().on('change.intro', function(changed) {
            if (changed.deleted().length) {
                continueTo(undo);
            }
        });

        function continueTo(nextStep) {
            context.map().on('move.intro', null);
            context.history().on('change.intro', null);
            context.on('exit.intro', null);
            nextStep();
        }
    }


    function undo() {
        context.history().on('change.intro', function() {
            continueTo(play);
        });

        reveal('.top-toolbar button.undo-button',
            helpHtml('intro.points.undo')
        );

        function continueTo(nextStep) {
            context.history().on('change.intro', null);
            nextStep();
        }
    }


    function play() {
        dispatch.call('done');
        reveal('.ideditor',
            helpHtml('intro.points.play', { next: t('intro.areas.title') }), {
                tooltipBox: '.intro-nav-wrap .chapter-area',
                buttonText: localizer.t_html('intro.ok'),
                buttonCallback: function() { reveal('.ideditor'); }
            }
        );
    }


    chapter.enter = function() {
        addPoint();
    };


    chapter.exit = function() {
        timeouts.forEach(window.clearTimeout);
        context.on('enter.intro exit.intro', null);
        context.map().on('move.intro drawn.intro', null);
        context.history().on('change.intro', null);
        context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
        context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
    };


    chapter.restart = function() {
        chapter.exit();
        chapter.enter();
    };


    return utilRebind(chapter, dispatch, 'on');
}
