var samples = {

    langs: function () {
        if (!this._langs) {
            this._langs = [];
            var self = this;
            $("#toggle_code_lang a").each(function () {
                self._langs.push($(this).attr("id"));
            });
        }
        return this._langs;
    },

    sections: function () {
        if (!this._sections) {
            this._sections = [];
            var hasAllLangs = "";
            for (var i = 0; i < this.langs().length; i++) {
                hasAllLangs += ":has(div.highlight-" +
                    this.langs()[i] + ")";
            }
            this._sections = $("div.body div.section").children();
        }
        return this._sections;
    },

    hide: function () {
        for (var i = 0; i < this.langs().length; i++) {
            var id = this.langs()[i];
            this.hideSamples(id);
        }
    },

    show: function () {
        this.change($.cookie('samples_code_language') || 'bash');
    },

    change: function (to, from) {
        from = from || this.current;
        this.rememberPosition();
        if (!from) {
            this.hide();
        } else {
            this._prev = from;
            if (this.hasSamplesFor(to))
                this.hideSamples(from);
        }
        if (this.hasSamplesFor(to))
            this.showSamples(to);
        this.scrollBack();
        var date = new Date();
        date.setDate(date.getDate() + 365);
        $.cookie('samples_code_language', to,
                 { expires: date});
    },

    isSampleOutput: function (elem) {
        return $(elem).hasClass('highlight-javascript')
    },

    hasSamplesFor: function (lang) {
        return ($("div.body div.section").children(":has(div.highlight-" + lang + ")").length != 0);
    },

    sourceOnly: function (sample) {
        var next = sample.next();
        while (next.attr("class") != undefined) {
            if (next.attr("class").indexOf("highlight") == -1) {
                next = next.next();
            } else {
                return !this.isSampleOutput(next);
            }
        }
        return true;
    },

    setDisplay: function (id, display) {
        for (var i = 0;i < this.sections().length; i++) {
            var section = $(this.sections()[i]);
            var samples = section.children("div.highlight-" + id);
            var self = this;
            samples.each(function () {
                var sample = $(this);
                if (self.sourceOnly(sample)) {
                    sample.css('display', display);  // Sample code block.
                } else {
                    // Hide needed sample with comments and result block.
                    // Hide opening lines.
                    sample.prev().css('display', display);
                    // Hide sample code block.
                    sample.css('display', display);
                    // 'Response:' line
                    var sibling = sample.next();
                    while (!self.isSampleOutput(sibling)) {
                        sibling.css('display', display);
                        sibling = sibling.next();
                    }
                    // Response block.
                    sibling.css('display', display);
                }

            });
        }
    },

    showSamples: function (id) {
        this.setDisplay(id, 'block');
        $("#toggle_code_lang #" + id).attr('class', 'current');
        this.current = id
    },

    hideSamples: function (id) {
        this.setDisplay(id, 'none');
        $("#toggle_code_lang #" + id).removeClass('current');
    },

    rememberPosition: function () {
        var self = this;
        self._mark = undefined;
        self._mark =
            $(_.find($("div.body div:visible, div.body p:visible"),
                     function (elem) {
                         return $(elem).offset().top >
                             $("div.document").offset().top &&
                             $(elem).offset().top <
                             $("div.document").offset().top +
                             $("div.document").height()
                     }));
        if (self._mark.length > 0) {
            self._markDiff = self._mark.offset().top -
                $("div.document").offset().top;
        } else {
            self._mark = undefined;
        }

    },

    parentSection: function (sample) {
        var parent = $(sample).parent();
        while(parent.index() > 0 && !parent.hasClass("section")) {
            parent = parent.parent();
        }
        if (parent.index() < 0) {
            parent = undefined;
        }
        return parent
    },

    sampleAnalogy: function (section, openingLines) {
        var sample = _.find(section.find("div.highlight-" +
                                         this.current),
                            function (e) {
                                return $(e).prev().text() ==
                                    openingLines;
                            });
        return $(sample);
    },

    scrollBack: function () {
        var self = this;
        if (this._mark) {
            // if now the mark is invisible
            // this means the mark is a part of some sample group
            if (this._mark.css("display") == "none") {
                // find section
                var section = self.parentSection(this._mark);
                // find analogy in the same section
                var analogy = undefined;
                // analogy means that sample block should have the same opening
                // lines
                // Note: sample block consists of:
                // 1. Opening lines
                // 2. Sample
                // 3. Response lines
                // 4. Sample output
                var openingLines = 'qqq';
                if (this.isSampleOutput(this._mark)) {
                    // 4. the mark is the output part of the sample
                    // find the opening line of the mark
                    var sibling = this._mark.prev();
                    while (!sibling.hasClass("highlight-" +
                                             this._prev)) {
                        sibling = sibling.prev();
                    }
                    openingLines = sibling.prev().text();
                    // find sample analogy
                    var sample = self.sampleAnalogy(section, openingLines);
                    // find sample part analogy
                    sibling = $(sample).next();
                    while (!this.isSampleOutput(sibling)) {
                        sibling = sibling.next();
                    }
                    analogy = $(sibling);
                } else if (this._mark.hasClass("highlight-" + this._prev)) {
                    // 2. the mark is the sample part of the sample
                    openingLines = this._mark.prev().text();
                    var sample = self.sampleAnalogy(section, openingLines);
                    analogy = $(sample);
                } else if (this._mark.next().hasClass("highlight-" +
                                                      this._prev)) {
                    // 1. the mark is the opening lines part
                    openingLines = this._mark.text();
                    var sample = self.sampleAnalogy(section, openingLines);
                    analogy = $(sample).prev();
                } else {
                    // 3. the mark is the response lines part
                    openingLines = this._mark.prev().prev().text();
                    var sample = self.sampleAnalogy(section, openingLines);
                    analogy = $(sample).next();
                }
            } else {
                analogy = this._mark;
            }
            var analogyMarkDiff = analogy.offset().top -
                $("div.document").offset().top;
            var scrollDiff = analogyMarkDiff - this._markDiff;
            $("div.document").scrollTop($("div.document").scrollTop() +
                                        scrollDiff);
            this._mark = undefined;
        }
    }
};

$(function () {
    samples.show();
    $("#toggle_code_lang a").click(function(e){
        e.preventDefault();
        samples.change( $(this).attr("id"));
    });
});

