package org.bench4q.agent.test;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;

import org.bench4q.agent.Main;
import org.junit.Test;

import static org.junit.Assert.*;

public class MainTest {
	public MainTest() {
		Main.init();
	}

	@Test
	public void initTest() {
		Main.init();
		assertTrue(new File("configure").exists());
	}

	@Test
	public void getPropertiesTest() throws IOException {
		FileInputStream inputStream = new FileInputStream(new File("configure"
				+ System.getProperty("file.separator")
				+ "agent-config.properties"));
		Properties properties = new Properties();
		properties.load(inputStream);
		assertEquals(6565,
				Integer.parseInt((String) properties.get("servePort")));
		assertEquals(false, Boolean.parseBoolean((String) properties
				.get("isToSaveDetailResult")));
	}

	@Test
	public void test() {
		assertEquals(2, threeSumClosest(new int[] { -1, -4, 1, 2 }, 1));
	}

	public int threeSumClosest(int[] num, int target) {
		int[] arrayAfterSort = sort(num);
		int result = Integer.MAX_VALUE;
		int indexOfJustGEtarget = indexOfJustGEtarget(target, arrayAfterSort,
				-1, -1);
		// LeftSide
		for (int i = indexOfJustGEtarget; i >= 0; i--) {
			result = whichCloser(
					target,
					result,
					towSumClosest(i, arrayAfterSort, target - arrayAfterSort[i])
							+ arrayAfterSort[i]);
		}
		// RightSide
		for (int i = indexOfJustGEtarget + 1; i < arrayAfterSort.length; i++) {
			result = whichCloser(
					target,
					result,
					towSumClosest(i, arrayAfterSort, target - arrayAfterSort[i]));
		}
		return result;
	}

	private int whichCloser(int target, int param1, int param2) {
		return getDistance(target, param1) > getDistance(target, param2) ? param2
				: param1;
	}

	private int getDistance(int target, int param1) {
		return (target - param1) > 0 ? target - param1 : param1 - target;
	}

	private int indexOfJustGEtarget(int target, int[] arrayAfterSort,
			int firstIndexCannotBeUsed, int secondIndexCannotBeUsed) {
		int i = 0;
		for (; i < arrayAfterSort.length; i++) {
			if (i == firstIndexCannotBeUsed || i == secondIndexCannotBeUsed) {
				continue;
			}
			if (arrayAfterSort[i] >= target)
				break;
		}
		if (i == arrayAfterSort.length) {
			for (int j = arrayAfterSort.length - 1; j >= arrayAfterSort.length - 3; j--) {
				if (j == firstIndexCannotBeUsed || j == secondIndexCannotBeUsed) {
					continue;
				}
				return j;
			}
			return -1;
		} else {
			return i;
		}
	}

	// find twoSumClosest from num, target is num[index], and the index can't be
	// used.
	private int towSumClosest(final int firstLoopIndex, int[] arrayAfterSort,
			int realTarget) {
		int result = Integer.MAX_VALUE;
		int indexJustGETarget = indexOfJustGEtarget(realTarget, arrayAfterSort,
				firstLoopIndex, -1);
		// Left side
		for (int i = indexJustGETarget; i >= 0; i--) {
			if (i == firstLoopIndex) {
				continue;
			}
			result = whichCloser(realTarget, result,
					oneClosest(firstLoopIndex, i, arrayAfterSort, realTarget)
							+ arrayAfterSort[i]);
		}
		return result;
	}

	private int oneClosest(int firstLoopIndex, int secondLoopIndex,
			int[] arrayAfterSort, int realTarget) {
		int indexJustGETarget = indexOfJustGEtarget(realTarget, arrayAfterSort,
				firstLoopIndex, secondLoopIndex);
		int indexJustLETarget = -1;
		// Just find Left side one
		for (int i = indexJustGETarget - 1; i >= 0
				&& i >= indexJustGETarget - 3; i--) {
			if (i == firstLoopIndex || i == secondLoopIndex) {
				continue;
			}
			indexJustLETarget = i;
			break;
		}

		if (indexJustLETarget == -1) {
			return arrayAfterSort[indexJustGETarget];
		} else if (indexJustGETarget == arrayAfterSort.length
				|| indexJustGETarget == -1) {
			return arrayAfterSort[indexJustLETarget];
		} else {
			return whichCloser(realTarget, arrayAfterSort[indexJustGETarget],
					arrayAfterSort[indexJustLETarget]);
		}
	}

	private int[] sort(int[] num) {
		for (int j = 1; j <= num.length - 1; j++) {
			for (int i = 0; i < num.length - j; i++) {
				if (num[i] > num[i + 1]) {
					int temp = num[i + 1];
					num[i + 1] = num[i];
					num[i] = temp;
				}
			}
		}
		return num;
	}

	public class TreeNode {
		int val;
		TreeNode left;
		TreeNode right;

		TreeNode(int x) {
			val = x;
		}
	}

	@Test
	public void testRegular() {
		assertTrue(isMatch("aab", "c*a*b"));
		assertTrue(isMatch("aaaa", ".*"));
		assertTrue(isMatch("aa", "a*"));
		assertFalse(isMatch("aaaa", "aa"));
		assertTrue(isMatch("aaa", "a.a"));
		assertTrue(isMatch("aaa", "a*a"));
		assertFalse(isMatch("aaba", "ab*a*c*a"));
		assertTrue(isMatch("", "a*"));
		assertTrue(isMatch("aasdfasdfasdfasdfas", "aasdf.*asdf.*asdf.*asdf.*s"));
		assertFalse(isMatch("", "..*"));
		assertTrue(isMatch("ba", ".*."));
	}

