/*
 * SSA_Compressor.java
 *
 * Created on 22 Март 2006 г., 17:05
 */

/**
 *
 * @author voit
 */
import java.io.*;
import java.util.*;

//----------------------------------------------------------------------------//

public class SSA_Compressor
{
	private SSA_VideoClient videoClient;

	private SSA_Transform trans;

	public int widthImage, heightImage, mult;

	private int countRLE;
	private char bufRLE[] = new char[768 * 576 * 4];

	private int prevHuffmanCount; // Количество элементов Хаффмана.
	private long prevHuffmanCRC, curHuffmanCRC;
	private int Color; ///Флаг по которому определяем цветная картинка или нет
	private boolean First = false;
	// Константы для кодировщика Хаффмана.

	private static final int EOF_SYMBOL = 0x10000;
	private static final int ESC_SYMBOL = 0x10001;
	private static final int SYMBOL_COUNT = 0x10000 + 2;
	private static final int NODE_COUNT = (((SYMBOL_COUNT * 2) - 1));
	private static final int ROOT_NODE = 0;
	private static final int VALUE_TRUE = 1;
	private static final int VALUE_FALSE = 0;

	// Значения битов поля typeImage в структуре COMPRESSIMAGEINFOHEADER.

	private static final int TI_MODEAUTONUM = 1; // Режим автонумерации кадров (работает только в MODE_SEQUENCE).
	private static final int TI_RELATIVE = 2;    // Сделать это изображение опорным (работает только в MODE_SEQUENCE).
	private static final int TI_GRAYSCALE = 4;   // Ч/б изображение.

	// Значения поля typeСompressor в структуре COMPRESSIMAGEINFOHEADER.

	private static final int TC_FAST = 0;
	private static final int TC_SLOW = 1;
	private static final int TC_MIX = 2;
	private static final int TC_ADAPTIVE = 0x80000000;
	private static final int TC_MASK = 0xFFFF;

	// Выделение памяти для таблицы кодирования Хаффмана.

	// Переменные для организации битового потока Хаффмана.
	private int maskHuffmanValue, curHuffmanValue;

	// Массив флагов лист/нелист для узлов.

	private byte isLeafArray[] = new byte[NODE_COUNT];
	private int childArray[] = new int[NODE_COUNT];

	// Выделение памяти для построения дерева Хаффмана.

	// Массив весов узлов.
	private int weightNodeArray[] = new int[NODE_COUNT];

	// Массив родителей для узлов.

	private int parentNodeArray[] = new int[NODE_COUNT];
	private int leafArray[] = new int[SYMBOL_COUNT];

	// Очередной незанятый узел.
	private int nextFreeNode;

	private int srcBlock[];

	private long idParentImageUnpack, idImage, idNeedImage;
	private boolean enableParentImageUnpack;
	private short parentWaveletUnpack[] = new short[768 * 576 * 3 / 2];
	private short bufImage[] = new short[768 * 576 * 3 / 2];

	private short workArray[] = new short[768 * 576 * 3 / 2];
	private int dataSize; // Размер блока данных сжатого кадра.
	private int curDataSize; // Счётчик реально считанных данных.
	private DataInputStream inStream; // Входной поток сжатых данных.

	private boolean buildedHuffmanTree = false; // Флаг присутствия таблицы декодирования Хаффмана.
	private int recvValue; // Считанное значение процедурой recvInt().

	private long totalStartTime, totalEndTime;
	private long localStartTime, localEndTime;

