package com.goafanti.crawler.callback;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.alibaba.fastjson.JSON;

public class VacationFutureCallback extends CrawlerFutureCallback {

	private String		year			= null;

	private Pattern		dateTime		= null;

	private Pattern		dateTimePeriod	= null;

	private Pattern		dateTimePeriod2	= null;

	private Set<String>	vacations		= null;

	private Set<String>	workDays		= null;

	public VacationFutureCallback(Map<String, String> result) {
		setResult(result);
	}

	@Override
	protected void handleWebpage(String pageContent) {
		Pattern pattern = Pattern.compile("<td class=\"b12c\">([\\s\\S]*?)</td>", Pattern.MULTILINE);
		Matcher m = pattern.matcher(pageContent);
		if (m.find()) {
			handleNotice(m.group(1));
		}
	}

	private void handleNotice(String notice) {
		Matcher yearMatcher = Pattern.compile("关于(\\d+)年").matcher(notice);
		if (yearMatcher.find()) {
			year = yearMatcher.group(1);
		} else {
			return;
		}
		List<String> parts = new ArrayList<>();
		Matcher linesMatcher = Pattern.compile("<p.*?>([\\s\\S]+?)</p>", Pattern.MULTILINE).matcher(notice);
		while (linesMatcher.find()) {
			parts.add(linesMatcher.group(1));
		}
		handleParts(parts);
	}

	private void handleParts(List<String> parts) {
		dateTime = Pattern.compile("(\\d+)月(\\d+)日");
		dateTimePeriod = Pattern.compile("(\\d+)月(\\d+)日至(\\d+)日");
		dateTimePeriod2 = Pattern.compile("(\\d+)月(\\d+)日至(\\d+)月(\\d+)日");
		vacations = new HashSet<>();
		workDays = new HashSet<>();
		for (String part : parts) {
			for (String s : part.split("<.*?>")) {
				if (s.matches("\\d+月\\d+日.*")) {
					handleLine(s);
				}
			}
		}
		Calendar c = Calendar.getInstance();
		int y = Integer.parseInt(year);
		c.set(y, 0, 1);
		while (c.get(Calendar.YEAR) == y) {
			int dow = c.get(Calendar.DAY_OF_WEEK);
			if (dow == 7 || dow == 1) {
				String ckey = getDateStr(c);
				if (!workDays.contains(ckey)) {
					vacations.add(ckey);
				}
			}
			c.add(Calendar.DATE, 1);
		}
		if (this.getResult() != null) {
			this.getResult().put(year, JSON.toJSONString(vacations));
		}
	}

	private void handleLine(String line) {
		for (String s : line.split("。")) {
			boolean isVacation = s.indexOf("上班") == -1;
			if (findNoPeriod2(s, isVacation) && findNoPeriod(s, isVacation)) {
				Matcher dtm = dateTime.matcher(s);
				while (dtm.find()) {
					if (isVacation) {
						vacations.add(getDateStr(Integer.parseInt(dtm.group(1)), Integer.parseInt(dtm.group(2))));
					} else {
						workDays.add(getDateStr(Integer.parseInt(dtm.group(1)), Integer.parseInt(dtm.group(2))));
					}
				}
			}
		}
	}

	private boolean findNoPeriod2(String s, boolean isVacation) {
		Matcher dtp2m = dateTimePeriod2.matcher(s);
		if (dtp2m.find()) {
			addPeriodToSet(dtp2m.group(1), dtp2m.group(2), dtp2m.group(3), dtp2m.group(4),
					isVacation ? vacations : workDays);
			return false;
		}
		return true;
	}

	private boolean findNoPeriod(String s, boolean isVacation) {
		Matcher dtp2m = dateTimePeriod.matcher(s);
		if (dtp2m.find()) {
			addPeriodToSet(dtp2m.group(1), dtp2m.group(2), dtp2m.group(1), dtp2m.group(3),
					isVacation ? vacations : workDays);
			return false;
		}
		return true;
	}

	private void addPeriodToSet(String stM, String stD, String edM, String edD, Set<String> set) {
		Calendar st = Calendar.getInstance();
		st.set(Integer.parseInt(year), Integer.parseInt(stM) - 1, Integer.parseInt(stD));
		Calendar ed = (Calendar) st.clone();
		ed.set(Integer.parseInt(year), Integer.parseInt(edM) - 1, Integer.parseInt(edD));
		while (ed.after(st)) {
			set.add(getDateStr(st));
			st.add(Calendar.DATE, 1);
		}
		set.add(getDateStr(ed));
	}

	private String getDateStr(Calendar c) {
		return getDateStr(c.get(Calendar.MONTH) + 1, c.get(Calendar.DATE));
	}

	private String getDateStr(int m, int d) {
		StringBuilder sb = new StringBuilder(year);
		if (m < 10) {
			sb.append(0);
		}
		sb.append(m);
		if (d < 10) {
			sb.append(0);
		}
		sb.append(d);
		return sb.toString();
	}

}
