package com.dbs.mplus.service.api.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.dbs.mplus.framework.FrameworkConstants;
import com.dbs.mplus.framework.util.ConvertUtil;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

/**
 * JSONObject를 매번 까보는 대신에 공통적으로 필요한 부분을 꺼내기 위함 
 * 
 * 
 * @author siyoung.ahn
 *
 */
public class MplusBaseResParser {
	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	private Map<String, Object> resultMap;
	private List<Map<String, Object>> resultList; 
	
	// 결과
	private static final String KEY_RESULT = "result"; 
	// 처리 결과 메시지
	private static final String KEY_EXTRA_MESSAGE = "message"; 
	// 에러 키
	//private static final String KEY_ERROR = "error";
	// 성공 키
	private static final String KEY_SUCCESS = "success";
	// 텍스트 키
	private static final String KEY_TEXT = "text"; 

	private boolean bList = false;
	private boolean bEmptyResult = false; 
	private boolean bError = false;
	
	private String errorMessage; 
	private String extraMessage; 

	/**
	 * 생성자  
	 * @param obj
	 */
	public MplusBaseResParser(JSONObject obj) {
		
		/*
		 * result: [] 
		 * message: ""
		 * error: true or false
		 * */
		
		// 텍스트 체크
		if(obj.containsKey(KEY_TEXT)) {
			// 에러가 아님. text key 데이터를 result키에 다시 넣어준다. 
			obj.put(KEY_SUCCESS, true);
			obj.put(KEY_RESULT, obj);
		}
		
		// 에러인지 체크
		if(obj.getBoolean(KEY_SUCCESS))
			this.bError = false;
		else
			this.bError = true;
		
		// 메시지 처리
		if(obj.containsKey(KEY_EXTRA_MESSAGE)) {
			if(this.bError) {
				errorMessage = obj.getString(KEY_EXTRA_MESSAGE);
				extraMessage = obj.getString(KEY_EXTRA_MESSAGE);
			} else {
				extraMessage = obj.getString(KEY_EXTRA_MESSAGE);
			}
		} else {
			extraMessage = "";
		}
		
		// 결과 처리
		if( this.bError )
			this.bEmptyResult = true;
		else
			this.bEmptyResult = false;
		
		Object resultObj = obj.get(KEY_RESULT);
		if (resultObj == null ) {
			this.bEmptyResult = true;
		} else {
			if (resultObj instanceof JSONArray) 
				this.bList = true;
			else
				this.bList = false;
		}
		
		// 비어있다면!
		if(this.bEmptyResult) {
			return;
		}	
		
		// JSONObject > map 인 경우
		if( !this.bList )
		{
			JSONObject resultJSONObj = JSONObject.fromObject(resultObj);
			if(resultJSONObj != null && !resultJSONObj.isNullObject()) { 
				this.resultMap = this.convert(resultJSONObj); 
			}
		} 
		// JSONObject > array<map> 인 경우
		else 
		{
			this.resultList = new ArrayList<Map<String, Object>>(); 
			final JSONArray array = JSONArray.fromObject(resultObj);

			// 데이터가 있는 경우에만 변환 작업  
			if(array != null && !array.isEmpty()) { 
				for(int i = 0; i < array.size(); i++) {
					this.resultList.add(this.convert(array.getJSONObject(i)));
				}				
			}
		}
	}
	
	/**
	 * 결과가 에러인지 여부 
	 * @return
	 */
	public boolean isError() { 
		return false;
	}
	/**
	 * 결과가 리스트인지 여부 
	 * @return
	 */
	public boolean isResultList() { 
		return false; 
	}
	
	/**
	 * isResultList == true 이면 List<Map<String, String>> 반환 
	 * false이면 Map<String, String> 반환 
	 * @return
	 */
	public Object getResult() { 
		if(this.bList) {
			return this.resultList; 
		}
		
		return this.resultMap;
	}
	
	/**
	 * JSONObject를 Map형태로 변환 
	 * @param obj
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private Map<String, Object> convert(JSONObject obj) { 
		if(obj == null) {
			if(this.logger.isDebugEnabled()) {
				this.logger.debug("변환할 결과가 없음");
			}
			
			return null; 
		}
		
		final Iterator<String> keyIter = obj.keys(); 
		String key = null; 
		final Map<String, Object> map = new HashMap<String, Object>(); 
		Object value = null;
		
		// value가 null일 경우 Map의 Object에 JSONObject가 들어가게됨.
		// 이것을 클라이언트에 내려주기위해서 스프링프레임워크가 jsonView로 변환할때
		// 에러가 나는듯 함
		// 따라서 클래스가 JSONObject 일 때, null으로 강제로 넣어주는데, 
		// 좀 더 테스트해서 더 확실한 방법으로 처리해야될것같음..
		
		while(keyIter.hasNext()) {
			key = keyIter.next(); 
			value = obj.get(key); 
						
			if(value instanceof JSONObject) {
				map.put(key, null);
			} else if(value instanceof String) { 
				map.put(key, ((String) value).trim());
			} else {
				map.put(key, value); 
			}
		}
		
		return map; 
	}
	/**
	 * 결과가 없는 응답인가? 
	 * @param obj
	 * @return
	 */
	private boolean checkEmptyResult(JSONObject obj) { 
		return false; 
	}
	

	/**
	 * 병원에서 응답 받은 결과가 list인지 여부  
	 * 
	 * 이렇게만 체크해서 최종 결과가 list인지 여부를 체크하는 것은 위험함. 
	 * 왜냐면 병원에서 결과를 list로 준다해놓고, 결과가 1건인 경우는 map으로 던지기도 하기 때문임 
	 * 결과가 list인지, map인지에 대한 것은 api 정의에 있는 것이 최종 형상임 
	 * 
	 * 이 메소드는 병원에서 응답 받은 결과가 list 형태인지 여부만 반환하는 메소드임 
	 * 
	 * @param obj
	 * @return
	 */
	private boolean checkListResultType(JSONObject obj) { 
		return false; 
	}
	
	/**
	 * 결과가 empty인지 여부 
	 * @return
	 */
	public boolean isEmptyResult() { 
		return bEmptyResult; 
	}
	/**
	 * 기간계가 뿜어준 에러 메시지 
	 * @return
	 */
	public String getErrorMessage() { 
		return this.errorMessage; 
	}
	/**
	 * 기간계가 전달한 추가 메시지 
	 * @return
	 */
	public String getExtraMessage() { 
		return this.extraMessage;
	}
	
	@Override 
	public String toString() {
		final StringBuilder builder = new StringBuilder(FrameworkConstants.NEW_LINE); 
				
		builder.append(ConvertUtil.convertStringForDebug(this.resultMap)); 
		
		return builder.toString(); 
	}
}
