if (typeof Hatena == 'undefined') {
    var Hatena = { };
}
if (typeof Hatena.UgoMemo == 'undefined') {
    Hatena.UgoMemo = { };
}
if (typeof Hatena.UgoMemo.ChannelSuggest == 'undefined') {
    Hatena.UgoMemo.ChannelSuggest = { };
}

Hatena.UgoMemo.ChannelSuggest = function() {
    this.inputForm    = Ten.querySelector('form.add-channel');
    if (!this.inputForm) return;
    this.inputElement = Ten.querySelector('input#channel-name-form-text');
    if (!this.inputElement) return;
    this.completions  = new Hatena.UgoMemo.ChannelSuggest.Completions(this.inputElement);
    this.searcher     = new Hatena.UgoMemo.ChannelSuggest.Searcher(this.completions);
    this.bindEvents();
};

Hatena.UgoMemo.ChannelSuggest.prototype = {
    inputForm: null,
    inputElement: null,
    completions: null,
    isMouseOver: false,
    isFocused: false,
    bindEvents: function() {
        var self = this;
        var events;
        events = ['onkeyup', 'onclick']; // , 'onchange'
        for(var i = 0; i < events.length; i++) {
            new Ten.Observer(self.inputElement, events[i], self, 'handleQueryChange');
        }
        events = ['onkeydown'];
        for(var i = 0; i < events.length; i++) {
            new Ten.Observer(self.inputElement, events[i], self, 'handleOperationKey');
        }

        new Ten.Observer(self.inputElement, 'onfocus', self, 'handleFocus');
        // new Ten.Observer(self.inputElement, 'onblur', self, 'handleBlur');

        new Ten.Observer(self.completions.containerElement, 'onmouseover', self, 'handleMouseOver');
        new Ten.Observer(self.completions.containerElement, 'onmouseout', self, 'handleMouseOut');
        new Ten.Observer(self.completions.containerElement, 'onclick', self, 'handleClick');
    },
    handleQueryChange: function(event) {
        var self = this;

        if (event.isKey('enter')) return;

        self.searcher.setQuery(self.inputElement.value);
    },
    handleOperationKey: function(event) {
        var self = this;

        if (event.isKey('enter')) {
            var item = self.completions.getCurrentItemElement();
            if (!item) return;
            self.selectChannel(item.getAttribute('data-channel-name'));
            event.stop();
            return;
        }

        if (event.isKey('down')) {
            self.completions.selectDown();
            event.stop();
            return;
        }

        if (event.isKey('up')) {
            self.completions.selectUp();
            event.stop();
            return;
        }
    },
    handleFocus: function(event) {
        var self = this;

        self.isFocused = true;
        self.searcher.setQuery(self.inputElement.value);
        self.searcher.startObserve();
    },
    handleBlur: function(event) {
        var self = this;

        self.isFocused = false;
        self.closeIfNeeded();
    },
    handleMouseOver: function(event) {
        var self = this;

        self.isMouseOver = true;
        if (event.target.tagName.toUpperCase() != "LI") return;
        self.completions.selectByIndex(+event.target.getAttribute('data-item-index'));
        self.closeIfNeeded();
    },
    handleMouseOut: function(event) {
        var self = this;

        self.isMouseOver = false;
        self.closeIfNeeded();
    },
    handleClick: function(event) {
        var self = this;

        var target = event.target;
        while(target && target.tagName.toUpperCase() != "DIV") {
            var channelName = target.getAttribute('data-channel-name');
            if (target.tagName.toUpperCase() == "A") {
                return;
            }
            if (channelName) {
                self.selectChannel(channelName);
                return;
            }
            target = target.parentNode;
        }
    },
    closeIfNeeded: function() {
        var self = this;
        if (!(self.isFocused ||self.isMouseOver)) {
            self.closeTimer = setTimeout(function() {
                self.searcher.stopObserve();
                self.searcher.setQuery('');
                self.completions.hide();
            }, 100);
        } else if (self.closeTimer) {
            clearTimeout(self.closeTimer);
            self.closeTimer = null;
        }
    },
    selectChannel: function(channel_name) {
        var self = this;
        self.inputElement.value = channel_name;
        self.completions.hide();
        self.searcher.setQuery('');
        var submit = Ten.querySelector('input#channel-name-form-submit', self.inputForm);
        if (submit) submit.focus();
    }
};

Hatena.UgoMemo.ChannelSuggest.Searcher = function(completions) {
    this.completions = completions;
}

