package cokr.xit.fims;

import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import cokr.xit.foundation.AbstractObject;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;

/**자동차 등록번호 유틸리티
 * @author mjkhan
 */
@Getter
@Setter
@Accessors(chain = true)
public class Vhrno extends AbstractObject {
	public static final String PASSENGER = "passenger";
	public static final String VAN = "van";
	public static final String TRUCK = "truck";
	public static final String SPECIALTY = "specialty";
	public static final String EMERGENCY = "emergency";
	public static final String UNKNOWN = "unknown";

	private static final Pattern
		newPrivate = Pattern.compile("^(\\d{2,3})([가-힣])(\\d{4})$"),
		commercial = Pattern.compile("^([가-힣]{2,3})([가-힣])(\\d{4})$"),
		etc = Pattern.compile("^([가-힣]{2,3})(\\d{2,3})([가-힣])(\\d{4})$");
	private static final List<String> diplomatic = List.of("외교", "영사", "준외", "준영", "국기", "협정", "대표");

	private String
		region,
		type,
		usage,
		number,
		fullNumber;

	/**전체 등록번호를 설정하고, 지역, 차종, 용도, 번호로 구분하여 초기화한다.
	 * @param fullNumber 전체 등록번호
	 * @return 현재 Vhrno
	 */
	public Vhrno setFullNumber(String fullNumber) {
		region = type = usage = number = null;
		this.fullNumber = fullNumber;

		Matcher matcher = newPrivate.matcher(fullNumber);
		if (matcher.matches())
			return setType(matcher.group(1)).setUsage(matcher.group(2)).setNumber(matcher.group(3));
		matcher = commercial.matcher(fullNumber);
		if (matcher.matches())
			return setRegion(matcher.group(1)).setUsage(matcher.group(2)).setNumber(matcher.group(3));
		matcher = etc.matcher(fullNumber);
		if (matcher.matches())
			return setRegion(matcher.group(1)).setType(matcher.group(2)).setUsage(matcher.group(3)).setNumber(matcher.group(4));

		for (String str: diplomatic) {
			if (fullNumber.contains(str)) {
				setUsage(str);
				setNumber(fullNumber.replace(str, "").trim());
				return this;
			}
		}

		return this;
	}

	/**차종을 반환한다.
	 * @return 차종
	 * <ul><li>{@link #PASSENGER} - 승용차</li>
	 *     <li>{@link #VAN} - 승합차</li>
	 *     <li>{@link #TRUCK} - 화물차</li>
	 *     <li>{@link #SPECIALTY} - 특수차</li>
	 *     <li>{@link #EMERGENCY} - 긴급차</li>
	 *     <li>{@link #UNKNOWN} - 알 수 없음</li>
	 * </ul>
	 */
	public String getVehicleType() {
		int num = toInt(type);
		if ((num >= 1 && num <= 69) || (num >= 100 && num <= 699))
			return PASSENGER;
		if ((num >= 70 && num <= 79) || (num >= 700 && num <= 799))
			return VAN;
		if ((num >= 80 && num <= 97) || (num >= 800 && num <= 979))
			return TRUCK;
		if ((num >= 98 && num <= 99) || (num >= 980 && num <= 997))
			return SPECIALTY;
		if (num >= 998 && num <= 999)
			return EMERGENCY;
		return UNKNOWN;
	}

	/**주어진 차종의 대여사업용 차량인지 반환한다.
	 * @param vehicleType 차종
	 * <ul><li>{@link #PASSENGER} - 승용차</li>
	 *     <li>{@link #VAN} - 승합차</li>
	 *     <li>{@link #TRUCK} - 화물차</li>
	 *     <li>{@link #SPECIALTY} - 특수차</li>
	 *     <li>{@link #EMERGENCY} - 긴급차</li>
	 *     <li>{@link #UNKNOWN} - 알 수 없음</li>
	 * </ul>
	 * @return 주어진 차종의 대여사업용 차량 여부
	 */
	public boolean isRental(String vehicleType) {
		if (!equals(getVehicleType(), vehicleType)) return false;

		int num = toInt(type);
		return switch (vehicleType) {
		case PASSENGER -> num >= 100 && num <= 699;
		case VAN -> num >= 700 && num <= 799;
		case TRUCK -> num >= 800 && num <= 979;
		case SPECIALTY -> num >= 980 && num <= 997;
		default-> false;
		};
	}