	public boolean isMatch(String s, String p) {
		if (s == null || p == null) {
			return false;
		}
		if (p.length() == 0 && s.length() == 0) {
			return true;
		}

		List<RegularPart> regularParts = getRegularParts(p);
		List<StartPointRange> startPointRanges = buildOneItemList(new StartPointRange(
				0, 0));
		for (int i = 0; i < regularParts.size(); i++) {
			startPointRanges = matchForNextStartRanges(s, startPointRanges,
					regularParts.get(i));
			if (startPointRanges.size() == 0) {
				return false;
			}
		}
		if (startPointRanges.get(startPointRanges.size() - 1).end != s.length()) {
			return false;
		}
		return true;
	}

	private List<StartPointRange> matchForNextStartRanges(String toBeMatched,
			List<StartPointRange> startPointRanges, RegularPart regularPart) {
		List<StartPointRange> resut = new ArrayList<StartPointRange>();
		for (StartPointRange startRange : startPointRanges) {
			resut.addAll(matchForNextStartRange(toBeMatched, startRange,
					regularPart));
		}
		return resut;
	}

	private List<StartPointRange> matchForNextStartRange(String toBeMatched,
			StartPointRange thisRange, RegularPart regularPart) {
		List<StartPointRange> result = new ArrayList<StartPointRange>();
		if (!regularPart.hasAsterisk) {
			if (regularPart.regularContent.equals(".")) {
				if (thisRange.begin > toBeMatched.length() - 1) {
					return new ArrayList<StartPointRange>();
				}
				return buildOneItemList(new StartPointRange(
						thisRange.begin + 1, thisRange.end + 1));
			} else {
				return doForNotAsteriskNotDot(toBeMatched, thisRange,
						regularPart);
			}
		} else {
			if (regularPart.regularContent.equals(".")) {
				return buildOneItemList(new StartPointRange(thisRange.begin,
						toBeMatched.length() - 1));
			} else {
				result.addAll(doForAsteriskNotDot(toBeMatched, thisRange,
						regularPart));
				return result;
			}
		}
	}

	private List<StartPointRange> doForNotAsteriskNotDot(String toBeMatched,
			StartPointRange thisRange, RegularPart regularPart) {
		List<StartPointRange> result = new ArrayList<StartPointRange>();
		for (int i = thisRange.begin; i <= thisRange.end; i++) {
			int endIndex = i + regularPart.regularContent.length();
			if (endIndex > toBeMatched.length()) {
				break;
			}
			String substring = toBeMatched.substring(i, endIndex);
			if (substring.equals(regularPart.regularContent)) {
				result.add(new StartPointRange(i + 1, endIndex));
			}
		}
		return result;
	}

	private List<StartPointRange> doForAsteriskNotDot(String toBeMatched,
			StartPointRange thisRange, RegularPart regularPart) {
		List<StartPointRange> result = new ArrayList<StartPointRange>();
		int begin = -1, end = -1;
		for (int i = thisRange.begin; i < toBeMatched.length(); i++) {
			if (toBeMatched.charAt(i) == regularPart.regularContent.charAt(0)) {
				if (begin == -1 && i <= thisRange.end) {
					begin = i;
				}
			} else {
				if (begin == -1) {
					continue;
				}
				end = i;
				result.add(new StartPointRange(begin, end));
				begin = -1;
				end = -1;
				if (i > thisRange.end) {
					break;
				}
			}
		}
		if (begin > -1 && end == -1) {
			result.add(new StartPointRange(begin, toBeMatched.length()));
		}
		if (result.size() == 0) {
			result.add(thisRange);
		}
		return result;
	}

	private List<StartPointRange> buildOneItemList(
			StartPointRange startPointRange) {
		List<StartPointRange> result = new ArrayList<StartPointRange>();
		result.add(startPointRange);
		return result;
	}

	/**
	 * It do it in a reverse way
	 * 
	 * @param regularExpression
	 * @return
	 */

	private List<RegularPart> getRegularParts(String regularExpression) {
		List<RegularPart> result = new ArrayList<RegularPart>();
		if (regularExpression == null || regularExpression.isEmpty()) {
			return result;
		}
		int index = regularExpression.length() - 1;
		char[] array = regularExpression.toCharArray();
		StringBuffer temp = new StringBuffer();
		for (; index >= 0; index--) {
			if (array[index] == '*') {
				if (index == 0) {
					temp.append(array[index]);
				} else {
					if (temp.length() != 0) {
						result.add(new RegularPart(temp.reverse().toString(),
								false));
						temp.delete(0, temp.length());
					}
					result.add(new RegularPart(new String(
							new char[] { array[index - 1] }), true));
					index--;
				}
			} else if (array[index] == '.') {
				if (temp.length() != 0) {
					result.add(new RegularPart(temp.reverse().toString(), false));
					temp.delete(0, temp.length());
				}
				result.add(new RegularPart(new String(
						new char[] { array[index] }), false));
			} else {
				temp.append(array[index]);
			}
		}
		if (temp.length() > 0) {
			result.add(new RegularPart(temp.reverse().toString(), false));
		}
		Collections.reverse(result);
		return result;
	}

	static class StartPointRange {
		int begin;
		int end;

		StartPointRange() {
			StartPointRange.this.begin = -1;
			StartPointRange.this.end = -1;
		}

		StartPointRange(int begin, int end) {
			StartPointRange.this.begin = begin;
			StartPointRange.this.end = end;
		}
	}

	static class RegularPart {
		public String regularContent;
		public boolean hasAsterisk;

		RegularPart(String content, boolean hasAsterisk) {
			this.regularContent = content;
			this.hasAsterisk = hasAsterisk;
		}
	}
}
