/**
 * Filename    : Printer.js
 * Author      : Robert Cerny
 * Created     : 2006-10-30
 * Last Change : 2006-11-20
 *
 * Description:
 *   Prints a JavaScript object. Allows customizing by overriding the
 *   methods startObject, endObject, startName, endName and so on. Works
 *   with arrays as well.
 *
 * History:
 *   2006-10-30 Created.
 *
 * License:
 */

CERNY.require("CERNY.json.Printer",
              "CERNY.json",
              "Object.toJSONString");

CERNY.json.Printer = function(obj) {
    var self = CERNY.object(obj);
    self.STATES =  {START: "START",
                   OBJECT: {OBJECT: "OBJECT",
                            NAME: "NAME",
                            VALUE: {VALUE: "VALUE",
                                    STRING: {STRING: "STRING",
                                             ESCAPED: "ESCAPED"}}},
                   ARRAY: "ARRAY"};

    self.separator = "";

    self.print = CERNY.json.Printer_print;

    self.startName = CERNY.json.Printer_concat;
    self.endName = CERNY.json.Printer_concat;

    self.startArray = CERNY.json.Printer_concat;
    self.endArray = CERNY.json.Printer_concat;

    self.startObject = CERNY.json.Printer_concat;
    self.endObject = CERNY.json.Printer_concat;

    self.startValue = CERNY.json.Printer_concat;
    self.endValue = CERNY.json.Printer_concat;

    self.printNull = CERNY.json.Printer_concat;

    return self;
};

CERNY.json.Printer_concat = function(str) {
    return str;
};

CERNY.json.Printer_print = function(object) {

    // Reset intendation
    this.indent = 0;

    // Reset the state and create a shortcut to the current state
    this.state = [this.STATES.START];
    this.state.current = function() {
        return this[this.length - 1];
    };

    // Shorten some names and bind this to t
    var t = this;
    var state = t.state;
    var STATES = t.STATES;

    var rest = object.toJSONString(),
        token,
        result = "";

    while (rest.length > 0) {
        token = rest.substr(0,1);

        if (state.current() == STATES.OBJECT.VALUE.STRING.ESCAPED) {
            result += token;
            state.pop();
        } else {
            switch (token) {

            case '{':
                result += t.startObject(token);
                state.push(STATES.OBJECT.OBJECT);
                break;

            case '}':
                result += t.endObject(token);

                state.pop();
                state.pop();
                break;

            case '[':
                result += t.startArray(token);
                state.push(STATES.ARRAY);
                break;

            case ']':
                result += t.endArray(token);
                state.pop();
                break;

            case ':':
                if (state.current() === STATES.OBJECT.OBJECT) {
                    result += t.startValue(token);
                    state.push(STATES.OBJECT.VALUE.VALUE);
                } else {
                    result += token;
                }

                break;

            case ',':
                switch (state.current()) {

                case STATES.OBJECT.VALUE.VALUE:
                    result += t.endValue(token);
                    state.pop();
                    break;

                default:
                    result += token;
                }
                break;

            case "\\":
                switch (state.current()) {

                case STATES.OBJECT.VALUE.STRING.STRING:
                    result += token;
                    state.push(STATES.OBJECT.VALUE.STRING.ESCAPED);
                    break;


                default:
                    result += token;
                }
                break;

            case '"':
                switch (state.current()) {

                case STATES.OBJECT.OBJECT:
                    result += t.startName(token);
                    state.push(STATES.OBJECT.NAME);
                    break;

                case STATES.OBJECT.NAME:
                    result += t.endName(token);
                    state.pop();
                    break;

                case STATES.OBJECT.VALUE.VALUE:
                    result += token;
                    state.push(STATES.OBJECT.VALUE.STRING.STRING);
                    break;

                case STATES.OBJECT.VALUE.STRING.STRING:
                    result += token;
                    state.pop();
                    break;

                default:
                    result += token;

                }
                break;

            default:
                result += token;
            }
        }
        rest = rest.substr(1, rest.length);
    }

    return result;
};

