package com.fasterxml.jackson.jr.ob;

import java.io.IOException;
import java.util.*;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.io.SegmentedStringWriter;
import com.fasterxml.jackson.core.util.ByteArrayBuilder;

import com.fasterxml.jackson.jr.ob.JSON.Feature;
import com.fasterxml.jackson.jr.ob.comp.SequenceComposer;

/**
 * Root-level composer object that acts as streaming "builder"
 * object, using an underlying {@link com.fasterxml.jackson.core.JsonGenerator} object.
 * This is similar to {@link com.fasterxml.jackson.jr.ob.comp.ArrayComposer}, but does not
 * have parent composer (so no <code>end()</code> method),
 * but does implement {@link java.io.Closeable}
 * 
 * @param <T> Type of result being composed.
 */
public class JSONComposer<T>
    extends SequenceComposer<JSONComposer<T>>
{
    protected final int _features;

    protected final boolean _closeGenerator;
    
    protected T _result;

    protected SegmentedStringWriter _stringWriter;
    
    protected ByteArrayBuilder _byteWriter;
    
    public JSONComposer(int features, JsonGenerator gen, boolean closeGenerator)
    {
        super(gen);
        _features = features;
        _stringWriter = null;
        _byteWriter = null;
        _closeGenerator = closeGenerator;
    }

    protected JSONComposer(int features, JsonGenerator gen, SegmentedStringWriter w)
    {
        super(gen);
        _features = features;
        _stringWriter = w;
        _byteWriter = null;
        _closeGenerator = true;
    }

    protected JSONComposer(int features, JsonGenerator gen, ByteArrayBuilder w)
    {
        super(gen);
        _features = features;
        _stringWriter = null;
        _byteWriter = w;
        _closeGenerator = true;
    }

    protected JSONComposer(int features, T result)
    {
        super(null);
        _features = features;
        _result = result;
        _stringWriter = null;
        _byteWriter = null;
        _closeGenerator = false;
    }
    
    /*
    /**********************************************************************
    /* API, factory methods
    /**********************************************************************
     */

    public static <RESULT> JSONComposer<RESULT> streamComposer(int features,
            JsonGenerator gen, boolean closeGenerator)
    {
        return new JSONComposer<RESULT>(features, gen, closeGenerator);
    }
    
    public static JSONComposer<String> stringComposer(int features,
            JsonGenerator gen, SegmentedStringWriter w) {
        return new JSONComposer<String>(features, gen, w);
    }

    public static JSONComposer<byte[]> bytesComposer(int features,
            JsonGenerator gen, ByteArrayBuilder w) {
        return new JSONComposer<byte[]>(features, gen, w);
    }

    public static <T extends Collection<Object>> JSONComposer<T> collectionComposer(int features,
            T coll) {
        return new JSONComposer<T>(features, coll);
    }

    public static JSONComposer<Map<String,Object>> mapComposer(int features,
            Map<String,Object> map) {
        return new JSONComposer<Map<String,Object>>(features, map);
    }
    
    /*
    /**********************************************************************
    /* API, life-cycle
    /**********************************************************************
     */

    /**
     * Method to call to complete composition, flush any pending content,
     * and return instance of specified result type.
     */
    @SuppressWarnings("unchecked")
    public T finish() throws IOException
    {
        if (_open) {
            _closeChild();
            _open = false;
            if (_closeGenerator) {
                _generator.close();
            } else if (Feature.FLUSH_AFTER_WRITE_VALUE.isEnabled(_features)) {
                _generator.flush();
            }
        }
        if (_result == null) {
            Object x;
            if (_stringWriter != null) {
                x = _stringWriter.getAndClear();
                _stringWriter = null;
            } else if (_byteWriter != null) {
                x = _byteWriter.toByteArray();
                _byteWriter = null;
            } else {
                x = _generator.getOutputTarget();
            }
            _result = (T) x;
        }
        return _result;
    }
    
    /*
    /**********************************************************************
    /* Abstract method impls
    /**********************************************************************
     */

    @Override
    protected JSONComposer<T> _start() throws IOException {
        // Should never be called
        throw _illegalCall();
    }

    @Override
    protected Object _finish() throws IOException {
        // Should never be called
        throw _illegalCall();
    }
}