	/**주어진 차종의 비사업용 차량인지 반환한다.
	 * @param vehicleType 차종
	 * <ul><li>{@link #PASSENGER} - 승용차</li>
	 *     <li>{@link #VAN} - 승합차</li>
	 *     <li>{@link #TRUCK} - 화물차</li>
	 *     <li>{@link #SPECIALTY} - 특수차</li>
	 *     <li>{@link #EMERGENCY} - 긴급차</li>
	 *     <li>{@link #UNKNOWN} - 알 수 없음</li>
	 * </ul>
	 * @return 주어진 차종의 비사업용 차량 여부
	 */
	public boolean isNonCommercial(String vehicleType) {
		return isRental(vehicleType);
	}

	/**주어진 차종의 일반사업용 차량인지 반환한다.
	 * @param vehicleType 차종
	 * <ul><li>{@link #PASSENGER} - 승용차</li>
	 *     <li>{@link #VAN} - 승합차</li>
	 *     <li>{@link #TRUCK} - 화물차</li>
	 *     <li>{@link #SPECIALTY} - 특수차</li>
	 *     <li>{@link #EMERGENCY} - 긴급차</li>
	 *     <li>{@link #UNKNOWN} - 알 수 없음</li>
	 * </ul>
	 * @return 주어진 차종의 일반사업용 차량 여부
	 */
	public boolean isCommercial(String vehicleType) {
		if (!equals(getVehicleType(), vehicleType)) return false;

		int num = toInt(type);
		return switch (vehicleType) {
		case PASSENGER -> num >= 1 && num <= 69;
		case VAN -> num >= 70 && num <= 79;
		case TRUCK -> num >= 80 && num <= 97;
		case SPECIALTY -> num >= 98 && num <= 99;
		default-> false;
		};
	}

	/**용도가 비사업용인지 반환한다.
	 * @return 용도의 비사업용 여부
	 */
	public boolean isNonCommercial() {
		return !isRental()
			&& !isCommercial();
	}

	/**용도가 대여사업용인지 반환한다.
	 * @return 용도의 대여사업용 여부
	 */
	public boolean isRental() {
		return "허하호".contains(usage);
	}

	/**용도가 일반사업용인지(대여사업용 제외) 반환한다.
	 * @return 용도의 일반사업용 여부
	 */
	public boolean isCommercial() {
		return "바사아자배".contains(usage);
	}

	/**외교차량 여부를 반환한다.
	 * @return 외교차량 여부
	 */
	public boolean isDiplomatic() {
		return diplomatic.contains(usage);
	}

	public String describe() {
		return String.format("%s => region: %s, type: %s, usage: %s, number: %s", fullNumber, region, type, usage, number);
	}

	@Override
	public String toString() {
		return fullNumber;
	}
/*
	public static void main(String[] args) {
		Vhrno vhrno = new Vhrno();
		String str = "25오1778";
		System.out.println(vhrno.setFullNumber(str).describe() + ", target: " + (Vhrno.VAN.equals(vhrno.getVehicleType()) && vhrno.isCommercial()));
		System.out.println(vhrno.setFullNumber(str = "123가4567").describe() + ", target: " + (Vhrno.VAN.equals(vhrno.getVehicleType()) && vhrno.isCommercial()));
		System.out.println(vhrno.setFullNumber(str = "서울바9867").describe() + ", target: " + (Vhrno.VAN.equals(vhrno.getVehicleType()) && vhrno.isCommercial()));
		System.out.println(vhrno.setFullNumber(str = "경기32더5678").describe() + ", target: " + (Vhrno.VAN.equals(vhrno.getVehicleType()) && vhrno.isCommercial()));
		System.out.println(vhrno.setFullNumber(str = "외교001-001").describe() + ", target: " + (Vhrno.VAN.equals(vhrno.getVehicleType()) && vhrno.isCommercial()));
		System.out.println(vhrno.setFullNumber(str = "71버3581").describe() + ", target: " + (Vhrno.VAN.equals(vhrno.getVehicleType()) && vhrno.isCommercial()));
		System.out.println(vhrno.setFullNumber(str = "72수4545").describe() + ", target: " + (Vhrno.VAN.equals(vhrno.getVehicleType()) && vhrno.isCommercial()));
		System.out.println(vhrno.setFullNumber(str = "경기70사7668").describe() + ", target: " + (Vhrno.VAN.equals(vhrno.getVehicleType()) && vhrno.isCommercial()));
		System.out.println(vhrno.setFullNumber(str = "경기70사3015").describe() + ", target: " + (Vhrno.VAN.equals(vhrno.getVehicleType()) && vhrno.isCommercial()));
		System.out.println(vhrno.setFullNumber(str = "경기76아9252").describe() + ", target: " + (Vhrno.VAN.equals(vhrno.getVehicleType()) && vhrno.isCommercial()));
		System.out.println(vhrno.setFullNumber(str = "서울70아1477").describe() + ", target: " + (Vhrno.VAN.equals(vhrno.getVehicleType()) && vhrno.isCommercial()));
	}
*/
}