package cokr.xit.fims.cvlc.service.bean;

import java.net.http.HttpResponse;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import cokr.xit.base.file.service.FileQuery;
import cokr.xit.base.file.service.bean.FileBean;
import cokr.xit.fims.FimsConf;
import cokr.xit.fims.cmmn.CmmnUtil;
import cokr.xit.fims.cmmn.CrdnSttsHstry;
import cokr.xit.fims.cmmn.dao.CrdnSttsHstryMapper;
import cokr.xit.fims.cmmn.service.bean.CrdnSttsHstryBean;
import cokr.xit.fims.crdn.Crdn;
import cokr.xit.fims.crdn.CrdnQuery;
import cokr.xit.fims.crdn.dao.CrdnInfoMapper;
import cokr.xit.fims.crdn.dao.CrdnInstMapper;
import cokr.xit.fims.crdn.dao.CrdnUpdtMapper;
import cokr.xit.fims.crdn.service.bean.CrdnStngBean;
import cokr.xit.fims.cvlc.CrdnCvlcpt;
import cokr.xit.fims.cvlc.dao.AnswerWordsMapper;
import cokr.xit.fims.cvlc.dao.CrdnCvlcptMapper;
import cokr.xit.fims.cvlc.service.CrdnCvlcptService;
import cokr.xit.fims.excl.dao.LevyExclMapper;
import cokr.xit.fims.excl.service.bean.LevyExclBean;
import cokr.xit.fims.sprt.Sprt;
import cokr.xit.fims.sprt.service.bean.MediaBean;
import cokr.xit.foundation.component.AbstractServiceBean;
import cokr.xit.foundation.data.DataObject;
import cokr.xit.foundation.web.WebClient;
import cokr.xit.interfaces.disabledparking.service.bean.DisabledParkingBean;

@Service("crdnCvlcptService")
public class CrdnCvlcptServiceBean extends AbstractServiceBean implements CrdnCvlcptService {
	@Resource(name="objectMapper")
	private ObjectMapper objectMapper;

	@Resource(name="fileBean")
	private FileBean fileBean;

	@Resource(name="mediaBean")
	private MediaBean mediaBean;

	@Resource(name = "disabledParkingBean")
	private DisabledParkingBean disabledParkingBean;

	@Resource(name="crdnCvlcptMainBean")
	protected CrdnCvlcptMainBean crdnCvlcptMainBean;

	@Resource(name="crdnCvlcptSubBean")
	protected CrdnCvlcptSubBean crdnCvlcptSubBean;

	@Resource(name="crdnStngBean")
	private CrdnStngBean crdnStngBean;

	@Resource(name = "levyExclBean")
	protected LevyExclBean levyExclBean;

	@Resource(name="levyExclMapper")
	private LevyExclMapper levyExclMapper;

	@Resource(name = "answerWordsMapper")
	private AnswerWordsMapper answerWordsMapper;

	@Resource(name = "crdnCvlcptMapper")
	private CrdnCvlcptMapper crdnCvlcptMapper;

	@Resource(name = "crdnSttsHstryMapper")
	private CrdnSttsHstryMapper crdnSttsHstryMapper;

	@Resource(name = "crdnSttsHstryBean")
	private CrdnSttsHstryBean crdnSttsHstryBean;

	@Resource(name="crdnInfoMapper")
	private CrdnInfoMapper crdnInfoMapper;

	@Resource(name="crdnInstMapper")
	protected CrdnInstMapper crdnInstMapper;

	@Resource(name="crdnUpdtMapper")
	protected CrdnUpdtMapper crdnUpdtMapper;

