package com.dbs.mplus.fatima.framework.processor.msg;

import com.dbs.mplus.fatima.framework.MPlusFrameworkConstants;
import com.dbs.mplus.fatima.framework.util.MPlusLogUtil;
import com.dbs.mplus.fatima.framework.util.MPlusStringUtil;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

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

/**
 * JSON 메시지 분석기
 * @author hyeeun
 */
public class MessageParser {
    /**
     * json 문자열을 분석하여 기본적인 유효성 검사를 하고, 클래스로 반환
     * @param jsonStr 분석할 문자열
     * @return jsonStr을 분석한 결과 클래스
     * @throws JSONException
     */
    public static Message convert(String jsonStr) throws JSONException {
        final JSONObject obj = new JSONObject(jsonStr);
        // 문자열로 꺼내오고
        final String type = obj.getString(MPlusFrameworkConstants.MSG_TYPE_STR);
        // 결과를 담을 메시지
        Message message = null;

        // 변환해서
        final MPlusFrameworkConstants.MSG_TYPE msgType = MPlusFrameworkConstants.MSG_TYPE.getEnum(type);
        // 유효성 검사하고
        if(msgType == null) {
            throw new JSONException("invalid message type : " + type);
        }

        // 타입에 따라 분석
        switch(msgType) {
        case ERROR :
        {
            final JSONObject valueObj = obj.getJSONObject(MPlusFrameworkConstants.MSG_VALUE_STR);
            // 값을 변환하고
            final Map<String, String> valueMap = MessageParser.check(valueObj,
                    MPlusFrameworkConstants.MSG_VALUE_KEY.ERROR_MSG.toString());

            // 붙여주기
            message = new ErrorMessage(valueMap);
        }
        break;

        case COMMAND:
        {
            final String functionStr = obj.getString(MPlusFrameworkConstants.MSG_FUNCTION_TYPE_STR);
            // function 값의 유효성 확인
            final MPlusFrameworkConstants.MSG_FUNCTION_TYPE function = MPlusFrameworkConstants.MSG_FUNCTION_TYPE.getEnum(functionStr);
            if(function == null) {
                throw new JSONException("invalid function type : " + functionStr);
            }

            // function 실행에 필요한 값 변환
            Map<String, String> valueMap = null;
            if(!obj.isNull(MPlusFrameworkConstants.MSG_VALUE_STR)) {
                valueMap = convertFunction(function, obj.getJSONObject(MPlusFrameworkConstants.MSG_VALUE_STR));
            }

            // 전자동의서 작성 요청이면
            if(function.equals(MPlusFrameworkConstants.MSG_FUNCTION_TYPE.CONSENT)) {
                Map<String, String> paramMap = new HashMap<String, String>();
                // 동의서 공통 파라미터
                String userId = obj.getString(MPlusFrameworkConstants.CONSENT_USERID_STR);
                JSONObject jsonParam = obj.getJSONObject(MPlusFrameworkConstants.CONSENT_COMMON_PARAM_STR);
                Iterator<String> it = jsonParam.keys();
                while(it.hasNext()) {
                    String paramKey = it.next();
                    String paramValue = jsonParam.getString(paramKey);
                    paramMap.put(paramKey, paramValue);
                }

                // 동의서 Form 파라미터 뽑아오기
                List<Map<String, String>> consentMapList = new ArrayList<Map<String, String>>();
                JSONArray jsonArray = obj.getJSONArray(MPlusFrameworkConstants.CONSENT_PARAM_STR);
                for(int i=0; i<jsonArray.length(); i++) {
                    JSONObject jsonObj = jsonArray.getJSONObject(i);
                    Iterator<String> iterator = jsonObj.keys();
                    Map<String, String> consentParamMap = new HashMap<String, String>();
                    while(iterator.hasNext()) {
                        String paramKey = iterator.next();
                        String paramValue = jsonObj.getString(paramKey);
                        consentParamMap.put(paramKey, paramValue);
                    }
                    consentMapList.add(consentParamMap);
                }
                // 동의서 메시지 생성
                message = new ConsentMessage(function, valueMap, userId, paramMap, consentMapList);
            } else if(function.equals(MPlusFrameworkConstants.MSG_FUNCTION_TYPE.CONSENT_SIGN)) {
                // 동의서 공통 파라미터
                String userId = obj.getString(MPlusFrameworkConstants.CONSENT_USERID_STR);
                // 서명할 동의서 목록 뽑아오기
                List<Map<String, String>> consentSignMapList = new ArrayList<Map<String, String>>();
                JSONArray jsonArray = obj.getJSONArray(MPlusFrameworkConstants.CONSENT_SIGN_PARAM_STR);
                for(int i=0; i<jsonArray.length(); i++) {
                    JSONObject jsonObj = jsonArray.getJSONObject(i);
                    Iterator<String> iterator = jsonObj.keys();
                    Map<String, String> consentParamMap = new HashMap<String, String>();
                    while(iterator.hasNext()) {
                        String paramKey = iterator.next();
                        String paramValue = jsonObj.getString(paramKey);
                        consentParamMap.put(paramKey, paramValue);
                    }
                    consentSignMapList.add(consentParamMap);
                }
                message = new ConsentSignMessage(function, valueMap, userId, consentSignMapList);
            } else {
                message = new CommandMessage(function, valueMap);
            }
        }
        break;

        default :
            throw new JSONException("invalid message type : " + type);
        }

        return message;
    }

