// Find the block boundary for each discard level in the input image.
// We parse the input blocks and copy them in a temporary output stream.
// For the moment, we do nothing more that parsing the raw list of blocks and outputing result.
void LLImageJ2CKDU::findDiscardLevelsBoundaries(LLImageJ2C &base)
{
	// We need the number of levels in that image before starting.
	getMetadata(base);
	
	for (int discard_level = 0; discard_level < mLevels; discard_level++)
	{
		//std::cout << "Parsing discard level = " << discard_level << std::endl;
		// Create the input codestream object.
		setupCodeStream(base, TRUE, MODE_FAST);
		mCodeStreamp->apply_input_restrictions(0, 4, discard_level, 0, NULL);
		mCodeStreamp->set_max_bytes(KDU_LONG_MAX,true);
		siz_params *siz_in = mCodeStreamp->access_siz();
	
		// Create the output codestream object.
		siz_params siz;
		siz.copy_from(siz_in,-1,-1,-1,0,discard_level,false,false,false);
		siz.set(Scomponents,0,0,mCodeStreamp->get_num_components());
	
		U32 max_output_size = base.getWidth()*base.getHeight()*base.getComponents();
		max_output_size = (max_output_size < 1000 ? 1000 : max_output_size);
		U8 *output_buffer = new U8[max_output_size];
		U32 output_size = 0; // Address updated by LLKDUMemTarget to give the final compressed buffer size
		LLKDUMemTarget output(output_buffer, output_size, max_output_size);
		kdu_codestream codestream_out; 
		codestream_out.create(&siz,&output);
		//codestream_out.share_buffering(*mCodeStreamp);
		siz_params *siz_out = codestream_out.access_siz();
		siz_out->copy_from(siz_in,-1,-1,-1,0,discard_level,false,false,false);
		codestream_out.access_siz()->finalize_all(-1);
	
		// Set up rate control variables
		kdu_long max_bytes = KDU_LONG_MAX;
		kdu_params *cod = siz_out->access_cluster(COD_params);
		int total_layers;  cod->get(Clayers,0,0,total_layers);
		kdu_long *layer_bytes = new kdu_long[total_layers];
		int nel, non_empty_layers = 0;
	
		// Now ready to perform the transfer of compressed data between streams
		int flush_counter = INT_MAX;
		kdu_dims tile_indices_in;  
		mCodeStreamp->get_valid_tiles(tile_indices_in);
		kdu_dims tile_indices_out; 
		codestream_out.get_valid_tiles(tile_indices_out);
		assert((tile_indices_in.size.x == tile_indices_out.size.x) &&
			   (tile_indices_in.size.y == tile_indices_out.size.y));
		int num_blocks=0;
	
		kdu_coords idx;
		//std::cout << "Parsing tiles : x = " << tile_indices_out.size.x << " to y = " << tile_indices_out.size.y << std::endl;
		for (idx.y=0; idx.y < tile_indices_out.size.y; idx.y++)
		{
			for (idx.x=0; idx.x < tile_indices_out.size.x; idx.x++)
			{
				kdu_tile tile_in = mCodeStreamp->open_tile(idx+tile_indices_in.pos);
				int tnum_in = tile_in.get_tnum();
				int tnum_out = idx.x + idx.y*tile_indices_out.size.x;
				siz_out->copy_from(siz_in,tnum_in,tnum_out,0,0,discard_level,false,false,false);
				siz_out->finalize_all(tnum_out);
				// Note: do not open the output tile without first copying any tile-specific code-stream parameters
				kdu_tile tile_out = codestream_out.open_tile(idx+tile_indices_out.pos);
				assert(tnum_out == tile_out.get_tnum());
				copy_tile(tile_in,tile_out,tnum_in,tnum_out,siz_in,siz_out,0,num_blocks);
				tile_in.close();
				tile_out.close();
				flush_counter--;
				if ((flush_counter <= 0) && codestream_out.ready_for_flush())
				{
					flush_counter = INT_MAX;
					nel = codestream_out.trans_out(max_bytes,layer_bytes,total_layers);
					non_empty_layers = (nel > non_empty_layers)?nel:non_empty_layers;
				}
			}
		}
	
		// Generate the output code-stream
		if (codestream_out.ready_for_flush())
		{
			nel = codestream_out.trans_out(max_bytes,layer_bytes,total_layers);
			non_empty_layers = (nel > non_empty_layers)?nel:non_empty_layers;
		}
		if (non_empty_layers > total_layers)
			non_empty_layers = total_layers; // Can happen if a tile has more layers
	
		// Print out stats
		std::cout << "Code stream parsing for discard level = " << discard_level << std::endl;
		std::cout << "    Total compressed memory in  = " << mCodeStreamp->get_compressed_data_memory() << " bytes" << std::endl;
		std::cout << "    Total compressed memory out = " << codestream_out.get_compressed_data_memory() << " bytes" << std::endl;
		//std::cout << "    Output contains " << total_layers << " quality layers" << std::endl;		
		std::cout << "    Transferred " << num_blocks << " code-blocks from in to out" << std::endl;
		//std::cout << "    Read " << mCodeStreamp->get_num_tparts() << " tile-part(s) from a total of " << (int) tile_indices_in.area() << " tile(s)" << std::endl;
		std::cout << "    Total bytes read = " << mCodeStreamp->get_total_bytes() << std::endl;
		//std::cout << "    Wrote " << codestream_out.get_num_tparts() << " tile-part(s) in a total of " << (int) tile_indices_out.area() << " tile(s)" << std::endl;
		std::cout << "    Total bytes written = " << codestream_out.get_total_bytes() << std::endl;
		std::cout << "-------------" << std::endl;
	
		// Clean-up
		cleanupCodeStream();
		codestream_out.destroy();
		delete[] output_buffer;	
	}
	return;
}
// Returns TRUE to mean done, whether successful or not.
BOOL LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count)
{
	ECodeStreamMode mode = MODE_FAST;

	LLTimer decode_timer;

	if (!mCodeStreamp)
	{
		if (!initDecode(base, raw_image, decode_time, mode, first_channel, max_channel_count))
		{
			// Initializing the J2C decode failed, bail out.
			cleanupCodeStream();
			return TRUE; // done
		}
	}

	// These can probably be grabbed from what's saved in the class.
	kdu_dims dims;
	mCodeStreamp->get_dims(0,dims);

	// Now we are ready to walk through the tiles processing them one-by-one.
	kdu_byte *buffer = raw_image.getData();

	while (mTPosp->y < mTileIndicesp->size.y)
	{
		while (mTPosp->x < mTileIndicesp->size.x)
		{
			try
			{
				if (!mDecodeState)
				{
					kdu_tile tile = mCodeStreamp->open_tile(*(mTPosp)+mTileIndicesp->pos);

					// Find the region of the buffer occupied by this
					// tile.  Note that we have no control over
					// sub-sampling factors which might have been used
					// during compression and so it can happen that tiles
					// (at the image component level) actually have
					// different dimensions.  For this reason, we cannot
					// figure out the buffer region occupied by a tile
					// directly from the tile indices.  Instead, we query
					// the highest resolution of the first tile-component
					// concerning its location and size on the canvas --
					// the `dims' object already holds the location and
					// size of the entire image component on the same
					// canvas coordinate system.  Comparing the two tells
					// us where the current tile is in the buffer.
					S32 channels = base.getComponents() - first_channel;
					if (channels > max_channel_count)
					{
						channels = max_channel_count;
					}
					kdu_resolution res = tile.access_component(0).access_resolution();
					kdu_dims tile_dims; res.get_dims(tile_dims);
					kdu_coords offset = tile_dims.pos - dims.pos;
					int row_gap = channels*dims.size.x; // inter-row separation
					kdu_byte *buf = buffer + offset.y*row_gap + offset.x*channels;
					mDecodeState = new LLKDUDecodeState(tile, buf, row_gap);
				}
				// Do the actual processing
				F32 remaining_time = decode_time - decode_timer.getElapsedTimeF32();
				// This is where we do the actual decode.  If we run out of time, return false.
				if (mDecodeState->processTileDecode(remaining_time, (decode_time > 0.0f)))
				{
					delete mDecodeState;
					mDecodeState = NULL;
				}
				else
				{
					// Not finished decoding yet.
					//					setLastError("Ran out of time while decoding");
					return FALSE;
				}
			}
			catch (const char* msg)
			{
				base.setLastError(ll_safe_string(msg));
				base.decodeFailed();
				cleanupCodeStream();
				return TRUE; // done
			}
			catch (...)
			{
				base.setLastError( "Unknown J2C error" );
				base.decodeFailed();
				cleanupCodeStream();
				return TRUE; // done
			}


			mTPosp->x++;
		}
		mTPosp->y++;
		mTPosp->x = 0;
	}

	cleanupCodeStream();

	return TRUE;
}
// Returns TRUE to mean done, whether successful or not.
BOOL InWorldzJ2C::decodeImpl(ImageBaseForKDU* base, ImageBaseForKDU* raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count)
{
	if (!base || !raw_image)
	{
		// do nothing
		return TRUE;
	}

	if ((base->getData() == NULL) || (raw_image->getData() == NULL))
	{
		base->mLastErrorMsg = "We've been sent bad data";
		return TRUE;
	}

	ECodeStreamMode mode = MODE_FAST;

	DecodeTimer decode_timer;
	decode_timer.start();

	if (!mCodeStreamp)
	{
		if (!initDecode(*base, *raw_image, decode_time, mode, first_channel, max_channel_count))
		{
			// Initializing the J2C decode failed, bail out.
			cleanupCodeStream();
			return TRUE; // done
		}
	}

	// These can probably be grabbed from what's saved in the class.
	kdu_dims dims;
	mCodeStreamp->get_dims(0, dims);
	if (dims.size.x <=0 || dims.size.y <= 0)
	{
		std::cout << "bad sizes in stream, not decoding!" << std::endl;
		return TRUE;
	}

	// figure out why this is happening -- MC
	/*if (dims.pos.get_x() < 0)
	{
		dims.pos.set_x(0);
	}
	if (dims.pos.get_y() < 0)
	{
		dims.pos.set_y(0);
	}*/

	// Now we are ready to walk through the tiles processing them one-by-one.
	kdu_byte* buffer = raw_image->getData();
	if (!buffer)
	{
		// shouldn't ever happen!
		cleanupCodeStream();
		return TRUE;
	}

	while ((mTPosp->y < mTileIndicesp->size.y) && mTPosp->y >= 0)
	{
		while ((mTPosp->x < mTileIndicesp->size.x) && mTPosp->x >= 0)
		{
			try
			{
				if (!mDecodeState)
				{
					kdu_tile tile = mCodeStreamp->open_tile(*(mTPosp) + mTileIndicesp->pos);
					
					// Find the region of the buffer occupied by this
					// tile.  Note that we have no control over
					// sub-sampling factors which might have been used
					// during compression and so it can happen that tiles
					// (at the image component level) actually have
					// different dimensions.  For this reason, we cannot
					// figure out the buffer region occupied by a tile
					// directly from the tile indices.  Instead, we query
					// the highest resolution of the first tile-component
					// concerning its location and size on the canvas --
					// the `dims' object already holds the location and
					// size of the entire image component on the same
					// canvas coordinate system.  Comparing the two tells
					// us where the current tile is in the buffer.
					S32 channels = base->getComponents() - first_channel;
					if (channels > max_channel_count)
					{
						channels = max_channel_count;
					}

					kdu_resolution res = tile.access_component(0).access_resolution();
					kdu_dims tile_dims;
					res.get_dims(tile_dims);
					kdu_coords offset = tile_dims.pos - dims.pos;
					S32 row_gap = channels * dims.size.x; // inter-row separation
					kdu_byte* buf = buffer + offset.y*row_gap + offset.x * channels;
					mDecodeState = new LLKDUDecodeState(tile, buf, row_gap);
				}
				// Do the actual processing
				F32 remaining_time = decode_time - decode_timer.getElapsedTime();
				// This is where we do the actual decode.  If we run out of time, return false.
				if (mDecodeState->processTileDecode(remaining_time, (decode_time > 0.0f)))
				{
					delete mDecodeState;
					mDecodeState = NULL;
					//cleanupCodeStream(); ???
					base->mDecodeSuccessful = true;
					//return TRUE;
				}
				else
				{
					// Not finished decoding yet.
					//					setLastError("Ran out of time while decoding");
					base->mDecodeSuccessful = false;
					//cleanupCodeStream(); ?????
					return FALSE;
				}
			}
			catch (const char* msg)
			{
				if (msg)
				{
					base->mLastErrorMsg = msg;
				}
				base->mDecodeSuccessful = false;
				cleanupCodeStream();
				return TRUE; // done
			}
			catch (...)
			{
				base->mLastErrorMsg = "Unknown J2C error";
				base->mDecodeSuccessful = false;
				cleanupCodeStream();
				return TRUE; // done
			}
			mTPosp->x++;
		}
		mTPosp->y++;
		mTPosp->x = 0;
	}

	cleanupCodeStream();

	return TRUE;
}
LLImageJ2CKDU::~LLImageJ2CKDU()
{
	cleanupCodeStream(); // in case destroyed before decode completed
}
InWorldzJ2C::~InWorldzJ2C()
{
	cleanupCodeStream(); // in case destroyed before decode completed
}