	@Override
	public List<DataObject> getCivilComplaintList(CrdnQuery query) {
        return switch (blankIfEmpty(query.getGridType())) {
	        case "receiptData" -> {
	        	String appendCopyToOriginal = "CVLCPT_RCPT_YMD, CVLCPT_RCPT_NO, CVLCPT_LIST_NO, CVLCPT_LINK_ID, CRDN_REG_SE_CD, CRDN_ID";

	        	if (query.getOrderBy() == null) {
	    			if (isEmpty(query.getBy())) {
	    				query.setOrderBy(appendCopyToOriginal);
	    			} else {
	    				query.setOrderBy(CmmnUtil.convertCamelCaseToSnakeCase(query.getBy()+","+appendCopyToOriginal));
	    			}
	    		}
	        	yield crdnCvlcptMapper.selectCivilComplaintReceiptDataList(query);
	        }
	        case "answerTargetData" -> {
	        	if (query.getOrderBy() == null) {
	    			if (query.getBy() == null) {
	    				query.setOrderBy("CRDN_ID");
	    			} else {
	    				query.setOrderBy(CmmnUtil.convertCamelCaseToSnakeCase(query.getBy()));
	    			}
	    		}
	        	yield crdnCvlcptMapper.selectCivilComplaintAnswerTargetDataList(query);
	        }
	        default -> {
	        	if (query.getOrderBy() == null) {
	    			if (query.getBy() == null) {
	    				query.setOrderBy("CRDN_ID");
	    			} else {
	    				query.setOrderBy(CmmnUtil.convertCamelCaseToSnakeCase(query.getBy()));
	    			}
	    		}
	        	yield crdnCvlcptMapper.selectCivilComplaintList(query);
	        }
	    };
	}

	@Override
	public DataObject getCivilComplaintOriginalInfo(CrdnQuery query) {
		return crdnCvlcptMainBean.getCivilComplaintOriginalInfo(query);
	}

	@Override
	public DataObject getAnswerPreview(CrdnQuery query) {
		DataObject dataObject = crdnCvlcptMapper.selectAnswerPreview(query.getCrdnId());

		//저장된 답변이 없으면
		if (isEmpty(dataObject.get("CVLCPT_PRCS_RSLT_CN"))) {
			String resultWords = crdnCvlcptMainBean.mappingAnswerWords(query.getCrdnId(), query.getSggCd(), query.getCurrentUserAcnt());
			dataObject.set("CVLCPT_PRCS_RSLT_CN", resultWords);
		}

		return dataObject;
	}

	@Override
	public DataObject getCivilComplaintReceiptInitialDataInfo(CrdnQuery query) {
		//민원조회
		DataObject dataObject = crdnCvlcptMapper.selectCivilComplaintReceiptInitialDataInfo(query);

		//첨부파일 조회
		FileQuery fileQuery = new FileQuery()
			.setInfoType(Crdn.INF_TYPE)
			.setInfoKeys(query.getCrdnId());
		List<DataObject> fileList = mediaBean.getFileAndMosaic(fileQuery);
		if (!isEmpty(fileList))
			dataObject.set("fileList", fileList);

		//동영상 존재 여부 확인
		dataObject.set("VIDEO_EXIST_YN", "N");
		String linkTblNm = dataObject.string("LINK_TBL_NM");
		if (!isEmpty(linkTblNm)) {
			if (linkTblNm.equals("TB_ESB_INTERFACE")) {
				fileQuery.setInfoType("010");
			} else if (linkTblNm.equals("TB_SAEOL_CVLCPT_DTL")) {
				fileQuery.setInfoType("020");
			} else
				throw new RuntimeException("파일 조회 중 오류가 발생하였습니다.");

			fileQuery.setInfoKeys(dataObject.string("LINK_ID"));
			List<DataObject> linkFileList = fileBean.getFileList(fileQuery);
			if (!isEmpty(linkFileList)) {
				for(int i=0; i < linkFileList.size(); i++) {
					String mimeType = linkFileList.get(i).string("MIME_TYPE");
					if (Arrays.asList(Sprt.VIDEO_MIME_TYPE).contains(mimeType)) {
						dataObject.set("VIDEO_EXIST_YN", "Y");
					}
				}
			}
		}

		return dataObject;
	}

