/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.javascript.xmlimpl;

import java.util.Objects;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.IdFunctionObject;
import org.mozilla.javascript.ScriptRuntime;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Undefined;

class XMLCtor extends IdFunctionObject {
    static final long serialVersionUID = -8708195078359817341L;

    private static final Object XMLCTOR_TAG = "XMLCtor";

    private XmlProcessor options;

    //    private XMLLibImpl lib;

    XMLCtor(XML xml, Object tag, int id, int arity) {
        super(xml, tag, id, arity);
        //        this.lib = xml.lib;
        this.options = xml.getProcessor();
        activatePrototypeMap(MAX_FUNCTION_ID);
    }

    private void writeSetting(Scriptable target) {
        for (int i = 1; i <= MAX_INSTANCE_ID; ++i) {
            int id = super.getMaxInstanceId() + i;
            String name = getInstanceIdName(id);
            Object value = getInstanceIdValue(id);
            ScriptableObject.putProperty(target, name, value);
        }
    }

    private void readSettings(Scriptable source) {
        for (int i = 1; i <= MAX_INSTANCE_ID; ++i) {
            int id = super.getMaxInstanceId() + i;
            String name = getInstanceIdName(id);
            Object value = ScriptableObject.getProperty(source, name);
            if (value == Scriptable.NOT_FOUND) {
                continue;
            }
            switch (i) {
                case Id_ignoreComments:
                case Id_ignoreProcessingInstructions:
                case Id_ignoreWhitespace:
                case Id_prettyPrinting:
                    if (!(value instanceof Boolean)) {
                        continue;
                    }
                    break;
                case Id_prettyIndent:
                    if (!(value instanceof Number)) {
                        continue;
                    }
                    break;
                default:
                    throw new IllegalStateException();
            }
            setInstanceIdValue(id, value);
        }
    }

    // #string_id_map#

    private static final int Id_ignoreComments = 1,
            Id_ignoreProcessingInstructions = 2,
            Id_ignoreWhitespace = 3,
            Id_prettyIndent = 4,
            Id_prettyPrinting = 5,
            MAX_INSTANCE_ID = 5;

    @Override
    protected int getMaxInstanceId() {
        return super.getMaxInstanceId() + MAX_INSTANCE_ID;
    }

    @Override
    protected int findInstanceIdInfo(String s) {
        int id;
        // #generated# Last update: 2007-08-20 09:01:10 EDT
        L0:
        {
            id = 0;
            String X = null;
            int c;
            L:
            switch (s.length()) {
                case 12:
                    X = "prettyIndent";
                    id = Id_prettyIndent;
                    break L;
                case 14:
                    c = s.charAt(0);
                    if (c == 'i') {
                        X = "ignoreComments";
                        id = Id_ignoreComments;
                    } else if (c == 'p') {
                        X = "prettyPrinting";
                        id = Id_prettyPrinting;
                    }
                    break L;
                case 16:
                    X = "ignoreWhitespace";
                    id = Id_ignoreWhitespace;
                    break L;
                case 28:
                    X = "ignoreProcessingInstructions";
                    id = Id_ignoreProcessingInstructions;
                    break L;
            }
            if (!Objects.equals(X, s)) id = 0;
            break L0;
        }
        // #/generated#

        if (id == 0) return super.findInstanceIdInfo(s);

        int attr;
        switch (id) {
            case Id_ignoreComments:
            case Id_ignoreProcessingInstructions:
            case Id_ignoreWhitespace:
            case Id_prettyIndent:
            case Id_prettyPrinting:
                attr = PERMANENT | DONTENUM;
                break;
            default:
                throw new IllegalStateException();
        }
        return instanceIdInfo(attr, super.getMaxInstanceId() + id);
    }

    // #/string_id_map#

    @Override
    protected String getInstanceIdName(int id) {
        switch (id - super.getMaxInstanceId()) {
            case Id_ignoreComments:
                return "ignoreComments";
            case Id_ignoreProcessingInstructions:
                return "ignoreProcessingInstructions";
            case Id_ignoreWhitespace:
                return "ignoreWhitespace";
            case Id_prettyIndent:
                return "prettyIndent";
            case Id_prettyPrinting:
                return "prettyPrinting";
        }
        return super.getInstanceIdName(id);
    }

