package org.codehaus.jackson.impl;

import org.codehaus.jackson.*;

/**
 * Extension of {@link JsonStreamContext}, which implements
 * core methods needed, and also exposes
 * more complete API to generator implementation classes.
 */
public class JsonWriteContext
    extends JsonStreamContext
{
    // // // Return values for writeValue()

    public final static int STATUS_OK_AS_IS = 0;
    public final static int STATUS_OK_AFTER_COMMA = 1;
    public final static int STATUS_OK_AFTER_COLON = 2;
    public final static int STATUS_OK_AFTER_SPACE = 3; // in root context
    public final static int STATUS_EXPECT_VALUE = 4;
    public final static int STATUS_EXPECT_NAME = 5;

    protected final JsonWriteContext _parent;

    /**
     * Name of the field of which value is to be parsed; only
     * used for OBJECT contexts
     */
    protected String _currentName;
    
    /*
    /**********************************************************
    /* Simple instance reuse slots; speed up things
    /* a bit (10-15%) for docs with lots of small
    /* arrays/objects
    /**********************************************************
     */

    protected JsonWriteContext _child = null;

    /*
    /**********************************************************
    /* Life-cycle
    /**********************************************************
     */

    protected JsonWriteContext(int type, JsonWriteContext parent)
    {
        super();
        _type = type;
        _parent = parent;
        _index = -1;
    }
    
    // // // Factory methods

    public static JsonWriteContext createRootContext()
    {
        return new JsonWriteContext(TYPE_ROOT, null);
    }

    private final JsonWriteContext reset(int type) {
        _type = type;
        _index = -1;
        _currentName = null;
        return this;
    }
    
    public final JsonWriteContext createChildArrayContext()
    {
        JsonWriteContext ctxt = _child;
        if (ctxt == null) {
            _child = ctxt = new JsonWriteContext(TYPE_ARRAY, this);
            return ctxt;
        }
        return ctxt.reset(TYPE_ARRAY);
    }

    public final JsonWriteContext createChildObjectContext()
    {
        JsonWriteContext ctxt = _child;
        if (ctxt == null) {
            _child = ctxt = new JsonWriteContext(TYPE_OBJECT, this);
            return ctxt;
        }
        return ctxt.reset(TYPE_OBJECT);
    }

    // // // Shared API

    @Override
    public final JsonWriteContext getParent() { return _parent; }

    @Override
    public final String getCurrentName() { return _currentName; }
    
    // // // API sub-classes are to implement

    /**
     * Method that writer is to call before it writes a field name.
     *
     * @return Index of the field entry (0-based)
     */
    public final int writeFieldName(String name)
    {
        if (_type == TYPE_OBJECT) {
            if (_currentName != null) { // just wrote a name...
                return STATUS_EXPECT_VALUE;
            }
            _currentName = name;
            return (_index < 0) ? STATUS_OK_AS_IS : STATUS_OK_AFTER_COMMA;
        }
        return STATUS_EXPECT_VALUE;
    }
    
    public final int writeValue()
    {
        // Most likely, object:
        if (_type == TYPE_OBJECT) {
            if (_currentName == null) {
                return STATUS_EXPECT_NAME;
            }
            _currentName = null;
            ++_index;
            return STATUS_OK_AFTER_COLON;
        }

        // Ok, array?
        if (_type == TYPE_ARRAY) {
            int ix = _index;
            ++_index;
            return (ix < 0) ? STATUS_OK_AS_IS : STATUS_OK_AFTER_COMMA;
        }
        
        // Nope, root context
        // No commas within root context, but need space
        ++_index;
        return (_index == 0) ? STATUS_OK_AS_IS : STATUS_OK_AFTER_SPACE;
    }

    // // // Internally used abstract methods

    protected final void appendDesc(StringBuilder sb)
    {
        if (_type == TYPE_OBJECT) {
            sb.append('{');
            if (_currentName != null) {
                sb.append('"');
                // !!! TODO: Name chars should be escaped?
                sb.append(_currentName);
                sb.append('"');
            } else {
                sb.append('?');
            }
            sb.append('}');
        } else if (_type == TYPE_ARRAY) {
            sb.append('[');
            sb.append(getCurrentIndex());
            sb.append(']');
        } else {
            // nah, ROOT:
            sb.append("/");
        }
    }

    // // // Overridden standard methods

    /**
     * Overridden to provide developer writeable "JsonPath" representation
     * of the context.
     */
    @Override
    public final String toString()
    {
        StringBuilder sb = new StringBuilder(64);
        appendDesc(sb);
        return sb.toString();
    }
}
