import * as d3 from 'd3';
import _ from 'lodash';
import { d3combobox } from '../lib/d3.combobox.js';
import { t } from '../util/locale';

import {
    actionAddEntity,
    actionAddMember,
    actionChangeMember,
    actionDeleteMember
} from '../actions/index';

import { modeSelect } from '../modes/index';
import { osmEntity, osmRelation } from '../osm/index';
import { services } from '../services/index';
import { svgIcon } from '../svg/index';
import { uiDisclosure } from './disclosure';
import { utilDisplayName, utilNoAuto } from '../util/index';


export function uiRawMembershipEditor(context) {
    var taginfo = services.taginfo,
        id, showBlank;


    function selectRelation(d) {
        d3.event.preventDefault();
        context.enter(modeSelect(context, [d.relation.id]));
    }


    function changeRole(d) {
        var role = d3.select(this).property('value');
        context.perform(
            actionChangeMember(d.relation.id, _.extend({}, d.member, { role: role }), d.index),
            t('operations.change_role.annotation')
        );
    }


    function addMembership(d, role) {
        showBlank = false;

        if (d.relation) {
            context.perform(
                actionAddMember(d.relation.id, { id: id, type: context.entity(id).type, role: role }),
                t('operations.add_member.annotation')
            );

        } else {
            var relation = osmRelation();
            context.perform(
                actionAddEntity(relation),
                actionAddMember(relation.id, { id: id, type: context.entity(id).type, role: role }),
                t('operations.add.annotation.relation')
            );

            context.enter(modeSelect(context, [relation.id]));
        }
    }


    function deleteMembership(d) {
        context.perform(
            actionDeleteMember(d.relation.id, d.index),
            t('operations.delete_member.annotation')
        );
    }


    function relations(q) {
        var newRelation = {
                relation: null,
                value: t('inspector.new_relation')
            },
            result = [],
            graph = context.graph();

        context.intersects(context.extent()).forEach(function(entity) {
            if (entity.type !== 'relation' || entity.id === id)
                return;

            var matched = context.presets().match(entity, graph),
                presetName = (matched && matched.name()) || t('inspector.relation'),
                entityName = utilDisplayName(entity) || '';

            var value = presetName + ' ' + entityName;
            if (q && value.toLowerCase().indexOf(q.toLowerCase()) === -1)
                return;

            result.push({
                relation: entity,
                value: value
            });
        });

        result.sort(function(a, b) {
            return osmRelation.creationOrder(a.relation, b.relation);
        });

        // Dedupe identical names by appending relation id - see #2891
        var dupeGroups = _(result)
            .groupBy('value')
            .filter(function(v) { return v.length > 1; })
            .value();

        dupeGroups.forEach(function(group) {
            group.forEach(function(obj) {
                obj.value += ' ' + obj.relation.id;
            });
        });

        result.unshift(newRelation);
        return result;
    }


    function rawMembershipEditor(selection) {
        var entity = context.entity(id),
            parents = context.graph().parentRelations(entity),
            memberships = [];

        parents.slice(0, 1000).forEach(function(relation) {
            relation.members.forEach(function(member, index) {
                if (member.id === entity.id) {
                    memberships.push({ relation: relation, member: member, index: index });
                }
            });
        });

        var gt = parents.length > 1000 ? '>' : '';
        selection.call(uiDisclosure()
            .title(t('inspector.all_relations') + ' (' + gt + memberships.length + ')')
            .expanded(true)
            .on('toggled', toggled)
            .content(content)
        );


        function toggled(expanded) {
            if (expanded) {
                selection.node().parentNode.scrollTop += 200;
            }
        }


        function content(wrap) {
            var list = wrap.selectAll('.member-list')
                .data([0]);

            list = list.enter()
                .append('ul')
                .attr('class', 'member-list')
                .merge(list);


            var items = list.selectAll('li.member-row-normal')
                .data(memberships, function(d) {
                    return osmEntity.key(d.relation) + ',' + d.index;
                });

            items.exit()
                .each(unbind)
                .remove();

            var enter = items.enter()
                .append('li')
                .attr('class', 'member-row member-row-normal form-field');

            var label = enter
                .append('label')
                .attr('class', 'form-label')
                .append('a')
                .attr('href', '#')
                .on('click', selectRelation);

            label
                .append('span')
                .attr('class', 'member-entity-type')
                .text(function(d) {
                    var matched = context.presets().match(d.relation, context.graph());
                    return (matched && matched.name()) || t('inspector.relation');
                });

            label
                .append('span')
                .attr('class', 'member-entity-name')
                .text(function(d) { return utilDisplayName(d.relation); });

            enter
                .append('input')
                .attr('class', 'member-role')
                .property('type', 'text')
                .attr('maxlength', 255)
                .attr('placeholder', t('inspector.role'))
                .call(utilNoAuto)
                .property('value', function(d) { return d.member.role; })
                .on('change', changeRole);

            enter
                .append('button')
                .attr('tabindex', -1)
                .attr('class', 'remove button-input-action member-delete minor')
                .on('click', deleteMembership)
                .call(svgIcon('#operation-delete'));

            if (taginfo) {
                enter.each(bindTypeahead);
            }


            var newrow = list.selectAll('.member-row-new')
                .data(showBlank ? [0] : []);

            newrow.exit()
                .remove();

            enter = newrow.enter()
                .append('li')
                .attr('class', 'member-row member-row-new form-field');

            enter
                .append('input')
                .attr('type', 'text')
                .attr('class', 'member-entity-input')
                .call(utilNoAuto);

            enter
                .append('input')
                .attr('class', 'member-role')
                .property('type', 'text')
                .attr('maxlength', 255)
                .attr('placeholder', t('inspector.role'))
                .call(utilNoAuto)
                .on('change', changeRole);

            enter
                .append('button')
                .attr('tabindex', -1)
                .attr('class', 'remove button-input-action member-delete minor')
                .on('click', deleteMembership)
                .call(svgIcon('#operation-delete'));

            newrow = newrow
                .merge(enter);

            newrow.selectAll('.member-entity-input')
                .call(d3combobox()
                    .container(context.container())
                    .minItems(1)
                    .fetcher(function(value, callback) { callback(relations(value)); })
                    .on('accept', onAccept)
                );


            var addrel = wrap.selectAll('.add-relation')
                .data([0]);

            addrel = addrel.enter()
                .append('button')
                .attr('class', 'add-relation')
                .merge(addrel);

            addrel
                .call(svgIcon('#icon-plus', 'light'))
                .on('click', function() {
                    showBlank = true;
                    content(wrap);
                    list.selectAll('.member-entity-input').node().focus();
                });


            function onAccept(d) {
                var role = list.selectAll('.member-row-new .member-role').property('value');
                addMembership(d, role);
            }


            function bindTypeahead(d) {
                var row = d3.select(this),
                    role = row.selectAll('input.member-role');

                function sort(value, data) {
                    var sameletter = [],
                        other = [];
                    for (var i = 0; i < data.length; i++) {
                        if (data[i].value.substring(0, value.length) === value) {
                            sameletter.push(data[i]);
                        } else {
                            other.push(data[i]);
                        }
                    }
                    return sameletter.concat(other);
                }

                role.call(d3combobox()
                    .container(context.container())
                    .fetcher(function(role, callback) {
                        var rtype = d.relation.tags.type;
                        taginfo.roles({
                            debounce: true,
                            rtype: rtype || '',
                            geometry: context.geometry(id),
                            query: role
                        }, function(err, data) {
                            if (!err) callback(sort(role, data));
                        });
                    }));
            }


            function unbind() {
                var row = d3.select(this);

                row.selectAll('input.member-role')
                    .call(d3combobox.off);
            }
        }
    }


    rawMembershipEditor.entityID = function(_) {
        if (!arguments.length) return id;
        id = _;
        return rawMembershipEditor;
    };


    return rawMembershipEditor;
}