    @Override
    protected Object getInstanceIdValue(int id) {
        switch (id - super.getMaxInstanceId()) {
            case Id_ignoreComments:
                return ScriptRuntime.wrapBoolean(options.isIgnoreComments());
            case Id_ignoreProcessingInstructions:
                return ScriptRuntime.wrapBoolean(options.isIgnoreProcessingInstructions());
            case Id_ignoreWhitespace:
                return ScriptRuntime.wrapBoolean(options.isIgnoreWhitespace());
            case Id_prettyIndent:
                return ScriptRuntime.wrapInt(options.getPrettyIndent());
            case Id_prettyPrinting:
                return ScriptRuntime.wrapBoolean(options.isPrettyPrinting());
        }
        return super.getInstanceIdValue(id);
    }

    @Override
    protected void setInstanceIdValue(int id, Object value) {
        switch (id - super.getMaxInstanceId()) {
            case Id_ignoreComments:
                options.setIgnoreComments(ScriptRuntime.toBoolean(value));
                return;
            case Id_ignoreProcessingInstructions:
                options.setIgnoreProcessingInstructions(ScriptRuntime.toBoolean(value));
                return;
            case Id_ignoreWhitespace:
                options.setIgnoreWhitespace(ScriptRuntime.toBoolean(value));
                return;
            case Id_prettyIndent:
                options.setPrettyIndent(ScriptRuntime.toInt32(value));
                return;
            case Id_prettyPrinting:
                options.setPrettyPrinting(ScriptRuntime.toBoolean(value));
                return;
        }
        super.setInstanceIdValue(id, value);
    }

    // #string_id_map#
    private static final int Id_defaultSettings = 1,
            Id_settings = 2,
            Id_setSettings = 3,
            MAX_FUNCTION_ID = 3;

    @Override
    protected int findPrototypeId(String s) {
        int id;
        // #generated# Last update: 2007-08-20 09:01:10 EDT
        L0:
        {
            id = 0;
            String X = null;
            int s_length = s.length();
            if (s_length == 8) {
                X = "settings";
                id = Id_settings;
            } else if (s_length == 11) {
                X = "setSettings";
                id = Id_setSettings;
            } else if (s_length == 15) {
                X = "defaultSettings";
                id = Id_defaultSettings;
            }
            if (!Objects.equals(X, s)) id = 0;
            break L0;
        }
        // #/generated#
        return id;
    }

    // #/string_id_map#

    @Override
    protected void initPrototypeId(int id) {
        String s;
        int arity;
        switch (id) {
            case Id_defaultSettings:
                arity = 0;
                s = "defaultSettings";
                break;
            case Id_settings:
                arity = 0;
                s = "settings";
                break;
            case Id_setSettings:
                arity = 1;
                s = "setSettings";
                break;
            default:
                throw new IllegalArgumentException(String.valueOf(id));
        }
        initPrototypeMethod(XMLCTOR_TAG, id, s, arity);
    }

    @Override
    public Object execIdCall(
            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
        if (!f.hasTag(XMLCTOR_TAG)) {
            return super.execIdCall(f, cx, scope, thisObj, args);
        }
        int id = f.methodId();
        switch (id) {
            case Id_defaultSettings:
                {
                    options.setDefault();
                    Scriptable obj = cx.newObject(scope);
                    writeSetting(obj);
                    return obj;
                }
            case Id_settings:
                {
                    Scriptable obj = cx.newObject(scope);
                    writeSetting(obj);
                    return obj;
                }
            case Id_setSettings:
                {
                    if (args.length == 0 || args[0] == null || args[0] == Undefined.instance) {
                        options.setDefault();
                    } else if (args[0] instanceof Scriptable) {
                        readSettings((Scriptable) args[0]);
                    }
                    return Undefined.instance;
                }
        }
        throw new IllegalArgumentException(String.valueOf(id));
    }

    /** hasInstance for XML objects works differently than other objects; see ECMA357 13.4.3.10. */
    @Override
    public boolean hasInstance(Scriptable instance) {
        return (instance instanceof XML || instance instanceof XMLList);
    }
}
