// 
//   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
//   Free Software Foundation, Inc
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

#ifndef GNASH_SWF_DEFINEVIDEOSTREAMTAG_H
#define GNASH_SWF_DEFINEVIDEOSTREAMTAG_H

#include <mutex>
#include <boost/ptr_container/ptr_vector.hpp>
#include <memory> 
#include <vector> 

#include "DefinitionTag.h"
#include "SWF.h"
#include "SWFRect.h" // for composition
#include "MediaParser.h" // for videoFrameType and videoCodecType enums

// Forward declarations
namespace gnash {
    class movie_definition;
    class SWFStream;
    class RunResources;
}

namespace gnash {

namespace SWF {

class DefineVideoStreamTag : public DefinitionTag
{
	
    /// The undecoded video frames, using the swf-frame number as key
	//
	/// Elements of this vector are owned by this instance, and will be deleted 
	/// at instance destruction time.
	typedef boost::ptr_vector<media::EncodedVideoFrame> EmbeddedFrames;

    /// A Functor for comparing frames by frame number.
    //
    /// A comparison operator would avoid having two variants, but seems less
    /// intuitive, and could open up all sorts of unexpected behaviour due to
    /// type promotion.
    struct FrameFinder
    {
        typedef EmbeddedFrames::const_reference Frame;

        bool operator()(Frame frame, size_t i) const {
            return frame.frameNum() < i;
        }
        
        bool operator()(size_t i, Frame frame) const {
            return i < frame.frameNum();
        }
    };

public:

	~DefineVideoStreamTag();

	DisplayObject* createDisplayObject(Global_as& gl, DisplayObject* parent)
        const;

	/// Read tag SWF::DEFINEVIDEOSTREAM 
	//
	/// The DisplayObject_id is assumed to have been already read by caller.
	///
	/// This function is allowed to be called only *once* for each
	/// instance of this class.
	///
	static void loader(SWFStream& in, SWF::TagType tag, movie_definition& m,
            const RunResources& r);


	/// Read tag SWF::VIDEOFRAME
	//
	/// The DisplayObject_id (used to find this instance in the DisplayObject's
    /// dictionary) is assumed to have been already read.
	///
	/// This function is allowed to be called zero or more times, as long
	/// as readDefineVideoStream was read before.
	void readDefineVideoFrame(SWFStream& in, SWF::TagType tag,
            movie_definition& m);

	/// Return local video bounds in twips
	const SWFRect& bounds() const {
		return m_bound;
	}

	/// Get info about video embedded in this definition
	//
	/// May return NULL if there's no embedded video
	/// (ActionScript created definition - new Video)
	///
	media::VideoInfo* getVideoInfo() const { return _videoInfo.get(); }

	/// Visit a slice of encoded video frames
	//
	/// @param from     Frame number of first frame to get
	/// @param to       Frame number of last frame to get
    /// @tparam t       A visitor that should accept a const
    ///                 media::EncodedVideoFrame.
    template<typename T>
    size_t visitSlice(const T& t, std::uint32_t from, std::uint32_t to) const {

        std::lock_guard<std::mutex> lock(_video_mutex);

        // It's assumed that frame numbers are in order.
        EmbeddedFrames::const_iterator lower = std::lower_bound(
                _video_frames.begin(), _video_frames.end(), from, FrameFinder());

        EmbeddedFrames::const_iterator upper = std::upper_bound(
                lower, _video_frames.end(), to, FrameFinder());

        std::for_each(lower, upper, t);
        return (upper - lower);
    }
    
    void addVideoFrameTag(std::unique_ptr<media::EncodedVideoFrame> frame);

private:

	/// Construct a video stream definition with given ID
	//
	/// NOTE: for dynamically created definitions (ActionScript Video class
    ///       instances) you can use an id of -1. See DefinitionTag
    ///       constructor, as that's the one which will eventually get passed
    ///       the id.
    /// NOTE: What dynamically created definitions?
	DefineVideoStreamTag(SWFStream& in, std::uint16_t id);

	void read(SWFStream& in);

	/// Reserved flags read from DEFINEVIDEOSTREAM tag
	std::uint8_t m_reserved_flags;

	/// Flags read from DEFINEVIDEOSTREAM tag
	std::uint8_t m_deblocking_flags;

	/// Smoothing flag, as read from DEFINEVIDEOSTREAM tag
	bool m_smoothing_flags;

	/// Frame in which the DEFINEVIDEOSTREAM was found
	//std::uint16_t m_start_frame;

	/// Number of frames in the embedded video, as reported
	/// by the DEFINEVIDEOSTREAM tag
	///
	std::uint16_t m_num_frames;

	/// Codec ID as read from DEFINEVIDEOSTREAM tag
	//
	/// 0: extern file
	/// 2: H.263
	/// 3: screen video (Flash 7+ only)
	/// 4: VP6
	/// 5: VP6 video with alpha
	///
	media::videoCodecType m_codec_id;

	/// Bounds of the video, as read from the DEFINEVIDEOSTREAM tag.
	SWFRect m_bound;

    // Mutable for locking in const member functions.
	mutable std::mutex _video_mutex;
	
	EmbeddedFrames _video_frames;

	/// Width of the video
	std::uint32_t _width;

	/// Height of the video
	std::uint32_t _height;

	/// Info about embedded video
	//
	/// TODO: drop _width/_height/m_codec_id leaving all in this member
    /// instead ?
	std::unique_ptr<media::VideoInfo> _videoInfo;

};

} // namespace SWF
} // namespace gnash


#endif // GNASH_VIDEO_STREAM_DEF_H
