/*global
 $: false
 */
/*
 * Pagalbinės funkcijos, kurias kviečia teisės akto peržiūra/spausdinimas
 */
var mLegalActFrame = (function () {
    'use strict';
    var MODULE_NAME = 'mLegalActFrame';

    function assert(test, message) {
        if (test) {
            throw message || 'error';
        }
    }

    function makeFrameContents(frame) {
        if (frame.is('iframe')) {
            return frame.contents();
        }
        return frame;
    }

    function escapeQuery(query) {
        return query.replace(/([^\-_a-zA-Z0-9])/g, '\\$1');
    }

    /**
     * finds all descendants of root (including root if needed), that
     * corresponds to given selector
     */
    function queryWithoutChildren(root, selector) {
        var breath = $(root), result = $();

        while (true) {
            result = result.add(breath.filter(selector));
            breath = breath.not(selector).children();
            if (!breath.size()) {
                break;
            }
        }

        return result;
    }

    function waitForFrameToLoad(frame, onFrameLoad) {
        var framedoc, readyState;
        frame = $(frame);

        assert(frame.size() === 0, 'invalid frame given');

        framedoc = frame.get(0);
        framedoc = framedoc.contentWindow || framedoc.contentDocument;
        if (framedoc.document) {
            framedoc = framedoc.document;
        }
        readyState = framedoc.readyState.toLowerCase();

        if (
            readyState === 'complete' &&
            frame.contents().find('body').children().size() !== 0 // by default chrome loads empty page which should not be counted as loaded
        ) {
            onFrameLoad(frame.contents());
        } else {
            frame.on('load', function () {
                onFrameLoad(frame.contents());
            });
        }
    }

    function frameLoadClosure(func) {
        return function (frame) {
            var args = Array.prototype.slice.call(arguments, 1);
            if ($(frame).is('iframe')) {
                waitForFrameToLoad(frame, function (frameContents) {
                    func.apply(this, [frameContents].concat(args));
                });
            } else {
                // frameContents there given instead of an actual frame
                func.apply(this, [frame].concat(args));
            }
        };
    }

    function scrollTo(frameContents, pos, timeInMS) {
        frameContents.find('body, html').stop(false, false).animate({
            scrollTop: Math.max(0, pos)
        }, timeInMS || 0);
    }

    function findPartIdPos(frameContents, partId) {
        if (!partId) {
            return null;
        }
        frameContents = makeFrameContents(frameContents);
        var element = frameContents.find('#' + escapeQuery(partId) + ',a[name=' + escapeQuery(partId) + ']').first();
        if (partId && element.size() !== 0) {
            return element.offset().top - 20;
        }
        return null;
    }

    function findFramesScrollPos(frameContents) {
        frameContents = makeFrameContents(frameContents);
        return Math.max(frameContents.find('html:first').scrollTop() || 0, frameContents.find('body:first').scrollTop() || 0);
    }

    function scrollToPartId(frameContents, partId, timeInMS) {
        frameContents = makeFrameContents(frameContents);
        var pos = findPartIdPos(frameContents, partId);
        if (pos !== null) {
            scrollTo(frameContents, pos, timeInMS);
        }
    }

    function showHiddenFromView(frameContents) {
        var body = frameContents.find('body'), state = body.data(MODULE_NAME + '-saved-state');
        if (state) {
            body.children().remove();
            body.append(state.clone());
        } else {
            body.data(MODULE_NAME + '-saved-state', body.children().clone());
        }
    }

    function viewOnly(frameContents, partId) {
        showHiddenFromView(frameContents);
        var body = frameContents.find('body'), elements = queryWithoutChildren(body.children(), '#' + escapeQuery(partId)).clone();
        body.children().remove();
        body.append(elements);
        scrollTo(frameContents, 0);
    }

    function explode(frameContents) {
        var html = $('html');
        html.children().remove();
        html.append(frameContents.find('html').children().clone());
    }

    function loadHiddenFrame(src, onFrameLoad) {
        var frame = $('<iframe />').css({
            visibility: 'hidden',
            position: 'absolute',
            left: -10000,
            top: -10000
        });
        frame.appendTo($('body'));

        waitForFrameToLoad(frame, function (frameContents) {
            var baseUrl = src;
            baseUrl = baseUrl.split('/');
            if (baseUrl && baseUrl.length) {
                baseUrl.pop();
            }
            baseUrl = baseUrl.join('/') + '/';
            // it is practically impossible to append base tag so rewrite urls our self
            frameContents.find('img').each(function () { var u = $(this).attr('src'); if (u && u.substr(0, 4) !== 'http') $(this).attr('src', baseUrl + u); });
            frameContents.find('a').each(function () { var u = $(this).attr('href'); if (u && u.substr(0, 4) !== 'http') $(this).attr('href', baseUrl + u); });
            onFrameLoad(frameContents);
        });

        frame.attr('src', src);
    }

    function targetLinksToParent(frameContents) {
        frameContents.find('a').each(function () {
            var self = $(this);
            if (!self.attr('target')) {
                self.attr('target', '_parent');
            }
        });
    }

    function addHeader(frameContents, header) {
        frameContents.find('body').prepend($(header));
    }

    return {
        scrollTo: frameLoadClosure(scrollTo),
        viewOnly: frameLoadClosure(viewOnly),
        explode: frameLoadClosure(explode),
        showHiddenFromView: frameLoadClosure(showHiddenFromView),
        addHeader: frameLoadClosure(addHeader),
        targetLinksToParent: frameLoadClosure(targetLinksToParent),
        // frame must be loaded for these
        scrollToPartId: scrollToPartId,
        findPartIdPos: findPartIdPos,
        findFramesScrollPos: findFramesScrollPos,
        // frame loading
        loadHiddenFrame: loadHiddenFrame,
        waitForFrameToLoad: waitForFrameToLoad
    };
}());
