package org.bench4q.agent.test.storage;

import static org.junit.Assert.*;

import java.io.File;
import java.io.IOException;
import java.util.List;

import org.bench4q.agent.storage.Buffer;
import org.bench4q.agent.storage.DoubleBufferStorage;
import org.bench4q.share.helper.TestHelper;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:/org/bench4q/agent/config/application-context.xml" })
public class TestDoubleBufferStorage {
	private DoubleBufferStorage doubleBufferStorage;
	private static String SAVE_DIR = "StorageTest"
			+ System.getProperty("file.separator");
	private static final String INPUT_FILE_PATH = SAVE_DIR + "testInput.txt";
	private static String SAVE_PATH = SAVE_DIR + "test.txt";

	private DoubleBufferStorage getDoubleBufferStorage() {
		return doubleBufferStorage;
	}

	@Autowired
	private void setDoubleBufferStorage(DoubleBufferStorage doubleBufferStorage) {
		this.doubleBufferStorage = doubleBufferStorage;
	}

	public TestDoubleBufferStorage() throws IOException {
		File dir = new File(SAVE_DIR);
		if (!dir.exists()) {
			dir.mkdirs();
		}
		File outPutFile = new File(SAVE_PATH);
		if (!outPutFile.exists()) {
			outPutFile.createNewFile();
		}
		File inputFile = new File(INPUT_FILE_PATH);
		if (!inputFile.exists()) {
			inputFile.createNewFile();
		}
	}

	@After
	public void cleanUp() {
		File file = new File(SAVE_DIR + "test.txt");
		if (file.exists()) {
			file.delete();
		}
		for (int i = 0; i < 4; ++i) {
			String realSavePathString = this.getDoubleBufferStorage()
					.calculateSavePath(SAVE_PATH, i);
			File output = new File(realSavePathString);
			if (output.exists()) {
				output.delete();
			}
		}
	}

	@SuppressWarnings("unchecked")
	@Test
	public void testWriteWithLittleContent() throws Exception {
		this.getDoubleBufferStorage().writeFile("test", SAVE_PATH);
		assertEquals(
				48 * 1024 - 4,
				((Buffer) invokePrivateMethod(this.getDoubleBufferStorage()
						.getClass(), this.getDoubleBufferStorage(),
						"getCurrentBuffer", null, null)).getRemainSize());
		List<Buffer> buffers = ((List<Buffer>) invokePrivateMethod(this
				.getDoubleBufferStorage().getClass(),
				this.getDoubleBufferStorage(), "getBufferList", null, null));
		for (int i = 0; i < buffers.size()
				&& i != (Integer) invokePrivateMethod(this
						.getDoubleBufferStorage().getClass(),
						this.getDoubleBufferStorage(), "getCurrentBufferIndex",
						null, null); i++) {
			assertEquals(48 * 1024, buffers.get(i).getRemainSize());
		}
	}

	@Test
	@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
	public void testWriteWithJustLargerThanOneBufferContent() throws Exception {
		String realSavePathString = this.getDoubleBufferStorage()
				.calculateSavePath(
						SAVE_PATH,
						(Integer) invokePrivateMethod(this
								.getDoubleBufferStorage().getClass(), this
								.getDoubleBufferStorage(),
								"getCurrentBufferIndex", null, null));
		doWriteFile(95);
		while (!this.getDoubleBufferStorage().forTest.isSuccess()) {
			Thread.sleep(1000);
		}
		String readContent = this.getDoubleBufferStorage().readFiles(
				realSavePathString);
		assertTrue(readContent.length() <= 48 * 1024);
		assertTrue(readContent.length() >= 46 * 1024);
		assertTrue(readContent.indexOf("encoding") > 0);
		Buffer currentBuffer = (Buffer) invokePrivateMethod(this
				.getDoubleBufferStorage().getClass(),
				this.getDoubleBufferStorage(), "getCurrentBuffer", null, null);
		System.out.println(currentBuffer.getCurrentPos());
		System.out
				.println((Integer) invokePrivateMethod(this
						.getDoubleBufferStorage().getClass(), this
						.getDoubleBufferStorage(), "getCurrentBufferIndex",
						null, null));
		assertTrue(currentBuffer.getCurrentPos() == 1563);
	}

	private void doWriteFile(int size) {
		String saveContent = this.getDoubleBufferStorage().readFiles(
				INPUT_FILE_PATH);
		for (int i = 0; i < size; i++) {
			this.getDoubleBufferStorage().writeFile(saveContent, SAVE_PATH);
		}
	}

	@Test
	@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
	public void testWriteWithJustLargerThanTwoBufferContent()
			throws InterruptedException {
		doWriteFile(92 * 2 + 2);
		while (!this.getDoubleBufferStorage().forTest.isSuccess()) {
			Thread.sleep(1000);
		}
		assertEquals(47932 * 2, getResultLength());
	}

	@Test
	@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
	public void testWriteContentLargerThanThree() throws InterruptedException {
		doWriteFile(92 * 3 + 2);
		while (!this.getDoubleBufferStorage().forTest.isSuccess()) {
			Thread.sleep(1000);
		}
		int resultLength = getResultLength();
		assertEquals(47932 * 3, resultLength);
	}

	private int getResultLength() {
		int resultLength = 0;
		for (int i = 0; i < 4; i++) {
			String path = this.getDoubleBufferStorage().calculateSavePath(
					SAVE_PATH, i);
			if (!new File(path).exists()) {
				continue;
			}
			String readContentString = this.getDoubleBufferStorage().readFiles(
					path);
			resultLength += readContentString.length();
		}
		return resultLength;
	}

	@Test
	public void testChangeBuffer() throws Exception {
		@SuppressWarnings("unchecked")
		List<Buffer> buffers = (List<Buffer>) invokePrivateMethod(this
				.getDoubleBufferStorage().getClass(),
				this.getDoubleBufferStorage(), "getBufferList", null, null);
		buffers.get(0).setCurrentPos(48 * 1024 - 1220);
		buffers.get(1).setCurrentPos(0);
		invokePrivateMethod(getDoubleBufferStorage().getClass(),
				this.getDoubleBufferStorage(), "setCurrentBufferIndex",
				new Class<?>[] { int.class }, new Object[] { 0 });
		invokePrivateMethod(this.getDoubleBufferStorage().getClass(),
				this.getDoubleBufferStorage(), "changeToTheMaxRemainBuffer",
				null, null);

		assertEquals(1220, buffers.get(0).getRemainSize());
		assertEquals(48 * 1024, buffers.get(1).getRemainSize());
		assertEquals(
				1,
				invokePrivateMethod(this.getDoubleBufferStorage().getClass(),
						this.getDoubleBufferStorage(), "getCurrentBufferIndex",
						null, null));
	}

	private static Object invokePrivateMethod(Class<?> containerClass,
			Object targetObject, String methodName, Class<?>[] paramClasses,
			Object[] params) throws Exception {
		return TestHelper.invokePrivate(targetObject, methodName, paramClasses,
				params);
	}

	@Test
	public void testReadFiles() {
		String content = this.getDoubleBufferStorage().readFiles(
				INPUT_FILE_PATH);
		assertEquals(content.length(), 521);
	}
}
