define(function(require, exports, module) {
    var data = require('../core/data');
    var LINE_ENDING_SPLITER = /\r\n|\r|\n/;
    var EMPTY_LINE = '';
    var NOTE_MARK_START = '<!--Note-->';
    var NOTE_MARK_CLOSE = '<!--/Note-->';

    function encode(json) {

        return _build(json, 1).join('\n');
    }

    function _build(node, level) {
        var lines = [];

        level = level || 1;

        var sharps = _generateHeaderSharp(level);
        lines.push(sharps + ' ' + node.data.text);
        lines.push(EMPTY_LINE);

        var note = node.data.note;
        if (note) {
            var hasSharp = /^#/.test(note);
            if (hasSharp) {
                lines.push(NOTE_MARK_START);
                note = note.replace(/^#+/gm, function($0) {
                    return sharps + $0;
                });
            }
            lines.push(note);
            if (hasSharp) {
                lines.push(NOTE_MARK_CLOSE);
            }
            lines.push(EMPTY_LINE);
        }

        if (node.children) node.children.forEach(function(child) {
            lines = lines.concat(_build(child, level + 1));
        });

        return lines;
    }

    function _generateHeaderSharp(level) {
        var sharps = '';
        while (level--) sharps += '#';
        return sharps;
    }

    function decode(markdown) {

        var json,
            parentMap = {},
            lines, line, lineInfo, level, node, parent, noteProgress, codeBlock;

        // 一级标题转换 `{title}\n===` => `# {title}`
        markdown = markdown.replace(/^(.+)\n={3,}/, function($0, $1) {
            return '# ' + $1;
        });

        lines = markdown.split(LINE_ENDING_SPLITER);

        // 按行分析
        for (var i = 0; i < lines.length; i++) {
            line = lines[i];

            lineInfo = _resolveLine(line);

            // 备注标记处理
            if (lineInfo.noteClose) {
                noteProgress = false;
                continue;
            } else if (lineInfo.noteStart) {
                noteProgress = true;
                continue;
            }

            // 代码块处理
            codeBlock = lineInfo.codeBlock ? !codeBlock : codeBlock;

            // 备注条件：备注标签中，非标题定义，或标题越位
            if (noteProgress || codeBlock || !lineInfo.level || lineInfo.level > level + 1) {
                if (node) _pushNote(node, line);
                continue;
            }

            // 标题处理
            level = lineInfo.level;
            node = _initNode(lineInfo.content, parentMap[level - 1]);
            parentMap[level] = node;
        }

        _cleanUp(parentMap[1]);
        return parentMap[1];
    }

    function _initNode(text, parent) {
        var node = {
            data: {
                text: text,
                note: ''
            }
        };
        if (parent) {
            if (parent.children) parent.children.push(node);
            else parent.children = [node];
        }
        return node;
    }

    function _pushNote(node, line) {
        node.data.note += line + '\n';
    }

    function _isEmpty(line) {
        return !/\S/.test(line);
    }

    function _resolveLine(line) {
        var match = /^(#+)?\s*(.*)$/.exec(line);
        return {
            level: match[1] && match[1].length || null,
            content: match[2],
            noteStart: line == NOTE_MARK_START,
            noteClose: line == NOTE_MARK_CLOSE,
            codeBlock: /^\s*```/.test(line)
        };
    }

    function _cleanUp(node) {
        if (!/\S/.test(node.data.note)) {
            node.data.note = null;
            delete node.data.note;
        } else {
            var notes = node.data.note.split('\n');
            while (notes.length && !/\S/.test(notes[0])) notes.shift();
            while (notes.length && !/\S/.test(notes[notes.length - 1])) notes.pop();
            node.data.note = notes.join('\n');
        }
        if (node.children) node.children.forEach(_cleanUp);
    }

    data.registerProtocol('markdown', module.exports = {
        fileDescription: 'Markdown/GFM 格式',
        fileExtension: '.md',
        mineType: 'text/markdown',
        dataType: 'text',

        encode: function(json) {
            return encode(json.root);
        },

        decode: function(markdown) {
            return decode(markdown);
        }
    });
});