	@Override
	public String sendBackCivilComplaintInfo(String crdnId) {
		boolean saved = crdnCvlcptSubBean.executelevyExcl("sendBack", crdnId);
		if (!saved)
			throw new RuntimeException("반송 처리 중 서손처리에 실패하였습니다.");

		DataObject crdnInfo = crdnInfoMapper.selectCrdnInfo(crdnId);
		boolean isFirstBorn = crdnInfo.string("CRDN_REG_SE_CD").equals("02");
		boolean isNotYetTransmit = !Arrays.asList(new String[]{"04","05","06"}).contains(crdnInfo.string("CVLCPT_TRSM_CD"));

		//자동등록된 연계자료이며, 전송 작업 처리 전 자료에 대해 민원 정보를 업데이트한다.
		if (isFirstBorn && isNotYetTransmit) {
			CrdnCvlcpt crdnCvlcpt = new CrdnCvlcpt();
			crdnCvlcpt.setCvlcptLinkId(crdnInfo.string("CVLCPT_LINK_ID"));
			crdnCvlcpt.setCvlcptPrcsCd("01");

			boolean retSuccess = crdnCvlcptMapper.updateCvlcptProcessSectionForSendBack(crdnCvlcpt) == 1;
			if (!retSuccess)
				throw new RuntimeException("반송 처리 중 단속민원답변 수정에 실패하였습니다.");

			if (crdnInfo.string("LINK_TBL_NM").equals("TB_ESB_INTERFACE")
					|| crdnInfo.string("LINK_TBL_NM").equals("TB_SAEOL_CVLCPT_DTL")) {
				boolean result = crdnCvlcptMainBean.insertTransmitInfo(true, crdnInfo.string("CVLCPT_LINK_ID"));
				if (!result)
					throw new RuntimeException("반송 처리 중 전송자료 등록에 실패하였습니다.");
			}
		}

		return saved ? "[S]" : "[F]";
	}

	@Override
	public String updateCvlcptProcessSection(Map<String, Object> nonQueryRequest, CrdnCvlcpt crdnCvlcpt, Crdn crdn) {
		boolean saved = crdnCvlcptSubBean.updateSubInfo(nonQueryRequest, crdn);
		if (!saved)
			throw new RuntimeException("민원자료 저장 중 오류가 발생하였습니다.");

		DataObject crdnInfo = crdnInfoMapper.selectCrdnInfo(crdn.getCrdnId());
		crdnCvlcpt.setCvlcptLinkId(crdnInfo.string("CVLCPT_LINK_ID"));

		boolean isFirstBorn = crdnInfo.string("CRDN_REG_SE_CD").equals("02");
		boolean isNotYetTransmit = !Arrays.asList(new String[]{"04","05","06"}).contains(crdnInfo.string("CVLCPT_TRSM_CD"));

		//자동등록된 연계자료이며, 전송 작업 처리 전 자료에 대해 민원 정보를 업데이트한다.
		if (isFirstBorn && isNotYetTransmit) {
			boolean retSuccess = crdnCvlcptMainBean.initCvlcptPrcsRsltCn(crdnCvlcpt.getCvlcptLinkId());
			if (!retSuccess)
				throw new RuntimeException("민원자료 저장 중 단속민원 대장의 민원처리결과내용 초기화에 실패하였습니다.");

			String newCvlcptPrcsCd = "";
			String newCvlcptTrsmCd = "";
			String ansRsnCd = "";
			String acceptType = blankIfEmpty(nonQueryRequest.get("acceptType"));
			if (acceptType.equals("someAccept") || acceptType.equals("nonAccept")) { //일부수용,불수용
				ansRsnCd = crdn.getLevyExclRsnCd();
			} else {
				ansRsnCd = "3"+crdn.getVltnCd();
			}

			CrdnQuery crdnQuery = new CrdnQuery()
				.setAnsRsnCd(ansRsnCd)
				.setSggCd(crdn.getSggCd())
				.setTaskSeCd(crdn.getTaskSeCd());
			if (crdn.getVltnCd().equals("01") && "Y".equals(crdnCvlcpt.getDstrbncYn())) {
				crdnQuery.setDstrbncYn("Y");
			}

			// 민원 답변 문구(TB_CVLCPT_ANS_WORDS) 대장 조회
			DataObject infoCvlcptAnsWords = answerWordsMapper.selectCvlcptAnswerWordsInfo(crdnQuery);
			if (acceptType.equals("someAccept") || acceptType.equals("nonAccept")) { //일부수용,불수용
				// 비부과(서손)사유가 민원 답변대상인가? 민원 답변 문구(TB_CVLCPT_ANS_WORDS) 대장에 답변 사유 코드가 존재한다면..
				if (infoCvlcptAnsWords != null) {
					newCvlcptPrcsCd = "04";									// 민원 처리 코드 - 불수용(과태료 부과제외)
					newCvlcptTrsmCd = "01";									// 민원 전송 코드 - 전송 대상
				} else  {
					newCvlcptPrcsCd = "07";									// 민원 처리 코드 - 비부과(서손)사유가 답변 미대상
					newCvlcptTrsmCd = "03";									// 민원 전송 코드 - 비부과(서손)사유가 전송 미대상
				}
			} else { //수용일 때
				newCvlcptPrcsCd = "02";									// 민원 처리 코드 - 수용
				newCvlcptTrsmCd = "01";									// 민원 전송 코드 - 전송 대상
			}

	        crdnCvlcpt.setCvlcptPrcsCd(newCvlcptPrcsCd);									// 민원 처리 코드
	        crdnCvlcpt.setCvlcptPrcsSmry(switch (acceptType) { // 민원 처리 요약
	        case "someAccept" -> "과태료 계고";
	        case "accept" -> "과태료부과";
	        case "nonAccept" -> "과태료 미부과";
	        default -> null;
	        });

	        crdnCvlcpt.setCvlcptTrsmCd(newCvlcptTrsmCd);									// 민원 전송 코드
			//민원처리정보 변경
			retSuccess = crdnCvlcptMainBean.updateCvlcptPrcsSe(true, crdnCvlcpt);
			if (!retSuccess)
				throw new RuntimeException("민원자료 저장 중 단속민원답변 수정에 실패하였습니다.");
		}

		return saved ? "[S]" : "[F]";
	}