	////////////////////////////////////////////////////////////////////////////
	//
	SSA_Compressor( SSA_VideoClient vc)
	{
		videoClient = vc;

		trans = new SSA_Transform( vc, Color );

		enableParentImageUnpack = false;
	}
	//
	////////////////////////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////////////////////////
	//
	protected long convertIntToLong( int i )
	{
		if( i >= 0 )
			return i;
		else
			return (long) (i & 0x7FFFFFFF) + 0x80000000;
	}
	//
	////////////////////////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////////////////////////
	//
	protected int decompressImageColor( int srcBuf[], int dstBuf[], DataInputStream inStream, int dataSize, int Color )
	{
		////////////////////////////////////////////////////////////////////////
		int i, j;
		int quantSizeGrayComponent[] = new int[4];
		int quantSizeColorComponent[] = new int[3];
		int offset, typeCompressor;
		short wavelet[];
		String time;
		int countIndex, srcIndex, countWLT;
		////////////////////////////////////////////////////////////////////////
		

		totalStartTime = System.currentTimeMillis();

		srcBlock = srcBuf;

		this.inStream = inStream;
		this.dataSize = dataSize;

		curDataSize = 0;

		// Простенькая проверка на "правильность" кадра: ID, CRC и т.д.

		i = validImage();

		if( i != 0 )
		{
			switch( i )
			{
				case -1:

					videoClient.disconnect();

					break;

				case 1:

					break;

				case 2:

					break;

				case 3:

					break;
			}

			//videoClient.deb.msg( "Decompress error: " + i );

			return SSA_General.DECOMPRESS_ERROR;
		}

		widthImage = srcBuf[7] & 0xFFFF; //infoHeader->widthImage;
		heightImage = srcBuf[7] >>> 16;  //infoHeader->heightImage;

		//videoClient.deb.msg( "Width: " + widthImage );
		//videoClient.deb.msg( "Height: " + heightImage );

		mult = widthImage * heightImage;

		typeCompressor = srcBuf[14];

		// ID текущего кадра и требуемого опорного.

		idImage = (convertIntToLong(srcBuf[4]) << 32) | convertIntToLong(srcBuf[3]);
		idNeedImage = (convertIntToLong(srcBuf[6]) << 32) | convertIntToLong(srcBuf[5]);

		// Копирование коэффицентов квантования в массив.

		quantSizeGrayComponent[0] = (srcBuf[12] >> 8) & 0xFF;
		quantSizeGrayComponent[1] = (srcBuf[12] >> 16) & 0xFF;
		quantSizeGrayComponent[2] = (srcBuf[12] >> 24) & 0xFF;
		quantSizeGrayComponent[3] = srcBuf[13] & 0xFF;
		quantSizeColorComponent[0] = (srcBuf[13] >> 8) & 0xFF;
		quantSizeColorComponent[1] = (srcBuf[13] >> 16) & 0xFF;
		quantSizeColorComponent[2] = (srcBuf[13] >> 24) & 0xFF;
		
		if(!First)
		{
			if((srcBlock[12] & TI_GRAYSCALE)!=0)
				Color = 0;
			else
				Color = 1;
			trans = new SSA_Transform( videoClient, Color);
			First = true;		
		}

		/*if( (srcBlock[12] & TI_RELATIVE) != 0 )
			videoClient.deb.msg( "TI_RELATIVE. idNeedImage = " + idNeedImage );
		else
			videoClient.deb.msg( "TI_PARENT. idImage = " + idImage );*/

		localStartTime = System.currentTimeMillis();

		if( (typeCompressor & TC_ADAPTIVE) != 0 )
			countRLE = decompressBufHuffmanAdaptive( srcBuf, bufRLE );
		else
			countRLE = decompressBufHuffman( srcBuf, bufRLE );

		if( countRLE == -1 )
		{
			videoClient.disconnect();

			//videoClient.deb.msg( "CountRLE = -1" );

			return SSA_General.DECOMPRESS_ERROR;
		}

		localEndTime = System.currentTimeMillis();

		if( srcBuf[10] != countRLE )
		{
			//videoClient.deb.msg( "srcBuf[10] != countRLE" );

			return SSA_General.DECOMPRESS_ERROR;
		}

		// Для SLOW-FAST-алгоритма отдельная ветка.

		if( (typeCompressor & TC_MASK) == TC_MIX )
		{
			wavelet = parentWaveletUnpack; //Значение по умолчанию, чтоб не было ошибки при компиляции.

			if( (srcBlock[12] & TI_RELATIVE) != 0 )
			{
				// Декомпрессия буфера с данными RLE.
				countWLT = decompressBufRLE( bufRLE, workArray, countRLE );

				if( idParentImageUnpack == idNeedImage )
				{
					// Копирование опорного кадра в буфер распаковки.
					// (это не вейвлет, а уже реконструированное изображение)

					for( i = 0; i < ((mult * 3) >> 1); i++ )
						bufImage[i] = parentWaveletUnpack[i];
				}
				else
					for( i = 0; i < ((mult * 3) >> 1); i++ )
						bufImage[i] = 0;

				countIndex = workArray[0];

				srcIndex = countIndex + 1;

				// Переброс значений Y-компоненты.
				if(Color!=0)
				{
					srcIndex = extractImageFromCoeffs( countIndex, widthImage, 4, srcIndex, 0, quantSizeGrayComponent );
					srcIndex = extractImageFromCoeffs( countIndex, widthImage >> 1, 3, srcIndex, mult, quantSizeColorComponent );
					srcIndex = extractImageFromCoeffs( countIndex, widthImage >> 1, 3, srcIndex, mult * 5 / 4, quantSizeColorComponent );
				}

				trans.transformYBRtoRGB( bufImage, dstBuf, widthImage, heightImage );
			}
			else
			{
				countWLT = decompressBufRLE( bufRLE, bufImage, countRLE );

				idParentImageUnpack = idImage;
				enableParentImageUnpack = true;

				// Обратное вейвлет-преобразовние яркостной составляющей.
				trans.waveletTransformReverseFloat( bufImage, parentWaveletUnpack, 0, widthImage, heightImage, 4, quantSizeGrayComponent );

				// Обратное вейвлет-преобразовние цветовых составляющих.
				if(Color!=0)
				{
					trans.waveletTransformReverseFloat( bufImage, parentWaveletUnpack, mult, widthImage >> 1, heightImage >> 1, 3, quantSizeColorComponent );
					trans.waveletTransformReverseFloat( bufImage, parentWaveletUnpack, mult * 5 / 4, widthImage >> 1, heightImage >> 1, 3, quantSizeColorComponent );
				}

				trans.transformYBRtoRGB( parentWaveletUnpack, dstBuf, widthImage, heightImage );
			}
		}
		else
		{
			if( (srcBlock[12] & TI_RELATIVE) != 0 )
			{
				// Декомпрессия буфера с данными RLE.
				countWLT = decompressBufRLE( bufRLE, bufImage, countRLE );

				// Копирование родительского кадра в буфер.

				if( idParentImageUnpack == idNeedImage )
				{
					for( i = 0; i < ((mult * 3) >> 1); i++ )
						workArray[i] = parentWaveletUnpack[i];
				}
				else
					for( i = 0; i < ((mult * 3) >> 1); i++ )
						workArray[i] = 0;

				countIndex = bufImage[0];
				srcIndex = countIndex + 1;

				// Переброс значений Y-компоненты.

				for( i = 0; i < countIndex; i++ )
					workArray[bufImage[i + 1]] = bufImage[srcIndex++];

				srcIndex = setKoeffs( countIndex, 4, 0, srcIndex );

				// Обработка коэффициентов от Cb.

				for( i = 0; i < countIndex; i++ )
					workArray[mult + bufImage[i + 1]] = bufImage[srcIndex++];

				srcIndex = setKoeffs( countIndex, 3, mult, srcIndex );

				// Обработка коэффициентов от Cr.

				offset = mult * 5 >> 2;

				for( i = 0; i < countIndex; i++ )
					workArray[offset + bufImage[i + 1]] = bufImage[srcIndex++];

				srcIndex = setKoeffs( countIndex, 3, offset, srcIndex );

				wavelet = workArray;
			}
			else
			{
				countWLT = decompressBufRLE( bufRLE, parentWaveletUnpack, countRLE );

				idParentImageUnpack = idImage;
				enableParentImageUnpack = true;

				wavelet = parentWaveletUnpack;
			}

			if( (typeCompressor & TC_MASK) == TC_SLOW )
			{
				// Обратное вейвлет-преобразовние яркостной составляющей.
				trans.waveletTransformReverseFloat( wavelet, bufImage, 0, widthImage, heightImage, 4, quantSizeGrayComponent );

				// Обратное вейвлет-преобразовние цветовых составляющих.
				if(Color!=0)
				{
					trans.waveletTransformReverseFloat( wavelet, bufImage, mult, widthImage >> 1, heightImage >> 1, 3, quantSizeColorComponent );
					trans.waveletTransformReverseFloat( wavelet, bufImage, mult * 5 / 4, widthImage >> 1, heightImage >> 1, 3, quantSizeColorComponent );
				}
			}

			if( (typeCompressor & TC_MASK) == TC_FAST )
			{
				// Обратное вейвлет-преобразовние яркостной составляющей.
				trans.waveletTransformReverse( wavelet, bufImage, 0, widthImage, heightImage, 4, quantSizeGrayComponent );

				// Обратное вейвлет-преобразовние цветовых составляющих.
				if(Color!=0)
				{
					trans.waveletTransformReverse( wavelet, bufImage, mult, widthImage >> 1, heightImage >> 1, 3, quantSizeColorComponent );
					trans.waveletTransformReverse( wavelet, bufImage, mult * 5 / 4, widthImage >> 1, heightImage >> 1, 3, quantSizeColorComponent );
				}
			}

			trans.transformYBRtoRGB( bufImage, dstBuf, widthImage, heightImage );
		}

		totalEndTime = System.currentTimeMillis();
		time = Float.toString(totalEndTime-totalStartTime);
		//videoClient.deb.msg( "Время декомпрессии:"+time );

		return SSA_General.DECOMPRESS_OK;
	}
	//
	////////////////////////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////////////////////////
	//
	private int validImage()
	{
		////////////////////////////////////////////////////////////////////////
		int i, count;
		////////////////////////////////////////////////////////////////////////

		if( !recvInt() )
			return -1;

		if( recvValue != SSA_General.ID_SSA_VIDEOCODEC )
		{
			//videoClient.deb.msg( "CODEC ID: " + recvValue );

			return 3;
		}

		srcBlock[curDataSize++] = recvValue;

		// Чтение заголовка сжатого кадра.

		for( i = 1; i < SSA_General.SIZE_INFO_HEADER; i++ )
		{
			if( !recvInt() )
				return -1;

			srcBlock[curDataSize++] = recvValue;
		}

		if( srcBlock[15] != 0xAACCBBDD )
			return 2;

		curHuffmanCRC = 0;

		// Если адаптивное сжатие, таблицу читать не нужно.

		if( (srcBlock[14] & TC_ADAPTIVE) != 0 )
			return 0;

		// Чтение таблицы Хаффмана и обновременный подсчёт CRC.

		count = srcBlock[8];

		for( i = 0; i < count; i++ )
		{
			if( !recvInt() )
				return -1;

			srcBlock[curDataSize++] = recvValue;

			curHuffmanCRC += convertIntToLong( recvValue );
		}

		if( (int) (curHuffmanCRC & 0xFFFFFFFF) != srcBlock[9] )
			return 1;

		return 0;
	}
	//
	////////////////////////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////////////////////////
	// destData - Массив для RLE-данных.
	//
	private int decompressBufHuffman( int srcData[], char destData[] )
	{
		////////////////////////////////////////////////////////////////////////
		int i, huffmanCount, waitValues;
		int retValue, value;
		////////////////////////////////////////////////////////////////////////

		huffmanCount = srcData[8];
		waitValues = srcData[10];

		// Проверка условия перестроения дерева Хаффмана (для ускорения удалена проверка равности таблиц).

		if( !((huffmanCount == prevHuffmanCount) && (prevHuffmanCRC == curHuffmanCRC) && buildedHuffmanTree) )
		{
			prevHuffmanCount = huffmanCount;
			prevHuffmanCRC = curHuffmanCRC;

			// Построение таблицы декодирования.
			buildHuffmanTree();
		}

		// Инициализация входного битового потока.

		maskHuffmanValue = 0x80000000;

		if( !recvInt() )
			return -1;

		curHuffmanValue = recvValue;

		retValue = waitValues;

		for( i = 0; i < waitValues; i++ )
		{
			value = decodeHuffmanSymbol();

			if( value == -1 )
				return -1;

			if( value == EOF_SYMBOL )
			{
				retValue = 0;

				break;
			}

			destData[i] = (char) (value & 0xFFFF);
		}

		if( (retValue != 0) && (decodeHuffmanSymbol() == EOF_SYMBOL) )
			return retValue;
		else
			return 0;
	}
	//
	////////////////////////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////////////////////////
	//
	private void buildHuffmanTree()
	{
		////////////////////////////////////////////////////////////////////////
		int i, j;
		int value, count;
		////////////////////////////////////////////////////////////////////////

		// Инициализация дерева Хаффмана.
		initHuffmanTree();

		// Построение дерева Хаффмана.
		for( i = 0; i < prevHuffmanCount; i++ )
		{
			value = srcBlock[SSA_General.SIZE_INFO_HEADER + i] >>> 16;
			count = srcBlock[SSA_General.SIZE_INFO_HEADER + i] & 0xFFFF;

			addNewHuffmanNode( value );

			for( j = 0; j < count; j++ )
				updateHuffmanModel( value );
		}

		// Выставление флага, что хоть какая-то таблица построена.
		buildedHuffmanTree = true;
	}
	//
	////////////////////////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////////////////////////
	//
	private int decodeHuffmanSymbol()
	{
		////////////////////////////////////////////////////////////////////////
		int curNode, value;
		////////////////////////////////////////////////////////////////////////

		curNode = ROOT_NODE;

		// Проход по дереву, пока не найдётся лист.

		while( isLeafArray[curNode] != VALUE_TRUE )
		{
			// Чтение текущего бита из потока Хаффмана.

			if( (curHuffmanValue & maskHuffmanValue) != 0 )
				curNode = childArray[curNode] + 1;
			else
				curNode = childArray[curNode];

			maskHuffmanValue >>>= 1;

			// Если закончилось 32 буферизованных бита, выборка следующей порции.

			if( maskHuffmanValue == 0 )
			{
				maskHuffmanValue = 0x80000000;

				if( !recvInt() )
					return -1;

				curHuffmanValue = recvValue;

				while( isLeafArray[curNode] != VALUE_TRUE )
				{
					// Чтение текущего бита из потока Хаффмана.

					if( (curHuffmanValue & maskHuffmanValue) != 0 )
						curNode = childArray[curNode] + 1;
					else
						curNode = childArray[curNode];

					maskHuffmanValue >>>= 1;
				}

				break;
			}
		}

		value = childArray[curNode];

		if( value == ESC_SYMBOL )
		{
			value = readCodeFromStream();

			if( value == -1 )
				return -1;
		}

		return value;
	}
	//
	////////////////////////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////////////////////////
	// Инициализация дерева Хаффмана.
	// Начальное дерево кодирует два символа EOF и ESCAPE
	//      EOF - Конец файла.
	//      ESC - Префикс, означающий, что следующие 16 бит не закодированы.
	//                              |------|
	//                              | ROOT |
	//                              | w=2  |
	//                              |------|
	//                               /        \
	//                       /                  \
	//              |-----|          |-----|
	//              | EOF |          | ESC |
	//              | w=1 |          | w=1 |
	//              |-----|          |-----|
	//
	private void initHuffmanTree()
	{
		////////////////////////////////////////////////////////////////////////
		int i;
		////////////////////////////////////////////////////////////////////////

		//    Arrays.fill(leafArray, 5, 10, -1); // Только в AppletViewer работает.

		for( i = 0; i < SYMBOL_COUNT; i++ )
			leafArray[i] = -1;

		childArray[0] = 1;
		isLeafArray[0] = VALUE_FALSE;
		weightNodeArray[0] = 2;
		parentNodeArray[0] = -1;
		childArray[1] = EOF_SYMBOL;
		isLeafArray[1] = VALUE_TRUE;
		weightNodeArray[1] = 1;
		parentNodeArray[1] = 0;
		childArray[2] = ESC_SYMBOL;
		isLeafArray[2] = VALUE_TRUE;
		weightNodeArray[2] = 1;
		parentNodeArray[2] = 0;
		leafArray[EOF_SYMBOL] = 1;
		leafArray[ESC_SYMBOL] = 2;
		nextFreeNode = 3;
	}
	//
	////////////////////////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////////////////////////
	//
	private void addNewHuffmanNode( int newValue )
	{
		////////////////////////////////////////////////////////////////////////
		int srcNode, copyNode, newNode;
		////////////////////////////////////////////////////////////////////////

		// Расщепление самого лёгкого узла на 2 новых узла, один из которых будет равен исходному узлу.

		srcNode = nextFreeNode - 1;
		copyNode = nextFreeNode;
		newNode = nextFreeNode + 1;
		nextFreeNode += 2;

		weightNodeArray[copyNode] = weightNodeArray[srcNode];
		parentNodeArray[copyNode] = srcNode;       //2
		isLeafArray[copyNode] = VALUE_TRUE;        //3
		childArray[copyNode] = childArray[srcNode];//4
		leafArray[childArray[srcNode]] = copyNode; //5
		childArray[srcNode] = copyNode;
		isLeafArray[srcNode] = VALUE_FALSE;        //7

		// Непосредственно добавляемый узел с самым мелким весом.
		weightNodeArray[newNode] = 0;              //8
		parentNodeArray[newNode] = srcNode;        //9
		childArray[newNode] = newValue;            //10
		isLeafArray[newNode] = VALUE_TRUE;         //11
		leafArray[newValue] = newNode;             //12
	}
	//
	////////////////////////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////////////////////////
	//
	private void updateHuffmanModel( int value )
	{
		////////////////////////////////////////////////////////////////////////
		int curNode, newNode;
		int tempVal, node1, node2;
		byte tempByte;
		////////////////////////////////////////////////////////////////////////

		curNode = leafArray[value];

		while( curNode != - 1 )
		{
			weightNodeArray[curNode]++;

			for( newNode = curNode; newNode > ROOT_NODE; newNode-- )
				if( weightNodeArray[newNode - 1] >= weightNodeArray[curNode] )
					break;

			if( curNode != newNode )
			{
				// Обмен узлов местами.

				node1 = curNode;
				node2 = newNode;

				// Если первый узел является листом, тогда в списке листов
				// изменяется ссылка. Если же нет, то у детей меняется родитель.

				if( isLeafArray[node1] != 0 )
					leafArray[childArray[node1]] = node2;
				else
				{
					parentNodeArray[childArray[node1]] = node2;
					parentNodeArray[childArray[node1] + 1] = node2;
				}

				// Если второй узел является листом, тогда в списке листов
				// изменяется ссылка. Если же нет, то у детей меняется родитель.

				if( isLeafArray[node2] != 0 )
					leafArray[childArray[node2]] = node1;
				else
				{
					parentNodeArray[childArray[node2]] = node1;
					parentNodeArray[childArray[node2] + 1] = node1;
				}

				tempVal = weightNodeArray[node1];
				weightNodeArray[node1] = weightNodeArray[node2];
				weightNodeArray[node2] = tempVal;

				tempVal = childArray[node1];
				childArray[node1] = childArray[node2];
				childArray[node2] = tempVal;

				tempByte = isLeafArray[node1];
				isLeafArray[node1] = isLeafArray[node2];
				isLeafArray[node2] = tempByte;

				curNode = newNode;
			}

			curNode = parentNodeArray[curNode];
		}
	}
	//
	////////////////////////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////////////////////////
	//
	private int readCodeFromStream()
	{
		////////////////////////////////////////////////////////////////////////
		int value, app1, app2, remainBits;
		////////////////////////////////////////////////////////////////////////

		if( (maskHuffmanValue & 0x7FFF) == 0 )
		{
			remainBits = 0;

			maskHuffmanValue >>>= 16;

			value = maskHuffmanValue;

			while( value != 0 )
			{
				value >>>= 1;

				remainBits++;
			}

			value = (curHuffmanValue >>> remainBits) & 0xFFFF;

			if( maskHuffmanValue == 0 )
			{
				maskHuffmanValue = 0x80000000;

				if( !recvInt() )
					return -1;

				curHuffmanValue = recvValue;
			}
		}
		else
		{
			remainBits = 0;

			value = maskHuffmanValue;

			while( value != 0 )
			{
				value >>>= 1;

				remainBits++;
			}

			if( !recvInt() )
				return -1;

			app1 = curHuffmanValue << (16 - remainBits);
			app2 = recvValue >>> (remainBits + 16);

			value = (app1 | app2) & 0xFFFF;

			curHuffmanValue = recvValue;
			maskHuffmanValue = 1 << (remainBits + 15);
		}

		return value;
	}
	//
	////////////////////////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////////////////////////
	// Распаковка буфера с данными, сжатыми методом RLE.
	//
	private int decompressBufRLE( char srcBuf[], short destBuf[], int lenSrcBlock )
	{
		////////////////////////////////////////////////////////////////////////
		int i, j, k, countZeros;
		char val;
		////////////////////////////////////////////////////////////////////////

		// Защиты от вылезания за пределы массивов не предусмотрено, т.к. она
		// значительно снижает скорость выполнения данной процедуры

		i = 0;
		j = 0;

		while( true )
		{
			val = srcBuf[i];

			countZeros  = val & 0x3FFF;

			if( (val & 0xC000) == 0x8000 )
			{
				for( k = 0; k < countZeros; k++ )
					destBuf[j++] = 0;

				break;
			}

			i++;

			for( k = 0; k < countZeros; k++ )
				destBuf[j++] = 0;

			// Переброс ненулей.

			while( true )
			{
				val = srcBuf[i];

				if( ((val & 0xC000) == 0x8000) || ((val & 0xC000) == 0x4000) )
					break;

				destBuf[j++] = (short) val;

				i++;
			}
		}

		return j;
	}
	//
	////////////////////////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////////////////////////
	//
	private int setKoeffs( int countIndex, int level, int dstIndex, int srcIndex )
	{
		////////////////////////////////////////////////////////////////////////
		int len, i, j, k, x, y, depth;
		int sizeSeg1, sizeSeg2, sizeSeg3;
		int wd, ht, curWd, curIndex, offset;
		////////////////////////////////////////////////////////////////////////

		// Неоптимизировано.

		wd = widthImage >> 4;
		ht = heightImage >> 4;
		len = 1;

		for( depth = 0; depth < level; depth++ )
		{
			curWd = wd << depth;

			sizeSeg1 = curWd * (ht << depth);
			sizeSeg2 = sizeSeg1 << 1;
			sizeSeg3 = sizeSeg1 + sizeSeg2;

			for( k = 1; k <= countIndex; k++ )
			{
				curIndex = bufImage[k];

				y = (int)(curIndex / wd);
				x = curIndex - wd * y;

				offset = len * (y * curWd + x) + dstIndex;

				for( i = 0; i < len; i++ )
				{
					for (j = 0; j < len; j++)
					{
						workArray[sizeSeg1 + offset] = bufImage[srcIndex++];
						workArray[sizeSeg2 + offset] = bufImage[srcIndex++];
						workArray[sizeSeg3 + offset] = bufImage[srcIndex++];

						offset++;
					}

					offset = offset - len + curWd;
				}
			}

			len <<= 1;
		}

		return srcIndex;
	}
	//
	////////////////////////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////////////////////////
	//
	private boolean recvInt()
	{
		////////////////////////////////////////////////////////////////////////
		int i, val;
		byte rcv[] = new byte[4];
		////////////////////////////////////////////////////////////////////////

		if( dataSize < 4 )
		{
			val = 0;

			for( i = 0; i < 4; i++ )
			{
				try
				{
					val |= (inStream.read()) << (8 * i);

					dataSize--;

					if( dataSize == 0 )
						break;
				}
				catch(Exception e)
				{
					return false;
				}
			}
		}
		else
		{
			try
			{
				dataSize -= 4;
        
				val = inStream.read();
				val |= inStream.read() << 8;
				val |= inStream.read() << 16;
				val |= inStream.read() << 24;
			}
			catch( Exception e )
			{
				return false;
			}
		}

		recvValue = val;

		return true;
	}
	//
	////////////////////////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////////////////////////
	// destData - Массив для RLE-данных.
	//
	private int decompressBufHuffmanAdaptive( int srcData[], char destData[] )
	{
		////////////////////////////////////////////////////////////////////////
		int i, waitValues;
		int retValue, value;
		////////////////////////////////////////////////////////////////////////

		prevHuffmanCount = 0;

		waitValues = srcData[10];

		// Инициализация дерева Хаффмана.
		initHuffmanTree();

		// Инициализация входного битового потока.

		maskHuffmanValue = 0x80000000;

		if( !recvInt() )
			return -1;

		curHuffmanValue = recvValue;

		retValue = waitValues;

		for( i = 0; i < waitValues; i++ )
		{
			value = decodeHuffmanAdaptiveSymbol();

			if( value == -1 )
				return -1;

			if( value == EOF_SYMBOL )
			{
				retValue = 0;

				break;
			}

			destData[i] = (char) value;

			updateHuffmanModel( value );
		}

		if( (retValue != 0) && (decodeHuffmanAdaptiveSymbol() == EOF_SYMBOL) )
			return retValue;
		else
			return 0;
	}
	//
	////////////////////////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////////////////////////
	//
	private int decodeHuffmanAdaptiveSymbol()
	{
		////////////////////////////////////////////////////////////////////////
		int value, curNode;
		////////////////////////////////////////////////////////////////////////

		curNode = ROOT_NODE;

		while( isLeafArray[curNode] != VALUE_TRUE )
		{
			// Чтение текущего бита из потока Хаффмана.

			if( (curHuffmanValue & maskHuffmanValue) != 0 )
				curNode = childArray[curNode] + 1;
			else
				curNode = childArray[curNode];

			maskHuffmanValue >>>= 1;

			// Если закончилось 32 буферизованных бита, выборка следующей порции.

			if( maskHuffmanValue == 0 )
			{
				maskHuffmanValue = 0x80000000;

				if( !recvInt() )
					return -1;

				curHuffmanValue = recvValue;

				// Для ускорения цикл внесён вовнутрь.

				while( isLeafArray[curNode] != VALUE_TRUE )
				{
					// Чтение текущего бита из потока Хаффмана.

					if( (curHuffmanValue & maskHuffmanValue) != 0 )
						curNode = childArray[curNode] + 1;
					else
						curNode = childArray[curNode];

					maskHuffmanValue >>>= 1;
				}

				break;
			}
		}

		value = childArray[curNode];

		if( value == ESC_SYMBOL )
		{
			value = readCodeFromStream();

			if( value == -1 )
				return -1;

			updateHuffmanModel( ESC_SYMBOL );

			addNewHuffmanNode( value );
		}

		return value;
	}
	//
	////////////////////////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////////////////////////
	//
	private int extractImageFromCoeffs( int countIndex, int widthMatrix, int level,
		int srcIndex, int dstIndex, int quantSizeArray[] )
	{
		////////////////////////////////////////////////////////////////////////
		int i, j, k, stepSize, transform, base, curSize;
		int offset, x, y, curIndex, counter;
		int wlt[] = new int[256];
		int a1, a2, a3, a4, b1, b2, b3, b4;
		int index0, index1, index2, index3;
		int sizeCell, beginIndex = srcIndex;
		////////////////////////////////////////////////////////////////////////

		sizeCell = 1 << level;

		index0 = srcIndex + countIndex;
		index1 = srcIndex + countIndex * 4;
		index2 = srcIndex + countIndex * 16;
		index3 = srcIndex + countIndex * 64;

		for( counter = 0; counter < countIndex; counter++ )
		{
			offset = quantSizeArray[level - 1] - 2;

			b1 = workArray[srcIndex++];
			b2 = workArray[index0++];
			b3 = workArray[index0++];
			b4 = workArray[index0++];

			a1 = b1 + b2 + b3 + b4;
			a2 = b1 + b2 - b3 - b4;
			a3 = b1 - b2 + b3 - b4;
			a4 = b1 - b2 - b3 + b4;

			if( a1 < 0 ) 
				a1 = -((-a1) << offset);
			else 
				a1 <<= offset;

			if( a2 < 0 ) 
				a2 = -((-a2) << offset);
			else 
				a2 <<= offset;

			if( a3 < 0 ) 
				a3 = -((-a3) << offset);
			else 
				a3 <<= offset;

			if( a4 < 0 ) 
				a4 = -((-a4) << offset);
			else 
				a4 <<= offset;

			k = 1 << (level - 1);

			stepSize = k * sizeCell;

			wlt[0] = a1;
			wlt[k] = a2;
			wlt[stepSize] = a3;
			wlt[stepSize + k] = a4;

			curSize = 2;

			for( transform = 1; transform < level - 1; transform++ )
			{
				offset = quantSizeArray[level - transform - 1];

				k >>= 1;

				stepSize = k * sizeCell;

				for( i = 0; i < curSize; i++ )
				{
					base = i * stepSize << 1;

					for( j = 0; j < curSize; j++ )
					{
						b1 = wlt[base];

						switch( transform )
						{
							case 1:

								b2 = workArray[index1++];
								b3 = workArray[index1++];
								b4 = workArray[index1++];

								break;

							case 2:

								b2 = workArray[index2++];
								b3 = workArray[index2++];
								b4 = workArray[index2++];

								break;
						}

						// Расквантование коэффициентов.

						if( b2 < 0 ) 
							b2 = -((-b2) << offset);
						else 
							b2 <<= offset;

						if( b3 < 0 ) 
							b3 = -((-b3) << offset);
						else 
							b3 <<= offset;

						if( b4 < 0 ) 
							b4 = -((-b4) << offset);
						else
							b4 <<= offset;

						a1 = b1 + b2 + b3 + b4;
						a2 = b1 + b2 - b3 - b4;
						a3 = b1 - b2 + b3 - b4;
						a4 = b1 - b2 - b3 + b4;

						// Деление полученных значений на 4.

						if( a1 < 0 ) 
							a1 = -((-a1) >> 2);
						else 
							a1 >>= 2;

						if( a2 < 0 ) 
							a2 = -((-a2) >> 2);
						else 
							a2 >>= 2;

						if( a3 < 0 ) 
							a3 = -((-a3) >> 2);
						else 
							a3 >>= 2;

						if( a4 < 0 ) 
							a4 = -((-a4) >> 2);
						else 
							a4 >>= 2;

						wlt[base] = a1;
						wlt[base + k] = a2;
						wlt[base + stepSize] = a3;
						wlt[base + stepSize + k] = a4;

						base += k << 1;
					}
				}

				curSize <<= 1;
			}

			offset = quantSizeArray[0];

			stepSize = sizeCell;

			for( i = 0; i < curSize; i++ )
			{
				base = i * stepSize << 1;

				for( j = 0; j < curSize; j++ )
				{
					b1 = wlt[base];

					if( level == 4 )
					{
						b2 = workArray[index3++];
						b3 = workArray[index3++];
						b4 = workArray[index3++];
					}
					else
					{
						b2 = workArray[index2++];
						b3 = workArray[index2++];
						b4 = workArray[index2++];
					}

					// Расквантование коэффициентов.

					if( b2 < 0 ) 
						b2 = -((-b2) << offset);
					else 
						b2 <<= offset;

					if( b3 < 0 )
						b3 = -((-b3) << offset);
					else 
						b3 <<= offset;

					if( b4 < 0 ) 
						b4 = -((-b4) << offset);
					else 
						b4 <<= offset;

					a1 = b1 + b2 + b3 + b4;
					a2 = b1 + b2 - b3 - b4;
					a3 = b1 - b2 + b3 - b4;
					a4 = b1 - b2 - b3 + b4;

					if( a1 < 0 ) 
						a1 = 0;
					else 
					{ 
						a1 >>= 2; 

						if( a1 > 255 ) 
							a1 = 255; 
					}

					if( a2 < 0 ) 
						a2 = 0;
					else 
					{ 
						a2 >>= 2; 
                       
						if( a2 > 255 ) 
							a2 = 255; 
					}

					if( a3 < 0 ) 
						a3 = 0;
					else 
					{ 
						a3 >>= 2; 
                       
						if( a3 > 255 ) 
							a3 = 255; 
					}

					if( a4 < 0 ) 
						a4 = 0;
					else 
					{ 
						a4 >>= 2; 
                       
						if( a4 > 255 ) 
							a4 = 255; 
					}

					wlt[base] = a1;
					wlt[base + 1] = a2;
					wlt[base + stepSize] = a3;
					wlt[base + stepSize + 1] = a4;

					base += 2;
				}
			}

			int wd = widthMatrix >> level;
  
			curIndex = workArray[counter + 1];

			y = (int) (curIndex / wd);
			x = curIndex - wd * y;

			x *= sizeCell;
			y *= sizeCell;

			offset = 0;

			for (i = 0; i < sizeCell; i++)
			{
				base = (i + y) * widthMatrix + x + dstIndex;

				for( j = 0; j < sizeCell; j++ )
					bufImage[base++] = (short) wlt[offset++];
			}
		}

		if( level == 4 )
			srcIndex = beginIndex + countIndex * 256;

		if( level == 3 )
			srcIndex = beginIndex + countIndex * 64;

		return srcIndex;
	}
	//
	////////////////////////////////////////////////////////////////////////////
} 
