/*
 * Copyright and license
 * Copyright (c) 2009 University of Helsinki
 *
 *	Permission is hereby granted, free of charge, to any person
 *	obtaining a copy of this software and associated documentation
 *	files (the "Software"), to deal in the Software without
 *	restriction, including without limitation the rights to use,
 *	copy, modify, merge, publish, distribute, sublicense, and/or 
 *	sell copies of the Software, and to permit persons to whom the
 *	Software is furnished to do so, subject to the following
 *	conditions:
 *
 *	The above copyright notice and this permission notice shall be
 *	included in all copies or substantial portions of the Software.
 *
 *	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * 	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *	OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *	NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *	HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *	WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *	OTHER DEALINGS IN THE SOFTWARE.
 *
 *  more information:
 *	http://www.opensource.org/licenses/mit-license.php
 */

package fi.helsinki.cs.ohtu.mpeg2;

import java.io.IOException;

import fi.helsinki.cs.ohtu.mpeg2.util.BitOutputStream;
import fi.helsinki.cs.ohtu.mpeg2.util.StartCode;

/**
 * Represents a MPEG-2 program stream.
 * 
 * @author Pakkaamo
 */
public class ProgramStream implements PackDirtiedListener {
	
    /**
     * Class constructor.
     *
     * Initially, there will be no active pack. Use #setActivePack() to change that.
     *
     * @param stream The bit stream to write the program stream data to.
     */
    public ProgramStream(BitOutputStream stream) {
    	
        this.pack = null;
        this.dirtyPack = true;
        this.stream = stream;
    }

    /**
     * Gets the underlying stream.
     *
     * @return The stream.
     */
    public BitOutputStream getStream() {
        return stream;
    }

    /**
     * Sets the stream to write to.
     *
     * All future packs will be written to this stream.
     *
     * @param stream The new stream.
     */
    public void setStream(BitOutputStream stream) {
        this.stream = stream;
    }

    /**
     * Gets the active pack, as previously set by #setActivePack().
     *
     * @return The active pack.
     */
    public Pack getActivePack() {
        return pack;
    }

    /**
     * Sets the active pack.
     *
     * The active pack will the pack semantically containing all written packets. The pack headers are written when a
     * PES packet is written for the first time after changing the pack.
     *
     * If the pack set is the currently active pack, nothing will happen.
     */
    public void setActivePack(Pack pack) {
    	
        if (this.pack == pack)
            return;

        if (this.pack != null)
            this.pack.removeDirtiedListener(this);

        this.pack = pack;
        pack.addDirtiedListener(this);
        this.dirtyPack = true;
    }

    /**
     * Writes a packet to the stream.
     *
     * If the pack has been changed after writing the previous packet, the currently active pack's headers will be
     * written to the stream. To be changed in this context means both #setActivePack() usage and tampering with the
     * fields in the pack used by the {@link Pack} class accessors.
     *
     * This must not be called when there is no active pack, eg. when the program stream has just been created but the
     * active pack not yet set.
     *
     * @param packet The packet to write.
     * @throws IOException Propagated from the underlying stream write.
     */
    public void writePacket(PESPacket packet) throws IOException {
    	
        if (dirtyPack) {
            if (pack == null)
                throw new IllegalStateException("Attempted to write a packet without an active pack!");

            pack.writeTo(stream);
            dirtyPack = false;
        }

        packet.writeTo(stream);
    }

    /**
     * Closes the stream.
     *
     * No more packets can be written to the stream after closing it. Also closes the underlying stream.
     *
     * @throws IOException Propagated from writing the end codes to the underlying stream.
     */
    public void close() throws IOException {
    	
        StartCode.PS_END.writeTo(stream);
        stream.close();
    }

    /**
     * Used internally to make the stream re-emit the pack headers after changing some of the pack's fields.
     *
     * {@inheritDoc}
     * @see PackDirtiedListener#packDirtied(Pack)
     */
    public void packDirtied(Pack pack) {
    	
        assert (pack == this.pack);
        this.dirtyPack = true;
    }

    Pack pack;
    private boolean dirtyPack;
    private BitOutputStream stream;
}