/** 
 * @file llimagej2c.h
 * @brief Image implmenation for jpeg2000.
 *
 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#ifndef LL_LLIMAGEJ2C_H
#define LL_LLIMAGEJ2C_H

#include "llimage.h"
#include "llassettype.h"
#include "llmetricperformancetester.h"
#include <boost/scoped_ptr.hpp>

// JPEG2000 : compression rate used in j2c conversion.
const F32 DEFAULT_COMPRESSION_RATE = 1.f/8.f;

class LLImageJ2CImpl;
class LLImageCompressionTester ;

class LLImageJ2C : public LLImageFormatted
{
protected:
	virtual ~LLImageJ2C();

public:
	LLImageJ2C();

	// Base class overrides
	/*virtual*/ std::string getExtension() { return std::string("j2c"); }
	/*virtual*/ bool updateData();
	/*virtual*/ bool decode(LLImageRaw *raw_imagep, F32 decode_time);
	/*virtual*/ bool decodeChannels(LLImageRaw *raw_imagep, F32 decode_time, S32 first_channel, S32 max_channel_count);
	/*virtual*/ bool encode(const LLImageRaw *raw_imagep, F32 encode_time);
	/*virtual*/ S32 calcHeaderSize();
	/*virtual*/ S32 calcDataSize(S32 discard_level = 0);
	/*virtual*/ S32 calcDiscardLevelBytes(S32 bytes);
	/*virtual*/ S8  getRawDiscardLevel();
	// Override these so that we don't try to set a global variable from a DLL
	/*virtual*/ void resetLastError();
	/*virtual*/ void setLastError(const std::string& message, const std::string& filename = std::string());
	
	bool initDecode(LLImageRaw &raw_image, int discard_level, int* region);
	bool initEncode(LLImageRaw &raw_image, int blocks_size, int precincts_size, int levels);
	
	// Encode with comment text 
	bool encode(const LLImageRaw *raw_imagep, const char* comment_text, F32 encode_time=0.0);

	bool validate(U8 *data, U32 file_size);
	bool loadAndValidate(const std::string &filename);

	// Encode accessors
	void setReversible(const bool reversible); // Use non-lossy?
	void setMaxBytes(S32 max_bytes);
	S32 getMaxBytes() const { return mMaxBytes; }

	static S32 calcHeaderSizeJ2C();
	static S32 calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate = DEFAULT_COMPRESSION_RATE);

	static std::string getEngineInfo();

protected:
	friend class LLImageJ2CImpl;
	friend class LLImageJ2COJ;
	friend class LLImageJ2CKDU;
	friend class LLImageCompressionTester;
	void decodeFailed();
	void updateRawDiscardLevel();

	S32 mMaxBytes; // Maximum number of bytes of data to use...
	
	S32 mDataSizes[MAX_DISCARD_LEVEL+1];		// Size of data required to reach a given level
	U32 mAreaUsedForDataSizeCalcs;				// Height * width used to calculate mDataSizes

	S8  mRawDiscardLevel;
	F32 mRate;
	bool mReversible;
	boost::scoped_ptr<LLImageJ2CImpl> mImpl;
	std::string mLastError;

    // Image compression/decompression tester
	static LLImageCompressionTester* sTesterp;
};

// Derive from this class to implement JPEG2000 decoding
class LLImageJ2CImpl
{
public:
	virtual ~LLImageJ2CImpl();
protected:
	// Find out the image size and number of channels.
	// Return value:
	// true: image size and number of channels was determined
	// false: error on decode
	virtual bool getMetadata(LLImageJ2C &base) = 0;
	// Decode the raw image optionally aborting (to continue later) after
	// decode_time seconds.  Decode at most max_channel_count and start
	// decoding channel first_channel.
	// Return value:
	// true: decoding complete (even if it failed)
	// false: time expired while decoding
	virtual bool decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count) = 0;
	virtual bool encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time=0.0,
							bool reversible=false) = 0;
	virtual bool initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level = -1, int* region = NULL) = 0;
	virtual bool initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int blocks_size = -1, int precincts_size = -1, int levels = 0) = 0;

	virtual std::string getEngineInfo() const = 0;

	friend class LLImageJ2C;
};

#define LINDEN_J2C_COMMENT_PREFIX "LL_" // Used by LLAppearanceUtility

//
// This class is used for performance data gathering only.
// Tracks the image compression / decompression data,
// records and outputs them to the log file.
//
class LLImageCompressionTester : public LLMetricPerformanceTesterBasic
{
    public:
        LLImageCompressionTester();
        ~LLImageCompressionTester();
        
        void updateDecompressionStats(const F32 deltaTime) ;
        void updateDecompressionStats(const S32 bytesIn, const S32 bytesOut) ;
        void updateCompressionStats(const F32 deltaTime) ;
        void updateCompressionStats(const S32 bytesIn, const S32 bytesOut) ;
    
    protected:
        /*virtual*/ void outputTestRecord(LLSD* sd);
        
    private:
        //
        // Data size
        //
        U32 mTotalBytesInDecompression;     // Total bytes fed to decompressor
        U32 mTotalBytesOutDecompression;    // Total bytes produced by decompressor
        U32 mTotalBytesInCompression;       // Total bytes fed to compressor
        U32 mTotalBytesOutCompression;      // Total bytes produced by compressor
        U32 mRunBytesInDecompression;		// Bytes fed to decompressor in this run
        U32 mRunBytesOutDecompression;		// Bytes produced by the decompressor in this run
		U32 mRunBytesInCompression;			// Bytes fed to compressor in this run
        //
        // Time
        //
        F32 mTotalTimeDecompression;        // Total time spent in computing decompression
        F32 mTotalTimeCompression;          // Total time spent in computing compression
        F32 mRunTimeDecompression;          // Time in this run (we output every 5 sec in decompress)
    };

#endif