	@Override
	public String copyCvlcptCrdnInfo(String crdnId) {
		return crdnCvlcptSubBean.copyCvlcptCrdnInfo(crdnId) ? "[S]" : "[F]";
	}

	@Override
	public String updateAnswerContent(CrdnCvlcpt crdnCvlcpt) {

		int affected = crdnCvlcptMapper.updateAnswerContent(crdnCvlcpt);
		if (affected != 1)
			throw new RuntimeException("답변 내용 저장에 실패하였습니다.");

		return "[S]";

	}

	@Override
	public String updateAnswerTargetProcessStatus(String processType, String currentSggCd, String currentUserAcnt, String[] crdnIds) {
		List<DataObject> targetDataList = crdnCvlcptMapper.selectAnswerTargetList(crdnIds);

		for(int i=0; i < targetDataList.size(); i++) {
			//단속상태 변경(차적조회완료 -> 부과대상)
			DataObject targetInfo = targetDataList.get(i);
			if (targetInfo.string("CRDN_STTS_CD").equals("21")) {
				CrdnSttsHstry crdnSttsHstry = new CrdnSttsHstry();
				crdnSttsHstry.setCrdnId(targetInfo.string("CRDN_ID"));
				crdnSttsHstry.setBfrSttsCd(targetInfo.string("CRDN_STTS_CD"));
				crdnSttsHstry.setBfrSttsChgDt(targetInfo.string("CRDN_STTS_CHG_DT"));
				crdnSttsHstry.setCrdnSttsCd("21");
				crdnSttsHstry.setTaskDtlId(targetInfo.string("CRDN_ID"));
				crdnSttsHstry.setEtcCn("민원답변등록");
				boolean retSuccess = crdnSttsHstryBean.createHstryUpdateCrdnSttsCd(crdnSttsHstry);
				if (!retSuccess) {
					throw new RuntimeException("민원 전송 상태 변경 중 단속상태코드 변경에 실패하였습니다.");
				}
			}

			CrdnCvlcpt crdnCvlcpt = new CrdnCvlcpt();
			crdnCvlcpt.setCvlcptLinkId(targetInfo.string("CVLCPT_LINK_ID"));
			if (processType.equals("answerExecute")) {
				crdnCvlcpt.setCvlcptTrsmCd("04");
				if (ifEmpty(targetInfo.string("CVLCPT_PRCS_RSLT_CN"), () -> "").equals("")) {
					String answerWords = crdnCvlcptMainBean.mappingAnswerWords(targetInfo.string("CRDN_ID"), currentSggCd, currentUserAcnt);
					crdnCvlcpt.setCvlcptPrcsRsltCn(answerWords);
				}
			} else if (processType.equals("answerComplete")){
				crdnCvlcpt.setCvlcptTrsmCd("09");
			} else
				throw new RuntimeException("민원 전송 상태 변경에 실패하였습니다.");

			int affected = crdnCvlcptMapper.updateAnswerTargetTransmitStatus(crdnCvlcpt);
			if (affected != 1)
				throw new RuntimeException("민원 전송 상태 변경 중 오류가 발생하였습니다.");

			if (processType.equals("answerExecute")) {
				if (targetInfo.string("LINK_TBL_NM").equals("TB_ESB_INTERFACE")
						|| targetInfo.string("LINK_TBL_NM").equals("TB_SAEOL_CVLCPT_DTL")) {
					boolean result = crdnCvlcptMainBean.insertTransmitInfo(false, targetInfo.string("CVLCPT_LINK_ID"));
					if (!result)
						throw new RuntimeException("민원 전송 상태 변경 중 전송자료 등록에 실패하였습니다.");
				}

			}
		}

		return "[S]";
	}