// ajax (with cache), showItems, throttle, grep by regexp
Hatena.UgoMemo.ChannelSuggest.Searcher.prototype = {
    query: '',
    lastFetchedQuery: '',
    completions: null,
    cache: { },
    lastRequestedAt: 0,
    observeTimer: null,
    isRequesting: false,
    startObserve: function() {
        var self = this;
        if (this.observeTimer) return;

        self.observeTimer = setInterval(function() {
            self.observe();
        }, 200);
    },
    stopObserve: function() {
        var self = this;
        if (!self.observeTimer) return;

        clearInterval(self.observeTimer);
        self.observeTimer = null;
    },
    setQuery: function(query) {
        var self = this;

        query = self.normalizeQuery(query);

        if (query == self.query) return;

        self.query = query;

        if (query.length == 0) {
            self.setItems([]);
        }

        for (var i = query.length; i > 0; i--) {
            var items = self.cache[query.substring(0, i)];
            if (items) {
                self.setItems(items);
                break;
            }
        }

        items = self.completions.currentItems;
        if (items && items.length > 0) {
            self.setItems(items);
        }
        if (self.canFetch()) {
            self.fetchQuery(query);
        }
    },
    // internal
    observe: function() {
        var self = this;
        if (self.lastFetchedQuery == self.query) return;
        if (!self.canFetch()) return;
        if (!self.query || self.query.length == 0) return;

        self.fetchQuery(self.query);
        self.lastFetchedQuery = self.query;
    },
    normalizeQuery: function(query) { // 先頭のスペースを取る
        var self = this;
        query = query.replace(/^\s*/, '');
        return query;
    },
    canFetch: function() {
        var self = this;
        var now = new Date().getTime();
        if (!self.isRequesting) return true;
        if (now - self.lastRequestedAt > 5000) { // timeout
            self.isRequesting = false;
            return true;
        }
        return false;
    },
    fetchQuery: function(query) {
        var self = this;
        if (!query || query.length == 0) return;

        var uri = '/channels?mode=js&word=' + encodeURIComponent(query);
        new Ten.JSONP(uri, function(res) { self.handleReceived(res, query); });

        Ten.querySelector('form.add-channel .indicator').style.visibility = 'visible';

        self.lastRequestedAt = new Date().getTime();
        self.isRequesting = true;
    },
    filterItems: function(items, query) {
        var self = this;
        var newItems = [];

        if (query.length == 0) {
            return [];
        }

        for(var i = 0; i < items.length; i++) {
            if (items[i].name.indexOf(query) == 0) {
                newItems.push(items[i]);
            }
        }

        return newItems;
    },
    handleReceived: function(res, query) {
        var self = this;
        Ten.querySelector('form.add-channel .indicator').style.visibility = 'hidden';
        var items = res.channels;
        self.cache[query] = items;
        if (self.query.indexOf(query) == 0) {
            self.setItems(items);
        }
        self.observe();
    },
    setItems: function(items) {
        var self = this;
        self.completions.showItems(self.filterItems(items, self.query), self.query);
    }
}

Hatena.UgoMemo.ChannelSuggest.Completions = function(targetElement) {
    this.targetElement = targetElement;
    this.containerElement = Ten.querySelector('#channel-name-completions-container');
}

Hatena.UgoMemo.ChannelSuggest.Completions.prototype = {
    elementPosition: null,
    containerElement: null,
    itemsElement: null,
    currentIndex: 0,
    currentItems: [],
    getCurrentItemElement: function() {
        var self = this;

        if (!self.itemsElement) return null;
        return Ten.querySelectorAll('li', self.itemsElement)[self.currentIndex];
    },
    showItems: function(items, query) {
        var self = this;

        if (!items || items.length == 0) {
            self.hide();
            return;
        }

        var itemsElement = Ten.Element('ul', {id: 'channel-suggest-list'});

        if (self.currentIndex >= items.length) {
            self.currentIndex = 0;
        }

        var rule = new RegExp('^' + query);
        for(var i = 0; i < items.length; i++) {
            var itemElement = document.createElement('li');
            var name = items[i].name;
            itemElement.appendChild(Ten.Element('img', { className: 'channel-thumbnail', src: items[i].image, width: 32, height: 32 }));
            if (name.match(rule)) {
                var b = document.createElement('b');
                b.appendChild(document.createTextNode(query));
                itemElement.appendChild(b);
                itemElement.appendChild(document.createTextNode(name.replace(rule, '')));
            } else {
                itemElement.appendChild(document.createTextNode(name));
            }
            var countElement = Ten.Element(
                'a',
                {
                    className: 'movie-count',
                    href:      items[i].path,
                    target:    '_blank'
                }
            );
            countElement.appendChild(Ten.Element(
                'img',
                {
                    src: '/images/icon-memos-s.gif'
                }
            ));
            countElement.appendChild(document.createTextNode(items[i].movie_count));
            itemElement.appendChild(countElement);
            itemElement.setAttribute('data-channel-name', items[i].name);
            itemElement.setAttribute('data-item-index', i);
            if (i == self.currentIndex) {
                Ten.DOM.addClassName(itemElement, 'selected');
            }
            itemsElement.appendChild(itemElement);
        }
        if (self.itemsElement) {
            Ten.DOM.replaceNode(itemsElement, self.itemsElement);
        } else {
            self.containerElement.appendChild(itemsElement);
        }
        self.itemsElement = itemsElement;
        self.currentItems = items;
    },
    selectByIndex: function(index) {
        var self = this;
        if (isNaN(index)) return;
        if (self.currentIndex == index) return;
        Ten.DOM.removeClassName(self.getCurrentItemElement(), 'selected');
        self.currentIndex = index;
        if (self.currentItems.length <= self.currentIndex) {
            self.currentIndex = 0;
        }
        if (0 > self.currentIndex) {
            self.currentIndex = self.currentItems.length - 1;
        }
        Ten.DOM.addClassName(self.getCurrentItemElement(), 'selected');
    },
    selectDown: function() {
        var self = this;
        self.selectByIndex(self.currentIndex + 1);
    },
    selectUp: function() {
        var self = this;
        self.selectByIndex(self.currentIndex - 1);
    },
    selectThis: function() {
    },
    hide: function() {
        var self = this;

        if (!self.itemsElement) return;
        Ten.DOM.removeElement(self.itemsElement);
        self.currentItems = [];
        self.itemsElement = null;
    }
};

Ten.DOM.addEventListener('DOMContentLoaded', function() {
    new Hatena.UgoMemo.ChannelSuggest();
});

