# Source: core/serialization/serializer/montage-serializer.js

    /**
     * @module montage/core/serialization/serializer/montage-serializer
     */

    var Montage = require("../../core").Montage,
        MontageWalker = require("./montage-malker").MontageWalker,
        MontageBuilder = require("./montage-builder").MontageBuilder,
        MontageLabeler = require("./montage-labeler").MontageLabeler,
        MontageVisitor = require("./montage-visitor").MontageVisitor,
        logger = require("../../logger").logger("montage-serializer");

    var MontageSerializer = Montage.specialize({
        _require: {value: null},
        _visitor: {value: null},
        _labeler: {value: null},
        _builder: {value: null},
        _serializationIndentation: {value: 2},
        _malker: { value: null },
        legacyMode: { value: false },

        constructor: {
            value: function (legacyMode) {
                this.legacyMode = !!legacyMode;
            }
        },

        initWithRequire: {
            value: function (_require) {
                this._require = _require;

                this._builder = new MontageBuilder();
                this._labeler = new MontageLabeler();
                this._visitor = new MontageVisitor()
                    .initWithBuilderAndLabelerAndRequireAndUnits(
                        this._builder,
                        this._labeler,
                        this._require,
                        this.constructor._units
                    );

                this._malker = new MontageWalker(this._visitor, this.legacyMode);

                return this;
            }
        },

        getExternalObjects: {
            value: function () {
                return this._visitor.getExternalObjects();
            }
        },

        getExternalElements: {
            value: function () {
                return this._visitor.getExternalElements();
            }
        },

        setSerializationIndentation: {
            value: function(indentation) {
                this._serializationIndentation = indentation;
            }
        },

        serialize: {
            value: function(objects) {
                var serializationString;

                this._labeler.initWithObjects(objects);

                for (var label in objects) {
                    if (Object.hasOwnProperty.call(objects, label)) {
                        this._malker.visit(objects[label]);
                    }
                }

                serializationString = this._formatSerialization(
                    this._builder.getSerialization(
                        this._serializationIndentation
                    )
                );

                return serializationString;
            }
        },

        serializeObject: {
            value: function(object) {
                return this.serialize({root: object});
            }
        },

        _formatSerializationBindingsRegExp: {
            value: /\{\s*("(?:<->?)")\s*:\s*("[^"]+"\s*(?:,\s*"converter"\s*:\s*\{\s*"@"\s*:\s*"[^"]+"\s*\}\s*|,\s*"deferred"\s*:\s*(true|false)\s*)*)\}/gi
        },

        _formatSerializationBindingsReplacer: {
            value: function(_, g1, g2) {
                return '{' + g1 + ': ' +
                    g2.replace(/\n\s*/g, "").replace(/,\s*/g, ", ") +
                    '}';
            }
        },
        _formatSerializationBindings: {
            value: function(serialization) {
                return serialization.replace(
                    this._formatSerializationBindingsRegExp,
                    this._formatSerializationBindingsReplacer);
            }
        },

        _formatSerializationReferencesRegExp: {
            value: /\{\s*("[#@]")\s*:\s*("[^"]+")\s*\}/gi
        },
        _formatSerializationReferences: {
            value: function(serialization) {
                return serialization.replace(
                    this._formatSerializationReferencesRegExp, "{$1: $2}");
            }
        },

        _formatSerialization: {
            value: function(serialization) {
                return this._formatSerializationBindings(
                    this._formatSerializationReferences(serialization));
            }
        }

    }, {
        _units: {
            value: Object.create(null)
        },

        defineSerializationUnit: {
            value: function (name, funktion) {
                this._units[name] = funktion;
            }
        },

        _toCamelCaseRegExp: {value: /(?:^|-)([^-])/g},
        _replaceToCamelCase: {value: function (_, g1) {
            return g1.toUpperCase();}
        },

        _findObjectNameRegExp: {value: /([^\/]+?)(\.reel)?$/},

        getDefaultObjectNameForModuleId: {
            value: function (moduleId) {
                this._findObjectNameRegExp.test(moduleId);

                return RegExp.$1.replace(this._toCamelCaseRegExp, this._replaceToCamelCase);
            }
        }

    });

    exports.MontageSerializer = MontageSerializer;
    exports.serialize = function (object, _require, legacyMode) {
        return new MontageSerializer(legacyMode).initWithRequire(_require)
            .serializeObject(object);
    };