	@Override
	public Map<String, Object> getCvlcptAnswerWordsInfo(CrdnQuery crdnQuery) {
		Map<String,Object> wordsStngInfo = new HashMap<String, Object>();

		DataObject basic = crdnCvlcptMainBean.selectCvlcptAnswerBasicWordsInfo(crdnQuery);
		List<DataObject> levyCaseList = crdnCvlcptMainBean.selectAnswerBodyWordsList(crdnQuery,"3");
		List<DataObject> warningCaseList = crdnCvlcptMainBean.selectAnswerBodyWordsList(crdnQuery,"2");
		List<DataObject> nonlevyCaseList = crdnCvlcptMainBean.selectAnswerBodyWordsList(crdnQuery,"1");

		wordsStngInfo.put("basic", basic);
		wordsStngInfo.put("levyCaseList", levyCaseList);
		wordsStngInfo.put("warningCaseList", warningCaseList);
		wordsStngInfo.put("nonlevyCaseList", nonlevyCaseList);

		return wordsStngInfo;
	}

	@Override
	public String saveAnswerWordsInfo(String saveData, CrdnCvlcpt crdnCvlcpt) {
		boolean saved = false;
		if (saveData.equals("basic")) {
			saved = crdnCvlcptMainBean.saveAnswerBasicWordsInfo(crdnCvlcpt);
		}
		if (saveData.equals("body")) {
			saved = crdnCvlcptMainBean.saveAnswerBodyWordsInfo(crdnCvlcpt);
		}
		return saved ? "[S]" : "[F]";
	}

	@Override
	public String removeCvlcptAnswerWordsInfo(String ansWordsId) {
		CrdnCvlcpt crdnCvlcpt = new CrdnCvlcpt();
		crdnCvlcpt.setAnsWordsId(ansWordsId);
		return answerWordsMapper.deleteCvlcptAnswerWordsInfo(crdnCvlcpt) == 1 ? "[S]" : "[F]";
	}

	@Override
	public List<DataObject> getWarningWordsList(CrdnQuery crdnQuery) {
		return crdnCvlcptSubBean.selectWarningWordsList(crdnQuery);
	}

	@Override
	public String saveWarningWordsInfo(CrdnCvlcpt crdnCvlcpt) {
		return crdnCvlcptSubBean.saveWarningWordsInfo(crdnCvlcpt) ? "[S]" : "[F]";
	}

	@Override
	public List<DataObject> getDmndDataList(CrdnQuery query) {
		return crdnCvlcptMainBean.selectDmndDataList(query);
	}

	@Override
	public String createDmndDataInfo(CrdnCvlcpt crdnCvlcpt) {
		return crdnCvlcptMainBean.createDmndDataInfo(crdnCvlcpt) ? "[S]" : "[F]";
	}

	@Override
	public String removeDmndDataInfo(String... indivIds) {
		return crdnCvlcptMainBean.removeDmndDataInfo(indivIds) ? "[S]" : "[F]";
	}

