package sgc.portal.com.com.service;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import sgc.portal.base.util.EasyHashMap;
import sgc.portal.com.com.model.Weather;
import sgc.portal.com.com.persistence.ComWeatherMapper;

@Service
public class ComWeatherService {
	
    private final ComWeatherMapper mapper;

    public ComWeatherService(ComWeatherMapper mapper) {
        this.mapper = mapper;
    }
	
	/* 위경도 -> 단기예보 지점 좌표(X,Y) 전환 변수 설정 */
    double RE = 6371.00877; // 지구 반경(km)
    double GRID = 5.0; // 격자 간격(km)
    double SLAT1 = 30.0; // 투영 위도1(degree)
    double SLAT2 = 60.0; // 투영 위도2(degree)
    double OLON = 126.0; // 기준점 경도(degree)
    double OLAT = 38.0; // 기준점 위도(degree)
    double XO = 43; // 기준점 X좌표(GRID)
    double YO = 136; // 기1준점 Y좌표(GRID)
    
    int gridX = 0;
    int gridY = 0;
	/*
	 * 단기예보 조회 LIST
	 */
	public List<EasyHashMap<String, Object>> getWeatherVeryShort(HashMap<String, String> paramMap) {

		EasyHashMap<String, String> getKey = new EasyHashMap<String, String>();
		Weather weather = new Weather();
		getKey = mapper.getWeatherKey(weather);
		String ServiceKey = getKey.get("apiKey");
		//String ServiceKey = "G2dqdjyZGNbzUgvcO7Hrfy1l5xeWnwSpXIJnNZxU1mmkWM4u%2B80p181biL6hYPAjeGkxmGHqgQtL61XAF%2BL92Q%3D%3D"; // 기상정 API 개인키(SDCIT 표성빈 대리)
		String baseDate = paramMap.get("baseDate"); // 조회기준일자
		String tomorrowDate = paramMap.get("tomorrowDate"); // 익일
		String baseTime = paramMap.get("baseTime"); // 조회기준시간(필요항목 발표시각인 02:00으로 고정)
		String fcstTime = paramMap.get("fcstTime");
		
		// GPS 위도, 경도 -> 기상청 행정구역 예보좌표로 변경
		getGridxy( Double.parseDouble(paramMap.get("lat")), Double.parseDouble(paramMap.get("lng")) );
		
		System.out.println("계산된 gridX 좌표 : " + gridX);
		System.out.println("계산된 gridY 좌표 : " + gridY);
		
		List<EasyHashMap<String, Object>> result = new ArrayList<EasyHashMap<String, Object>>();
		EasyHashMap<String, Object> weatherToday = new EasyHashMap<String, Object>();
		EasyHashMap<String, Object> weatherTomorrow = new EasyHashMap<String, Object>();
		
		weatherToday.put("fcstDate", baseDate);
		weatherTomorrow.put("fcstDate", tomorrowDate);	
		
		//http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getVilageFcst?pageNo=1&numOfRows=500&dataType=XML&base_date=20230905&base_time=0200&nx=114&ny=43&ServiceKey=IE5qJGgXeA5V2FFzMJzHltQ2SyEYvb7zduypvkFhn5u%2BsaJtbWjE1k%2F9qPzj3u2EJ1hpw6X6OD75psUrPPItag%3D%3D
		
		try {
			// URL 객체를 생성하고 웹 주소를 넣습니다.
			URL url = new URL("http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getVilageFcst?"
					+ "pageNo=1&numOfRows=500&dataType=XML&base_date=" + baseDate + "&base_time=" + baseTime + ""
					+ "&nx=" + gridY + "&ny=" + gridX + "&ServiceKey=" + ServiceKey);

			System.out.println(url);

			// HttpURLConnection 객체를 생성합니다.
			HttpURLConnection con = (HttpURLConnection) url.openConnection();
			// 요청 방식을 GET으로 설정
			con.setRequestMethod("GET");
			// BufferedReader 객체를 생성하고 HttpURLConnection의 InputStream을 넣습니다.
			BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
			
			String inputLine;
			StringBuffer xmlContent = new StringBuffer();
			
			// 서버로부터 받은 데이터를 줄단위로 읽습니다.
			while ((inputLine = in.readLine()) != null) {
				xmlContent.append(inputLine);
			}
			// BufferedReader를 닫습니다.
			in.close();
			// HttpURLConnection을 닫습니다.
			con.disconnect();

			// Stringbuffer로 읽은 데이터를 XML 형식으로 변경 
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = factory.newDocumentBuilder();
			Document document = builder.parse(new InputSource(new StringReader(xmlContent.toString())));

			/* 응답메시지 종류
			 * category : PTY(강수형태), SKY(하늘상태), TMX(일 최고기온), TMN(일 최저기온)
			 * fcstValue : 각 category 데이터 값
			 * fcstTime : 예측시간(06:00 고정)
			 * fcstDate : 예측일자
			 */
			NodeList categoryList = document.getElementsByTagName("category");
			NodeList fcstValueList = document.getElementsByTagName("fcstValue");
			NodeList fcstTimeList = document.getElementsByTagName("fcstTime");
			NodeList fcstDateList = document.getElementsByTagName("fcstDate");

			/* category 응답메시지 만큼 roop */
			for (int i = 0; i < categoryList.getLength(); i++) {

				/* 각 응답메시지의 값 변수 설정 */
				Node categoryNode = categoryList.item(i);
				Node categorNodeVal = categoryNode.getChildNodes().item(0);

				Node fcstValNode = fcstValueList.item(i);
				Node fcstValueNodeVal = fcstValNode.getChildNodes().item(0);
				
				Node fcstTimeNode = fcstTimeList.item(i);
				Node fcstTimeNodeVal = fcstTimeNode.getChildNodes().item(0);
				
				Node fcstDateNode = fcstDateList.item(i);
				Node fcstDateNodeVal = fcstDateNode.getChildNodes().item(0);

				// 3일의 데이터 중 당일 데이터만 add
				if ( fcstDateNodeVal.getNodeValue().equals(baseDate) ) {
					
					if (categorNodeVal.getNodeValue().equals("TMX") || categorNodeVal.getNodeValue().equals("TMN") ) {
						
						weatherToday.put(categorNodeVal.getNodeValue(), fcstValueNodeVal.getNodeValue());
						
					} else if ( fcstTimeNodeVal.getNodeValue().equals(fcstTime) ) {

						if (categorNodeVal.getNodeValue().equals("SKY") || categorNodeVal.getNodeValue().equals("PTY")) {
							
							weatherToday.put(categorNodeVal.getNodeValue(), fcstValueNodeVal.getNodeValue());
						}

					}
				}
				
				// 3일의 데이터 중 익일 데이터만 add
				if ( fcstDateNodeVal.getNodeValue().equals(tomorrowDate) ) {
					
					if (categorNodeVal.getNodeValue().equals("TMX") || categorNodeVal.getNodeValue().equals("TMN") ) {
						
						weatherTomorrow.put(categorNodeVal.getNodeValue(), fcstValueNodeVal.getNodeValue());
					
					} else if ( fcstTimeNodeVal.getNodeValue().equals(fcstTime) ) {

						if (categorNodeVal.getNodeValue().equals("SKY") || categorNodeVal.getNodeValue().equals("PTY")) {
							
							weatherTomorrow.put(categorNodeVal.getNodeValue(), fcstValueNodeVal.getNodeValue());
						}

					}
				}
			}

			result.add(weatherToday);
			result.add(weatherTomorrow);

		} catch (Exception e) {
			// 예외 발생시 스택트레이스를 출력합니다.
			e.printStackTrace();
		}

		return result;
	}
	
