/*******************************************************************************
 * Copyright 2011 See AUTHORS file.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package com.badlogic.gdx.utils.compression;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/** Adapted from LZMA SDK version 9.22.
 * 
 * This was modified to be used directly on streams, rather than via the command line as in the LZMA SDK.
 * 
 * We only currently allow the default LZMA options to be used, as we know it works on for our target usage. */
public class Lzma {
	static class CommandLine {
		public static final int kEncode = 0;
		public static final int kDecode = 1;
		public static final int kBenchmak = 2;

		public int Command = -1;
		public int NumBenchmarkPasses = 10;

		public int DictionarySize = 1 << 23;
		public boolean DictionarySizeIsDefined = false;

		public int Lc = 3;
		public int Lp = 0;
		public int Pb = 2;

		public int Fb = 128;
		public boolean FbIsDefined = false;

		public boolean Eos = false;

		public int Algorithm = 2;
		public int MatchFinder = 1;

		public String InFile;
		public String OutFile;
	}

	/** Compresses the given {@link InputStream} into the given {@link OutputStream}.
	 * 
	 * @param in the {@link InputStream} to compress
	 * @param out the {@link OutputStream} to compress to
	 * @throws IOException */
	static public void compress (InputStream in, OutputStream out) throws IOException {
		CommandLine params = new CommandLine();
		boolean eos = false;
		if (params.Eos) eos = true;
		com.badlogic.gdx.utils.compression.lzma.Encoder encoder = new com.badlogic.gdx.utils.compression.lzma.Encoder();
		if (!encoder.SetAlgorithm(params.Algorithm)) throw new RuntimeException("Incorrect compression mode");
		if (!encoder.SetDictionarySize(params.DictionarySize)) throw new RuntimeException("Incorrect dictionary size");
		if (!encoder.SetNumFastBytes(params.Fb)) throw new RuntimeException("Incorrect -fb value");
		if (!encoder.SetMatchFinder(params.MatchFinder)) throw new RuntimeException("Incorrect -mf value");
		if (!encoder.SetLcLpPb(params.Lc, params.Lp, params.Pb)) throw new RuntimeException("Incorrect -lc or -lp or -pb value");
		encoder.SetEndMarkerMode(eos);
		encoder.WriteCoderProperties(out);
		long fileSize;
		if (eos) {
			fileSize = -1;
		} else {
			if ((fileSize = in.available()) == 0) {
				fileSize = -1;
			}
		}
		for (int i = 0; i < 8; i++) {
			out.write((int)(fileSize >>> (8 * i)) & 0xFF);
		}
		encoder.Code(in, out, -1, -1, null);
	}

	/** Decompresses the given {@link InputStream} into the given {@link OutputStream}.
	 * 
	 * @param in the {@link InputStream} to decompress
	 * @param out the {@link OutputStream} to decompress to
	 * @throws IOException */
	static public void decompress (InputStream in, OutputStream out) throws IOException {
		int propertiesSize = 5;
		byte[] properties = new byte[propertiesSize];
		if (in.read(properties, 0, propertiesSize) != propertiesSize) throw new RuntimeException("input .lzma file is too short");
		com.badlogic.gdx.utils.compression.lzma.Decoder decoder = new com.badlogic.gdx.utils.compression.lzma.Decoder();
		if (!decoder.SetDecoderProperties(properties)) throw new RuntimeException("Incorrect stream properties");
		long outSize = 0;
		for (int i = 0; i < 8; i++) {
			int v = in.read();
			if (v < 0) {
				throw new RuntimeException("Can't read stream size");
			}
			outSize |= ((long)v) << (8 * i);
		}
		if (!decoder.Code(in, out, outSize)) {
			throw new RuntimeException("Error in data stream");
		}
	}
}
