var fs = require('fs');
var assert = require('assert');
var csso = require('../lib/index.js');
var SourceMapConsumer = require('source-map').SourceMapConsumer;
var gonzalesToInternal = require('../lib/compressor/ast/gonzalesToInternal.js');
var internalTranslateWithSourceMap = require('../lib/compressor/ast/translateWithSourceMap.js');
var forEachTest = require('./fixture/internal').forEachTest;
var css = '.a { color: #ff0000; }\n.b { display: block; float: left; }';
var minifiedCss = '.a{color:red}.b{display:block;float:left}';
var anonymousMap = defineSourceMap('<unknown>');
var filenameMap = defineSourceMap('test.css');
var points = ['.a', 'color', '.b', 'display', 'float'];

function getOriginalPosition(str, source, filename) {
    var index = source.indexOf(str);
    var line = null;
    var column = null;

    if (index !== -1) {
        var lines = source.substr(0, index).split('\n');
        line = lines.length;
        column = lines.pop().length;
    }

    return {
        source: filename || '<unknown>',
        line: line,
        column: column,
        name: null
    };
}

function getGeneratedPosition(str, source) {
    var index = source.indexOf(str);
    var line = null;
    var column = null;

    if (index !== -1) {
        var lines = source.substr(0, index).split('\n');
        line = lines.length;
        column = lines.pop().length;
    }

    return {
        line: line,
        column: column,
        lastColumn: null
    };
}

function defineSourceMap(filename) {
    var string = '{"version":3,"sources":["' + filename + '"],"names":[],"mappings":"AAAA,E,CAAK,S,CACL,E,CAAK,a,CAAgB,U","sourcesContent":[' + JSON.stringify(css) + ']}';
    var base64 = new Buffer(string, 'utf8').toString('base64');
    var inline = '/*# sourceMappingURL=data:application/json;base64,' + base64 + ' */';

    return {
        string: string,
        base64: base64,
        inline: inline
    };
}

function extractSourceMap(source) {
    var m = source.match(/\/\*# sourceMappingURL=data:application\/json;base64,(.+) \*\//);

    if (m) {
        return new Buffer(m[1], 'base64').toString();
    }
}

function createInternalTranslateWidthSourceMapTest(name, test, scope) {
    it(name, function() {
        var ast = csso.parse(test.source, scope, true);
        var internalAst = gonzalesToInternal(ast);

        // strings should be equal
        assert.equal(internalTranslateWithSourceMap(internalAst).css, test.translate);
    });
}

describe('sourceMaps', function() {
    describe('translateWithSourceMap', function() {
        forEachTest(createInternalTranslateWidthSourceMapTest);
    });

    it('should return object if sourceMap is true', function() {
        var result = csso.minify(css, { sourceMap: true });

        assert(typeof result === 'object');
        assert('css' in result, 'should has `css` property');
        assert('map' in result, 'should has `map` property');
    });

    it('should return object if sourceMap is truly value', function() {
        var result = csso.minify(css, { sourceMap: 'anything' });

        assert(typeof result === 'object');
        assert('css' in result, 'should has `css` property');
        assert('map' in result, 'should has `map` property');
    });

    it('should return object if sourceMap is `inline`', function() {
        var result = csso.minify(css, { sourceMap: 'inline' });

        assert(typeof result === 'object');
        assert('css' in result, 'should has `css` property');
        assert('map' in result, 'should has `map` property');
    });

    it('should not add inline map when sourceMap is not `inline`', function() {
        var result = csso.minify(css, {
            sourceMap: true
        });

        assert.equal(result.css, minifiedCss);
    });

    it('should add inline map when sourceMap is `inline` and no filename', function() {
        var result = csso.minify(css, {
            sourceMap: 'inline'
        });

        assert.equal(result.css, minifiedCss + '\n' + anonymousMap.inline);
    });

    it('should add inline map when sourceMap is `inline` and filename', function() {
        var result = csso.minify(css, {
            sourceMap: 'inline',
            filename: 'test.css'
        });

        assert.equal(result.css, minifiedCss + '\n' + filenameMap.inline);
    });

    describe('check positions', function() {
        var result = csso.minify(css, {
            sourceMap: true
        });
        var consumer = new SourceMapConsumer(result.map.toString());

        points.forEach(function(str) {
            describe(str, function() {
                var original = getOriginalPosition(str, css);
                var generated = getGeneratedPosition(str, minifiedCss);

                it('generated->original', function() {
                    assert.deepEqual(consumer.originalPositionFor(generated), original);
                });
                it('original->generated', function() {
                    assert.deepEqual(consumer.generatedPositionFor(original), generated);
                });
            });
        });
    });

    describe('input source map', function() {
        var filename = __dirname + '/fixture/sourceMaps/autoprefixer.css';
        var source = fs.readFileSync(filename, 'utf8');
        var sourceMap = JSON.parse(extractSourceMap(source));
        var sourceContent = sourceMap.sourcesContent[0];
        var result = csso.minify(source, {
            filename: filename,
            inputSourceMap: sourceMap,
            sourceMap: true
        });
        var consumer = new SourceMapConsumer(result.map.toString());

        // generated -> original
        var mapping = {
            '.a': '.a',
            '-webkit-fi1ter': 'filter',
            'filter': 'filter',
            '.b': '.b',
            '-webkit-padding-before': 'padding-block-start',
            'padding-block-start': 'padding-block-start'
        };

        Object.keys(mapping).forEach(function(generatedKey) {
            describe(generatedKey, function() {
                var original = getOriginalPosition(mapping[generatedKey], sourceContent, 'source.css');
                var generated = getGeneratedPosition(generatedKey, result.css);

                it('generated->original', function() {
                    assert.deepEqual(consumer.originalPositionFor(generated), original);
                });
                it('original->generated', function() {
                    assert(consumer.allGeneratedPositionsFor(original).some(function(position) {
                        return position.line === generated.line &&
                               position.column === generated.column;
                    }), 'generated position should in generated positions list');
                });
            });
        });
    });
});

@@ -3778,239 +3778,36 @@
     
-%7D);%0A%0A    it('should add inline map when sourceMap is %60inline%60 and no filename', function() %7B%0A        var result = csso.minify(css, %7B%0A            sourceMap: 'inline'%0A        %7D);%0A%0A        assert.equal(result.css, minifiedCss + '%5Cn' +
+    assert.equal(result.map,
  ano
@@ -3812,30 +3812,30 @@
 nonymousMap.
-inline
+string
 );%0A    %7D);%0A%0A
@@ -3982,24 +3982,20 @@
 rceMap: 
-'inline'
+true
 ,%0A      
@@ -4082,17 +4082,43 @@
 dCss
- + '%5Cn' +
+);%0A        assert.equal(result.map,
  fil
@@ -4126,22 +4126,22 @@
 nameMap.
-inline
+string
 );%0A    %7D

fix tests