    /**
     * 메시지에 맞는 파라미터가 들어왔는지 확인하고, 정상인 경우 맵을 반환
     * @param function 메시지 타입이 command인 경우에만 유효한 function
     * @param value 분석할 값
     * @return 분석된 값의 맵
     * @throws JSONException
     */
    private static Map<String, String> convertFunction(MPlusFrameworkConstants.MSG_FUNCTION_TYPE function, JSONObject value) throws JSONException {
        switch(function) {
            case BROWSING:
               return MessageParser.check(value,
            		   MPlusFrameworkConstants.MSG_VALUE_KEY.BROWSING_URL.toString());
            case SYSTEM_BROWSING : 
            	return MessageParser.check(value,
            			MPlusFrameworkConstants.MSG_VALUE_KEY.BROWSING_URL.toString());
            case POPUP:
                return MessageParser.check(value,
                		MPlusFrameworkConstants.MSG_VALUE_KEY.BROWSING_URL.toString());
            case TOKEN:
            	return MessageParser.check(value,
            			MPlusFrameworkConstants.MSG_VALUE_KEY.CALLBACK_FN.toString());
            case SCREEN : 
            	return MessageParser.check(value,
            			MPlusFrameworkConstants.MSG_VALUE_KEY.ORIENTATION.toString());
            case VERSION :
                return MessageParser.check(value,
                        MPlusFrameworkConstants.MSG_VALUE_KEY.CALLBACK_FN.toString());
            case POPUP_BROWSING:
                return MessageParser.check(value,
                        MPlusFrameworkConstants.MSG_VALUE_KEY.BROWSING_URL.toString());
            case CONSENT:
                return MessageParser.check(value,
                        MPlusFrameworkConstants.MSG_VALUE_KEY.CALLBACK_FN.toString());
            case CONSENT_SIGN:
                return MessageParser.check(value,
                        MPlusFrameworkConstants.MSG_VALUE_KEY.CALLBACK_FN.toString());
            default : 
            	MPlusLogUtil.DEBUG(MessageParser.class, "메시지 파라미터 없음. function=" + function);
                break; 
        }

        return null;
    }

    /**
     * 지정된 값이 존재하는지 확인하고, 모두 유효할때 Map 형태로 반환
     * @param obj 값을 가지고 있는 변수
     * @param keys 존재하는지 검사 대상이 되는 목록
     * @return 값이 모두 존재하는 경우 반환되는 Map
     * @throws JSONException
     */
    private static Map<String, String> check(JSONObject obj, String... keys) throws JSONException {
        final Map<String, String> map = new HashMap<String, String>();
        String value = null;

        for(final String key : keys) {
            value = obj.getString(key);
            if(MPlusStringUtil.isEmpty(value)) {
                throw new JSONException("invalid value : " + obj);
            }
            map.put(key, value);
        }
        return map;
    }
}