	// 23.08.24 SDCIT 표성빈 대리
	// 위경도 -> 단기예보 지점 좌표(X,Y) 전환 함수(기상청 단기예보 조회서비스 오픈API 활용가이드 참고)
    public void getGridxy(double v1, double v2) {
 
        double DEGRAD = Math.PI / 180.0;
        // double RADDEG = 180.0 / Math.PI;
 
        double re = RE / GRID;
        double slat1 = SLAT1 * DEGRAD;
        double slat2 = SLAT2 * DEGRAD;
        double olon = OLON * DEGRAD;
        double olat = OLAT * DEGRAD;
 
        double sn = Math.tan(Math.PI * 0.25 + slat2 * 0.5) / Math.tan(Math.PI * 0.25 + slat1 * 0.5);
        sn = Math.log(Math.cos(slat1) / Math.cos(slat2)) / Math.log(sn);
        double sf = Math.tan(Math.PI * 0.25 + slat1 * 0.5);
        sf = Math.pow(sf, sn) * Math.cos(slat1) / sn;
        double ro = Math.tan(Math.PI * 0.25 + olat * 0.5);
        ro = re * sf / Math.pow(ro, sn);
        Map<String, Object> map = new HashMap<String, Object>();
        double ra = Math.tan(Math.PI * 0.25 + (v1) * DEGRAD * 0.5);
        ra = re * sf / Math.pow(ra, sn);
        double theta = v2 * DEGRAD - olon;
        if (theta > Math.PI)
            theta -= 2.0 * Math.PI;
        if (theta < -Math.PI)
            theta += 2.0 * Math.PI;
        theta *= sn;
 
        map.put("x", Math.floor(ra * Math.sin(theta) + XO + 0.5));
        map.put("y", Math.floor(ro - ra * Math.cos(theta) + YO + 0.5));
        
        // 단기예보 api 조회 시 소숫점 불필요하여 형변환
        gridY = (int) Math.floor(ra * Math.sin(theta) + XO + 0.5);
        gridX = (int) Math.floor(ro - ra * Math.cos(theta) + YO + 0.5);
        
    }

}