	@Override
	public String executeDisabledTagInfoInquire(String... crdnIds) {
		String disabledParkingDemonYn = FimsConf.get().getDisabledParkingDemonYn();
		String disabledParkingDemonUrl = FimsConf.get().getDisabledParkingDemonUrl();
		if (isEmpty(disabledParkingDemonYn)) {
			disabledParkingDemonYn = "N";
		}
		if (isEmpty(disabledParkingDemonUrl)) {
			disabledParkingDemonUrl = "";
		}

		HashMap<String, Boolean> vehicleResultMap = new HashMap<String, Boolean>();
		int i=0;
		for(; i<crdnIds.length; i++){
			DataObject crdnInfo = crdnInfoMapper.selectCrdnInfo(crdnIds[i]);
			String vhrno = crdnInfo.string("VHRNO");

			if (isEmpty(vhrno))
				continue;

			if (!vehicleResultMap.containsKey(vhrno)) {
				DataObject inquireResult = new DataObject();

				// 연계서비스 별도 프로세스 여부
				if (disabledParkingDemonYn.equals("Y")) {
					String sUrl = disabledParkingDemonUrl + "/intf/disabledParking/parkingInfo";

					HttpResponse<String> apiRslt = new WebClient().post(request -> {
			            request.contentType(WebClient.Request.ContentType.JSON);
			            request.uri(sUrl);
			            request.data("vehicleNo", crdnInfo.string("VHRNO"));
			        });
			        String rslt = apiRslt.body();
			        try {
						inquireResult = objectMapper.readValue(rslt, new TypeReference<DataObject>() {});
					} catch (JsonMappingException e) {
						e.printStackTrace();
					} catch (JsonProcessingException e) {
						e.printStackTrace();
					}
				} else {
					inquireResult = disabledParkingBean.getParkingInfo(crdnInfo.string("VHRNO"));
				}

				String parkingPsblYn = inquireResult.string("PARKING_PSBL_YN");
				String bohunParkingPsblYn = inquireResult.string("BOHUN_PARKING_PSBL_YN");

				boolean resulstYn = parkingPsblYn.equals("Y") || bohunParkingPsblYn.equals("Y");
				vehicleResultMap.put(vhrno, resulstYn);
			}

			boolean result = vehicleResultMap.get(vhrno);
			if (result) {
				boolean saved = crdnCvlcptSubBean.executelevyExcl("inquireTag", crdnInfo.string("CRDN_ID"));
				if (!saved)
					throw new RuntimeException("서손처리에 실패하였습니다.");

				boolean isFirstBorn = crdnInfo.string("CRDN_REG_SE_CD").equals("02");
				boolean isNotYetTransmit = !"04,05,06".contains(crdnInfo.string("CVLCPT_TRSM_CD"));

				//자동등록된 연계자료이며, 전송 작업 처리 전 자료에 대해 민원 정보를 업데이트한다.
				if (isFirstBorn && isNotYetTransmit) {
					CrdnCvlcpt crdnCvlcpt = new CrdnCvlcpt();
					crdnCvlcpt.setCvlcptLinkId(crdnInfo.string("CVLCPT_LINK_ID"));
					crdnCvlcpt.setCvlcptPrcsCd("04");
					crdnCvlcpt.setCvlcptPrcsSmry("과태료 미부과");

					CrdnQuery crdnQuery = new CrdnQuery();
					crdnQuery.setSggCd(crdnInfo.string("SGG_CD"));
					crdnQuery.setTaskSeCd("DPV");
					crdnQuery.setAnsRsnCd("103");

					// 민원 답변 문구(TB_CVLCPT_ANS_WORDS) 대장 조회
					DataObject infoCvlcptAnsWords = answerWordsMapper.selectCvlcptAnswerWordsInfo(crdnQuery);
					crdnCvlcpt.setCvlcptTrsmCd(infoCvlcptAnsWords == null ? "03" : "01");

					boolean retSuccess = crdnCvlcptMainBean.updateCvlcptPrcsSe(false, crdnCvlcpt);
					if (!retSuccess)
						throw new RuntimeException("서손처리 중 단속민원답변 상태 수정에 실패하였습니다.");
				}
			} else {
				Crdn crdn = new Crdn();
				crdn.setCrdnId(crdnInfo.string("CRDN_ID"));
				boolean saved = crdnUpdtMapper.updateTagInfo(crdn) == 1;
				if (!saved)
					throw new RuntimeException("표지조회처리 중 표지확인여부 변경에 실패하였습니다.");
			}
		}

		return i > 0 ? "[S]" : "[F]";
	}
}