/*

Siesta 5.6.1
Copyright(c) 2009-2022 Bryntum AB
https://bryntum.com/contact
https://bryntum.com/products/siesta/license

*/
Class('Siesta.Util.Serializer', {

    isa : Data.Visitor2,

    has     : {
        result                  : Joose.I.Array,
        manualEnum              : function () {
            for (var i in { toString : 1 }) return false

            return true
        }
    },


    methods : {

        assignRefAdrTo : function (object) {
            try {
                return this.SUPER(object)
            } catch (e) {
                if (!object.__REFADR__) object.__REFADR__ = this.getRefAdr()
            }

            return object.__REFADR__
        },


        write : function (str) {
            this.result.push(str)
        },


        visitOutOfDepthValue : function (value, depth) {
            this.write('...')
        },


        visitSymbol: function (value) {
            this.write(value.toString())
        },


        visitValue : function (value) {
            if (value == null)
                // `null` and `undefined`
                this.write(value + '')
            else {
                if (typeof value === 'symbol')
                    this.write(value.toString())
                else
                    this.write(typeof value == 'string' ? '"' + value.replace(/"/g, '\\"').replace(/\n/g, '\\n') + '"' : value + '')
            }
        },


        visitObjectKey : function (key, value, object) {
            this.write('"' + key + '": ')
        },


        getClassNameFor : function (object) {
            if (object.nodeType != null && object.nodeName != null && object.tagName) return 'DOMElement'

            // trying to detect and not dive into global window
            if (object.document != null && object.location != null && object.location.href != null) return 'Window'

            return this.SUPER(object)
        },


        visitSeen : function (value, depth) {
            this.write('[Circular]')
        },


        visitRegExp : function (value, depth) {
            this.write(value + '')
        },


        visitFunction : function (value, depth) {
            this.write('function ' + (value.name || '') + '() { ... }')
        },


        visitDate : function (value, depth) {
            this.write('"' + value + '"')
        },


        // safer alternative to parent's implementation of `visitObject` - some host objects has no "hasOwnProperty" method
        visitObject : function (object, depth) {
            for (var key in object) {
                if (key != '__REFADR__' && (!object.hasOwnProperty || object.hasOwnProperty(key))) {
                    var value   = object[ key ]

                    this.visitObjectKey(key, value, object, depth)
                    this.visitObjectValue(value, key, object, depth)
                }
            }

            var me  = this

            if (this.manualEnum)
                Joose.A.each([ 'hasOwnProperty', 'valueOf', 'toString', 'constructor' ], function (key) {
                    if (object.hasOwnProperty && object.hasOwnProperty(key)) {
                        var value   = object[ key ]

                        me.visitObjectKey(key, value, object, depth)
                        me.visitObjectValue(value, key, object, depth)
                    }
                })

            return object
        },


        visitJooseInstance : function (value, depth) {
            if (value.meta.hasMethod('toString')) {
                this.write(value.toString())

                return value
            }

            return this.SUPERARG(arguments)
        },


        visitDOMElement : function (object, depth) {
            var output  = '<' + object.tagName

            if (object.id) output += ' id="' + object.id + '"'
            if (object.className) output += ' class="' + object.className + '"'

            this.write(output + '>')
        },


        visitDOMStringMap : function () {
            this.write('[DOMStringMap]')
        },


        // the Object.prototype.toString.call(window) for FF
        visitWindow : function () {
            this.write('[window]')
        },


        // window.location type in FF
        visitLocation : function () {
            this.write('[window.location]')
        },


        visitMap : function (map, depth) {
            this.write('Map(' + map.size + '){')

            // we use iterator here instead of simple "for .. of" loop, because IE does not support it
            var iterator        = map.entries()

            for (var entry = iterator.next(); !entry.done; entry = iterator.next()) {
                this.visit(entry.value[ 0 ], depth + 1)
                this.write('=>')
                this.visit(entry.value[ 1 ], depth + 1)
                this.write(', ')
            }

            if (map.size > 0) this.result.pop()

            this.write('}')
        }
    },


    before : {
        visitObject : function () {
            this.write('{')
        },


        visitArray : function () {
            this.write('[')
        }
    },


    after : {
        visitObject : function () {
            var result = this.result

            if (result[ result.length - 1 ] == ', ') result.pop()

            this.write('}')
        },


        visitArray : function () {
            var result = this.result

            if (result[ result.length - 1 ] == ', ') result.pop()

            this.write(']')
        },


        visitObjectValue : function () {
            this.write(', ')
        },


        visitArrayEntry : function () {
            this.write(', ')
        }
    },


    my : {

        has : {
            HOST        : null
        },


        methods : {

            stringify : function (value, maxDepth) {
                try {
                    if (value != null && value.foobar) visitor = null
                } catch (e) {
                    if (value) return 'Value from cross-domain context'
                }

                var visitor     = new this.HOST({
                    maxDepth        : maxDepth || 4
                })

                visitor.visit(value, 0)

                return visitor.result.join('')
            }
        }
    }
})