/*
Copyright (c) 2015, Aaron van Geffen
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted
provided that the following conditions are met:

- Redistributions of source code must retain the above copyright notice, this list of conditions
  and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions
  and the following disclaimer in the documentation and/or other materials provided with the distribution.
*/

'use strict';

class AutoSuggest {
    constructor(opt) {
        if (typeof opt.inputElement === "undefined" || typeof opt.listElement === "undefined" ||
            typeof opt.baseUrl === "undefined" || typeof opt.appendCallback === "undefined") {
            return;
        }

        this.input = document.getElementById(opt.inputElement);
        this.input.autocomplete = "off";
        this.list = document.getElementById(opt.listElement);
        this.appendCallback = opt.appendCallback;
        this.baseurl = opt.baseUrl;

        this.input.addEventListener('keydown', event => this.doSelection(event), false);
        this.input.addEventListener('keyup', event => this.onType(event), false);
    }

    doSelection(event) {
        if (typeof this.container === "undefined" || this.container.children.length === 0) {
            return;
        }

        switch (event.key) {
            case 'Enter':
                event.preventDefault();
                this.container.children[this.selectedIndex].click();
                break;

            case 'ArrowUp':
            case 'ArrowDown':
                event.preventDefault();
                this.findSelectedElement().className = '';
                this.selectedIndex += event.key === 'ArrowUp' ? -1 : 1;
                if (this.selectedIndex < 0) {
                    this.selectedIndex = this.container.children.length - 1;
                } else if (this.selectedIndex === this.container.children.length) {
                    this.selectedIndex = 0;
                }
                let new_el = this.findSelectedElement().className = 'selected';
                break;
        }
    };

    findSelectedElement() {
        return this.container.children[this.selectedIndex];
    };

    onType(event) {
        if (['Enter', 'ArrowDown', 'ArrowUp'].indexOf(event.key) !== -1) {
            return;
        }

        let tokens = event.target.value.split(/\s+/).filter(token => token.length >= 2);

        if (tokens.length === 0) {
            if (typeof this.container !== "undefined") {
                this.clearContainer();
            }
            return false;
        }

        let request_uri = this.baseurl + '/suggest/?type=tags&data=' + window.encodeURIComponent(tokens.join(" "));
        let request = new HttpRequest('get', request_uri, {}, this.onReceive, this);
    };

    onReceive(response, self) {
        self.openContainer();
        self.clearContainer();
        self.fillContainer(response);
    };

    openContainer() {
        if (this.container) {
            if (!this.container.parentNode) {
                this.input.parentNode.appendChild(this.container);
            }
            return this.container;
        }

        this.container = document.createElement('ul');
        this.container.className = 'autosuggest';
        this.input.parentNode.appendChild(this.container);
        return this.container;
    };

    clearContainer() {
        while (this.container.children.length > 0) {
            this.container.removeChild(this.container.children[0]);
        }
    };

    clearInput() {
        this.input.value = "";
        this.input.focus();
    };

    closeContainer() {
        this.container.parentNode.removeChild(this.container);
    };

    fillContainer(response) {
        this.selectedIndex = 0;

        let query = this.input.value.trim().replace(/[\-\[\]{}()*+?.,\\\/^\$|#]/g, ' ');
        let query_tokens = query.split(/ +/).sort((a,b) => a.length - b.length);

        response.items.forEach((item, i) => {
            let node = document.createElement('li');
            node.innerHTML = this.highlightMatches(query_tokens, item.label);
            node.jsondata = item;
            node.addEventListener('click', event => {
                this.appendCallback(node.jsondata);
                this.closeContainer();
                this.clearInput();
            });
            this.container.appendChild(node);
            if (this.container.children.length === 1) {
                node.className = 'selected';
            }
        });
    };

    highlightMatches(query_tokens, item) {
        let itemTokens = item.split(/ +/);
        let queryTokens = new RegExp('(' + query_tokens.join('\|') + ')', 'i');
        itemTokens.forEach((token, index) => {
            item = item.replace(token, token.replace(queryTokens, ($1, match) => '<strong>' + match + '</strong>'));
        });
        return item;
    };
}

class TagAutoSuggest extends AutoSuggest {
    constructor(opt) {
        super(opt);
        this.type = "tags";
    }

    fillContainer(response) {
        if (response.items.length > 0) {
            super.fillContainer.call(this, response);
        } else {
            let node = document.createElement('li')
            node.innerHTML = "<em>Tag does not exist yet. Create it?</em>";

            node.addEventListener('click', event => {
                this.createNewTag(response => this.appendCallback(response));
                this.closeContainer();
                this.clearInput();
            });

            this.container.appendChild(node);
            this.selectedIndex = 0;
            node.className = 'selected';
        }
    };

    createNewTag(callback) {
        let request_uri = this.baseurl + '/suggest/?type=createtag';
        let request = new HttpRequest('post', request_uri, 'tag=' + encodeURIComponent(this.input.value), callback, this);
    }
}