// Set up the document for testing

// May need setAttribute?
//Y.one("link:not([href^=http]),link[href=^http://localhost/]").set('id', 'locallink');
Y.one("link").set('id', 'locallink');
if (!Y.one("#testbed")) {
    Y.one("body").append('<div id="testbed"></div>');
}

var d = document,
    Assert      = Y.Assert,
    Dom         = {},
    StyleSheet  = Y.StyleSheet,
    suite       = new Y.Test.Suite("Y.StyleSheet"),
    StyleAssert = {},
    testbed     = d.getElementById('testbed'),
    style   = Y.Node.create('<style type="text/css" id="styleblock"></style>'),
    cssText = 'h1 { font: normal 125%/1.4 Arial, sans-serif; }';

if (!Y.one("#styleblock")) {
    Y.one("head").append(style);

    if (style._node.styleSheet) {
        style._node.styleSheet.cssText = cssText;
    } else {
        style.append(cssText);
    }
}

StyleAssert.normalizeColor = function (c) {
    return c && (c+'').
        replace(/#([0-9a-f])([0-9a-f])([0-9a-f])(?![0-9a-f])/i,'#$1$1$2$2$3$3').
        replace(/#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})(?![0-9a-f])/i,
            function (m,r,g,b) {
                return "rgb("+parseInt(r,16)+", "+
                              parseInt(g,16)+", "+
                              parseInt(b,16)+")";
            });
};
StyleAssert.areEqual = function (a,b,msg) {
    var aa = StyleAssert.normalizeColor(a),
        bb = StyleAssert.normalizeColor(b);

    if (aa === 'bold') { aa = '700'; }
    if (bb === 'bold') { bb = '700'; }

    if (aa !== bb) {
        throw new Assert.ComparisonFailure(
            Assert._formatMessage(msg, "Values should be equal."), a, b);
    }
};

Dom.add = function (el,tag,conf) {
    var child = d.createElement(tag);
    if (conf) {
        Y.mix(child,conf,true);
    }
    return el.appendChild(child);
};
Dom.getNodeCount = function (tag,root) {
    return (root||d).getElementsByTagName(tag||'*').length;
};
Dom.getSheet = function (yuid) {
    var nodes = d.getElementsByTagName('style'),
        i;

    for (i = nodes.length - 1; i >= 0; --i) {
        if (Y.stamp(nodes[i]) === yuid) {
            return (nodes[i].sheet || nodes[i].styleSheet);
        }
    }
};

suite.add(new Y.Test.Case({
    name : "Test &lt;style&gt; node creation",

    setUp : function () {
        this.styleNodeCount = Dom.getNodeCount('style');
        this.linkNodeCount  = Dom.getNodeCount('link');

        this.testNode = Dom.add(testbed,'div',{id:'target'});
    },

    tearDown : function () {
        testbed.innerHTML = '';
    },

    test_createNew : function () {
        Y.StyleSheet('test');

        Assert.areSame(this.styleNodeCount + 1, Dom.getNodeCount('style'));
    },

    test_createFromExistingStyle : function () {
        Y.StyleSheet('styleblock');

        Assert.areSame(this.styleNodeCount, Dom.getNodeCount('style'));
    },

    test_createFromExistingLink : function () {
        Y.StyleSheet('locallink');

        Assert.areSame(this.styleNodeCount, Dom.getNodeCount('style'),"style");
        Assert.areSame(this.linkNodeCount, Dom.getNodeCount('link'),"link");
    },

    test_createEntireSheet : function () {
        Y.StyleSheet("#target { font-weight: bold; }");

        Assert.areSame(this.styleNodeCount + 1, Dom.getNodeCount('style'));

        StyleAssert.areEqual('bold',Y.DOM.getStyle(this.testNode,'fontWeight'));
    },

    test_gettingFromCache : function () {
        // By name
        var a = new StyleSheet('test'),
            b = new StyleSheet('test');

        Assert.areSame(this.styleNodeCount, Dom.getNodeCount('style'));
        Assert.areSame(a,b,"From cache by name");

        // By generated id
        b = new StyleSheet(a.getId());

        Assert.areSame(this.styleNodeCount, Dom.getNodeCount('style'));
        Assert.areSame(a,b,"From cache by yuiSSID");

        // By node
        a = new StyleSheet(d.getElementById('styleblock'));
        b = new StyleSheet('styleblock');

        Assert.areSame(this.styleNodeCount, Dom.getNodeCount('style'));
        Assert.areSame(a,b,"From cache by node vs id");
    }
}));

suite.add(new Y.Test.Case({
    name : "Test xdomain stylesheet access",

    setUp : function () {
        this.remoteLink = Dom.add(
            d.getElementsByTagName('head')[0],'link',{
                type : 'text/css',
                rel  : 'stylesheet',
                href : 'http://yui.yahooapis.com/2.6.0/build/base/base-min.css'
            });
    },

    tearDown : function () {
        this.remoteLink.parentNode.removeChild(this.remoteLink);
    },

    _should : {
        error : {
            "StyleSheet seeded with remote link should fail"          : true,
            "getCssText on a remote StyleSheet should throw an error" : true,
            "set(..) on a remote StyleSheet should throw an error"    : true,
            "disabling a remote StyleSheet should throw an error"     : true
        }
    },

    "StyleSheet seeded with remote link should fail" : function () {
        // Each line should throw an exception
        Y.StyleSheet(this.remoteLink);

        Y.log("StyleSheet creation allowed from remote file", "warn", "TestRunner");
        throw Error("This is an informative test only");
    },

    "getCssText on a remote StyleSheet should throw an error" : function () {
        // Each line should throw an exception
        var sheet = Y.StyleSheet(this.remoteLink);

        sheet.getCssText();

        Y.log("Getting cssText of a remote StyleSheet allowed", "warn", "TestRunner");
        throw Error("This is an informative test only");
    },

    "set(..) on a remote StyleSheet should throw an error" : function () {
        // Each line should throw an exception
        var sheet = Y.StyleSheet(this.remoteLink);

        sheet.set('#target', { color: '#f00' });

        Y.log("Creating rules in a remote StyleSheet allowed", "warn", "TestRunner");
        throw Error("This is an informative test only");
    },

    "disabling a remote StyleSheet should throw an error" : function () {
        // Each line should throw an exception
        var sheet = Y.StyleSheet(this.remoteLink);

        sheet.disable();

        Y.log("Disabling a remote StyleSheet allowed", "warn", "TestRunner");
        throw Error("This is an informative test only");
    }
}));

suite.add(new Y.Test.Case({
    name : "Test set",

    _should: {
        fail: {
            test_important: 2528707  // bug
        }
    },

    setUp : function () {
        this.stylesheet = new StyleSheet('test');

        this.testNode = Dom.add(testbed,'div',{
            id:'target',
            innerHTML:'<p>1</p><p>2</p><pre>pre</pre>'
        });
    },
    tearDown : function () {
        testbed.innerHTML = '';
        this.stylesheet.unset('#target');
        this.stylesheet.unset('#target p');
        this.stylesheet.unset('#target pre');
        // This should be unnecessary, but for the sake of cleanliness...
        this.stylesheet.unset('#target, #target p, #target pre');
    },

    test_addSimpleSelector : function () {
        this.stylesheet.set('#target',{
            color           : '#123456',
            backgroundColor : '#eef',
            border          : '1px solid #ccc'
        });

        StyleAssert.areEqual('#123456',
                        Y.DOM.getStyle(this.testNode,'color'),
                        "color");
        StyleAssert.areEqual('#eef',
                        Y.DOM.getStyle(this.testNode,'backgroundColor'),
                        "backgroundColor");
        StyleAssert.areEqual('#ccc',
                        Y.DOM.getStyle(this.testNode,'borderLeftColor'),
                        "border");
    },

    test_addRuleWithInvalidValue : function () {
        // This would throw an exception in IE if anywhere
        this.stylesheet.set('#target .foo .bar', { color : 'invalid-value' });
    },

    test_descendantSelector : function () {
        var before = Y.DOM.getStyle(
                        testbed.getElementsByTagName('pre')[0],'textAlign');

        this.stylesheet.set('#target p', { textAlign: 'right' });

        StyleAssert.areEqual('right',
                        Y.DOM.getStyle(
                            testbed.getElementsByTagName('p')[0],'textAlign'),
                        "#target p { text-align: right; }");

        StyleAssert.areEqual(before,
                        Y.DOM.getStyle(
                            testbed.getElementsByTagName('pre')[0],'textAlign'),
                        "#target pre should not be set (maybe auto/inherit?)");
    },

    test_setCommaSelector : function () {
        var sheet = Dom.getSheet(this.stylesheet.getId());

        if (!sheet) {
            Assert.fail("Could not find this StyleSheet's node or sheet");
        }

        this.stylesheet.set('#target, #target p, #target pre', {
            paddingLeft: '16px'
        });

        Assert.areEqual(3,(sheet.cssRules || sheet.rules).length, "Comma selector split failure");

        StyleAssert.areEqual('16px', Y.DOM.getStyle(this.testNode,'paddingLeft'));
        StyleAssert.areEqual('16px',
                        Y.DOM.getStyle(
                            testbed.getElementsByTagName('p')[0],'paddingLeft'),
                        "#target p");
        StyleAssert.areEqual('16px',
                        Y.DOM.getStyle(
                            testbed.getElementsByTagName('pre')[0],'paddingLeft'),
                        "#target pre");
    },

    test_important: function () {
        var target   = Y.one('#target'),
            sheet    = Dom.getSheet(this.stylesheet.getId()),
            original = target.get('offsetHeight');

        if (!sheet) {
            Assert.fail("Could not find this StyleSheet's node or sheet");
        }

        this.stylesheet.set('#target p', {
            paddingBottom: '10px !important'
        });

        Assert.areEqual(1,(sheet.cssRules || sheet.rules).length, "!important rule not added to the sheet");

        Assert.areNotEqual(original, target.get('offsetHeight'));

        Assert.fail(); // remove when the bug is fixed
    }
}));

suite.add(new Y.Test.Case({
    name : "Test Enable/Disable sheet",

    setUp : function () {
        this.stylesheet = new StyleSheet('test');

        this.stylesheet.enable();

        this.testNode = Dom.add(testbed,'div',{id:'target'});

        this.before = {
            color           : Y.DOM.getStyle(this.testNode,'color'),
            backgroundColor : Y.DOM.getStyle(this.testNode,'backgroundColor'),
            borderLeftColor : Y.DOM.getStyle(this.testNode,'borderLeftColor')
        };

    },

    tearDown : function () {
        testbed.innerHTML = '';
        this.stylesheet.enable();
        this.stylesheet.unset('#target');
        this.stylesheet.unset('#target p');
    },

    test_disableSheet : function () {
        this.stylesheet.set('#target',{
            color           : '#123456',
            backgroundColor : '#eef',
            border          : '1px solid #ccc'
        });

        StyleAssert.areEqual('#123456',
                        Y.DOM.getStyle(this.testNode,'color'),
                        "color (enabled)");
        StyleAssert.areEqual('#eef',
                        Y.DOM.getStyle(this.testNode,'backgroundColor'),
                        "backgroundColor (enabled)");
        StyleAssert.areEqual('#ccc',
                        Y.DOM.getStyle(this.testNode,'borderLeftColor'),
                        "border (enabled)");

        this.stylesheet.disable();

        StyleAssert.areEqual(this.before.color,
                        Y.DOM.getStyle(this.testNode,'color'),
                        "color (disabled)");
        StyleAssert.areEqual(this.before.backgroundColor,
                        Y.DOM.getStyle(this.testNode,'backgroundColor'),
                        "backgroundColor (disabled)");
        StyleAssert.areEqual(this.before.borderLeftColor,
                        Y.DOM.getStyle(this.testNode,'borderLeftColor'),
                        "border (disabled)");
    },

    test_enableSheet : function () {
        this.stylesheet.disable();

        this.stylesheet.set('#target',{
            color           : '#123456',
            backgroundColor : '#eef',
            border          : '1px solid #ccc'
        });

        StyleAssert.areEqual(this.before.color,
                        Y.DOM.getStyle(this.testNode,'color'),
                        "color (disabled)");
        StyleAssert.areEqual(this.before.backgroundColor,
                        Y.DOM.getStyle(this.testNode,'backgroundColor'),
                        "backgroundColor (disabled)");
        StyleAssert.areEqual(this.before.borderLeftColor,
                        Y.DOM.getStyle(this.testNode,'borderLeftColor'),
                        "border (disabled)");

        this.stylesheet.enable();

        StyleAssert.areEqual('#123456',
                        Y.DOM.getStyle(this.testNode,'color'),
                        "color (enabled)");
        StyleAssert.areEqual('#eef',
                        Y.DOM.getStyle(this.testNode,'backgroundColor'),
                        "backgroundColor (enabled)");
        StyleAssert.areEqual('#ccc',
                        Y.DOM.getStyle(this.testNode,'borderLeftColor'),
                        "border (enabled)");
    }
}));

suite.add(new Y.Test.Case({
    name : "Test unset",

    setUp : function () {
        this.stylesheet = new StyleSheet('test');

        this.testNode = Dom.add(testbed,'div',{
            id:'target',
            innerHTML:'<p>1</p><p>2</p><pre>pre</pre>'
        });

        this.before = {
            color           : Y.DOM.getStyle(this.testNode,'color'),
            backgroundColor : Y.DOM.getStyle(this.testNode,'backgroundColor'),
            borderLeftColor : Y.DOM.getStyle(this.testNode,'borderLeftColor'),
            textAlign       : Y.DOM.getStyle(this.testNode,'textAlign')
        };

    },
    tearDown : function () {
        testbed.innerHTML = '';
        this.stylesheet.unset('#target');
        this.stylesheet.unset('#target p');
        this.stylesheet.unset('#target pre');
        // This should be unnecessary, but for the sake of cleanliness...
        this.stylesheet.unset('#target, #target p, #target pre');
    },

    test_unset : function () {
        this.stylesheet.set('#target',{
            color           : '#f00',
            backgroundColor : '#eef',
            border          : '1px solid #ccc'
        });

        StyleAssert.areEqual('#f00',
                        Y.DOM.getStyle(this.testNode,'color'),
                        "color (before unset)");
        StyleAssert.areEqual('#eef',
                        Y.DOM.getStyle(this.testNode,'backgroundColor'),
                        "backgroundColor (before unset)");
        StyleAssert.areEqual('#ccc',
                        Y.DOM.getStyle(this.testNode,'borderLeftColor'),
                        "border (before unset)");

        this.stylesheet.unset('#target', 'color');

        StyleAssert.areEqual(this.before.color,
                        Y.DOM.getStyle(this.testNode,'color'),
                        "color (after unset)");

        this.stylesheet.unset('#target', ['backgroundColor','border']);

        StyleAssert.areEqual(this.before.backgroundColor,
                        Y.DOM.getStyle(this.testNode,'backgroundColor'),
                        "backgroundColor (after unset)");
        StyleAssert.areEqual(this.before.borderLeftColor,
                        Y.DOM.getStyle(this.testNode,'borderLeftColor'),
                        "border (after unset)");
    },

    test_removeRule : function () {
        this.stylesheet.set('#target', { textAlign: 'right' });

        StyleAssert.areEqual('right',
                        Y.DOM.getStyle(this.testNode,'textAlign'),
                        "#target { text-align: right; }");

        this.stylesheet.unset('#target');
        StyleAssert.areEqual(this.before.textAlign,
                        Y.DOM.getStyle(this.testNode,'textAlign'),
                        "#target text-align still in place");
    },

    test_unsetCommaSelector : function () {
        var p      = this.testNode.getElementsByTagName('p')[0],
            pre    = this.testNode.getElementsByTagName('pre')[0],
            before = {
                paddingLeft:[
                    Y.DOM.getStyle(this.testNode,'paddingLeft'),
                    Y.DOM.getStyle(p,'paddingLeft'),
                    Y.DOM.getStyle(pre,'paddingLeft')
                ],
                marginRight:[
                    Y.DOM.getStyle(this.testNode,'marginRight'),
                    Y.DOM.getStyle(p,'marginRight'),
                    Y.DOM.getStyle(pre,'marginRight')
                ]
            },
            after,
            sheet = Dom.getSheet(this.stylesheet.getId());

        if (!sheet) {
            Assert.fail("Could not find this StyleSheet's node or sheet");
        }

        this.stylesheet.set('#target, #target p, #target pre', {
            marginRight: '30px',
            paddingLeft: '16px'
        });


        Assert.areEqual(3,(sheet.cssRules || sheet.rules).length,
                        "Comma selector split failure");


        this.stylesheet.unset('#target, #target p, #target pre','paddingLeft');

        after = [
            Y.DOM.getStyle(this.testNode,'paddingLeft'),
            Y.DOM.getStyle(p,'paddingLeft'),
            Y.DOM.getStyle(pre,'paddingLeft')
        ];

        Assert.areEqual(3,(sheet.cssRules || sheet.rules).length,
                        "Should still be 3 rules");

        Y.ArrayAssert.itemsAreEqual(before.paddingLeft,after);

        after = [
            Y.DOM.getStyle(this.testNode,'marginRight'),
            Y.DOM.getStyle(p,'marginRight'),
            Y.DOM.getStyle(pre,'marginRight')
        ];
        Y.ArrayAssert.itemsAreEqual(['30px','30px','30px'],after);
    },

    test_removeCommaSelector : function () {
        var /*p      = this.testNode.getElementsByTagName('p')[0],
            pre    = this.testNode.getElementsByTagName('pre')[0],
            before = {
                paddingLeft: [
                    Y.DOM.getStyle(this.testNode,'paddingLeft'),
                    Y.DOM.getStyle(p,'paddingLeft'),
                    Y.DOM.getStyle(pre,'paddingLeft')
                ]
            },
            */
            sheet = Dom.getSheet(this.stylesheet.getId());

        if (!sheet) {
            Assert.fail("Could not capture this StyleSheet's node or sheet");
        }

        this.stylesheet.set('#target, #target p, #target pre', {
            paddingLeft: '16px'
        });

        Assert.areEqual(3,(sheet.cssRules || sheet.rules).length,
                        "Comma selector split failure");

        this.stylesheet.unset('#target, #target pre','paddingLeft');

        Assert.areEqual(1,(sheet.cssRules || sheet.rules).length);
    }
}));

suite.add(new Y.Test.Case({
    name : "Test getCssText",

    _should: {
        fail: {
            test_important: true
        }
    },

    setUp : function () {
        this.stylesheet = new StyleSheet('test');

        this.testNode = Dom.add(testbed,'div',{
            id:'target',
            innerHTML:'<p>1</p><p>2</p><pre>pre</pre>'
        });

        this.stylesheet.set('#target, #target p', {
            padding: '3px'
        });
    },
    tearDown : function () {
        testbed.innerHTML = '';
        this.stylesheet.unset('#target');
        this.stylesheet.unset('#target p');
    },

    test_getRuleCSS : function () {
        var css = this.stylesheet.getCssText('#target p');
        Y.log(css, 'info','TestLogger');
        Assert.isString(css);
        Assert.areSame(true, /padding/i.test(css));
    },

    test_getSheetCSS : function () {
        var css = this.stylesheet.getCssText();

        Y.log(css, 'info','TestLogger');

        Assert.isString(css);
        Assert.areSame(true, /padding/i.test(css));
        Assert.areSame(true, /#target/i.test(css));
        Assert.areSame(true, /#target\s+p\s+\{/i.test(css));
    },

    test_important: function () {
        this.stylesheet.set('#target p', {
            paddingBottom: '10px !important'
        });

        var css = this.stylesheet.getCssText();

        if (/important/i.test(css)) {
            Y.log("!important not found in cssText", "warn", "TestRunner");
        }

        Assert.fail(); // remove when the bug is fixed
    }
}));

suite.add(new Y.Test.Case({
    name : "Test float/opacity",

    setUp : function () {
        this.stylesheet = new StyleSheet('test');

        if (!d.getElementById('target')) {
            this.testNode = Dom.add(testbed,'div',{
                id:'target',
                innerHTML:'<p id="p1">1</p><p id="p2">2</p><p id="p3">3</p>'
            });
        }
    },

    test_float : function () {
        var p1 = Y.DOM.byId('p1'),
            p2 = Y.DOM.byId('p2'),
            p3 = Y.DOM.byId('p3');

        this.stylesheet.set('#target',{
                            overflow: 'hidden',
                            background: '#000',
                            zoom: 1
                        })
                        .set('#target p',{
                            height:'100px',
                            width:'100px',
                            border: '5px solid #ccc',
                            background: '#fff',
                            margin: '1em'
                        })
                        .set('#p1',{ 'float': 'left' })
                        .set('#p2',{ cssFloat: 'left' })
                        .set('#p3',{ styleFloat: 'left' });

        Assert.areEqual('left', Y.DOM.getStyle(p1,'float'));
        Assert.areEqual('left', Y.DOM.getStyle(p2,'float'));
        Assert.areEqual('left', Y.DOM.getStyle(p3,'float'));
    },

    test_opacity : function () {
        var p1 = Y.DOM.byId('p1'),
            p2 = Y.DOM.byId('p2'),
            p3 = Y.DOM.byId('p3');

        this.stylesheet.set('#p1',{ opacity: 0.5 }).
                        set('#p2',{ opacity: ".2" }).
                        set('#p3',{ opacity: 1 });

        Assert.areEqual(0.5,Y.DOM.getStyle(p1,'opacity'));
        Assert.areEqual(0.2,Y.DOM.getStyle(p2,'opacity'));
        Assert.areEqual(1,Y.DOM.getStyle(p3,'opacity'));
    }
}));

suite.add(new Y.Test.Case({
    name: "Testbed Cleanup",

    testbedCleanup: function () {
        Y.all('#testbed,style').remove(true);
    }
}));

Y.Test.Runner.add(suite);
