/*
 * Copyright (c) 2015. Ncomz. All Rights Reserved
 */

package com.ntels.onecable.admin.common.http;

import com.ntels.onecable.admin.common.AppConfig;
import com.ntels.onecable.admin.common.Constants;
import com.ntels.onecable.admin.common.c_interface.OnTaskExecutionFinished;
import com.ntels.onecable.admin.common.exception.ParseResponseException;
import com.ntels.onecable.admin.common.exception.RequestUnsuccessfulException;
import com.ntels.onecable.admin.common.util.Dlog;
import com.ntels.onecable.admin.common.util.Util;

import android.content.Context;
import android.os.AsyncTask;
import android.webkit.CookieManager;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

/**
 * <pre>
 * 1. ClassName :   HttpTask
 * 2. FileName  :   HttpTask.java
 * 3. Package   :   com.ntels.onecable.admin.common.http
 * 4. Date      :   2016-12-06
 * 5. Author    :   aaron
 * 6. Comment   :   {@link AsyncTask} for making the http request.
 * </pre>
 */
public class HttpTask extends AsyncTask<CustomRequest, Integer, Response[]> {

    private static final String TAG = HttpTask.class.getSimpleName();

    protected Context context;

    private OnTaskExecutionFinished _task_finished_event;
    private Util mUtil = new Util();
    private final String boundary = "SwA" + Long.toString(System.currentTimeMillis()) + "SwA";
    private static final String LINE_FEED = "\r\n";
    private String delimiter = "--";
    private String sTempCookies = "";
    String charset = "UTF-8";

    public HttpTask() {

    }

    /**
     * HTTP response listener setting.
     *
     * @param _event Response listener
     */
    public void setOnTaskFinishedEvent(OnTaskExecutionFinished _event) {

        if (_event != null) {
            this._task_finished_event = _event;
        }
    }

    /**
     * 생성자.
     *
     * @param context 컨텍스트
     */
    public HttpTask(Context context) {

        this.context = context;
    }

    @Override
    protected void onPreExecute() {

    }

    @Override
    protected Response[] doInBackground(CustomRequest... customRequests) {

        Response[] responses = new Response[customRequests.length];
        for (int i = 0; i < customRequests.length; i++) {
            responses[i] = null;
            try {
                responses[i] = makeRequest(customRequests[i]);
            } catch (RequestUnsuccessfulException e) {
                String msg = "Request was not made successfully";
                Dlog.v(msg, e);
                responses[i] = ResponseHelper.writeException(msg, e);
            } catch (ParseResponseException e) {
                String msg = "Response cannot be parsed successfully";
                Dlog.v(msg, e);
                responses[i] = ResponseHelper.writeException(msg, e);
            }

            publishProgress((i / customRequests.length) * 100);

            if (isCancelled()) {
                // break if cancelled
                break;
            }
        }

        return responses;
    }

    @Override
    protected void onPostExecute(Response[] result) {

        super.onPostExecute(result);
        //        httpSendToActivity.executeResponse(iSendNum, result);
        if (this._task_finished_event != null) {
            this._task_finished_event.OnTaskFihishedEvent(result);
        }
    }

    /**
     * HTTP 요청 수행
     *
     * @param customRequest HTTP 요청 정보 instance
     * @return HTTP 요청 결과
     */
    private Response makeRequest(CustomRequest customRequest) {

        Dlog.v(String.format("request(%s, %s)", customRequest.getUriStr(), customRequest.getBody()));
        Dlog.v(String.format("request(%s, %s)", customRequest.getUriStr(), customRequest.getBodyStr()));

        Response retval = null;

        URL url;
        try {
            url = new URL(customRequest.getUriStr());
        } catch (MalformedURLException e) {
            throw new RequestUnsuccessfulException("Cannot make request with malformed URL.", e);
        }

        Dlog.v("jino1111");
        HttpURLConnection httpConn = null;
        try {

            Dlog.v("jino11112");
            if (url.toString().toLowerCase().contains("https")) {

                trustAllHosts();

                HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
                httpsURLConnection.setHostnameVerifier(new HostnameVerifier() {
                    @Override
                    public boolean verify(String s, SSLSession sslSession) {

                        return true;
                    }
                });

                httpConn = httpsURLConnection;
            } else {
                httpConn = (HttpURLConnection) url.openConnection();
            }
            Dlog.v("jino11113");
            sTempCookies = CookieManager.getInstance().getCookie(url.toString());

            Dlog.v("jino2222");
            Dlog.v(String.format("httpMethod(%s)", customRequest.getHttpMethod()));
            Dlog.v("Accept-Language", AppConfig.getInstance().getLanguage_Id());
            Dlog.v("AppConfig.getInstance().getCtrtGroupId() = " + AppConfig.getInstance().getCtrtGroupId());

            httpConn.setConnectTimeout(10 * 1000);
            httpConn.setReadTimeout(10 * 1000);
            httpConn.setInstanceFollowRedirects(true);
//            httpConn.setRequestProperty("cookie", sTempCookies);
            httpConn.setAllowUserInteraction(false);
            if (customRequest.getHttpMethod().equals(CustomRequest.HTTP_METHOD_MULTIPART)) {
                httpConn.setRequestMethod(CustomRequest.HTTP_METHOD_POST);
            } else {
                httpConn.setRequestMethod(customRequest.getHttpMethod());
            }
            httpConn.setRequestProperty(Constants.KEY_CONTENT_TYPE, "application/json;charset=UTF-8");
            httpConn.setRequestProperty(Constants.KEY_LANG, AppConfig.getInstance().getLanguage_Id());

            if (!AppConfig.getInstance().getCtrtGroupId().equals("")) {
                httpConn.setRequestProperty("X-kneet-homegrp", AppConfig.getInstance().getCtrtGroupId());
            }

            Dlog.v("jino3333");
            //httpConn.setRequestProperty("cookie", sTempCookies);
            //			httpConn.setInstanceFollowRedirects(false); // 세션을 사용하려면 false로 설정해둬야함

            if (customRequest.getSendMethod().equals(CustomRequest.SEND_METHOD_SMART)) {
                if (!mUtil.null2empty(customRequest.getAuthorization1()).equals("")) {
                    Dlog.v("request.getAuthorization1() = " + customRequest.getAuthorization1());
                    httpConn.setRequestProperty("Authorization", customRequest.getAuthorization1());
                }
            } else if (customRequest.getSendMethod().equals(CustomRequest.SEND_METHOD_GEOFENCE)) {
                if (!mUtil.null2empty(customRequest.getAuthorization2()).equals("")) {
                    Dlog.v("request.getAuthorization2() = " + customRequest.getAuthorization2());
                    httpConn.setRequestProperty("Authorization", customRequest.getAuthorization2());
                }
            } else {
                if (!mUtil.null2empty(customRequest.getAuthorization()).equals("")) {
                    Dlog.v("request.getAuthorization() = " + customRequest.getAuthorization());
                    httpConn.setRequestProperty("Authorization", customRequest.getAuthorization());
                }
            }

            Dlog.v("jino4444");

            if (customRequest.getHttpMethod().equals(CustomRequest.HTTP_METHOD_MULTIPART)) {
                httpConn.setUseCaches(false);
                httpConn.setDoOutput(true);
                httpConn.setDoInput(true);
                httpConn.setRequestProperty(Constants.KEY_CONNECTION, "Keep-Alive");
                httpConn.setRequestProperty(Constants.KEY_CONTENT_TYPE, "multipart/form-data; boundary=" + boundary);

                OutputStream os = httpConn.getOutputStream();
                PrintWriter writer = new PrintWriter(new OutputStreamWriter(os, charset), true);

                for (int i = 0; i < customRequest.getBody().size(); i++) {
                    for (Map.Entry<String, Object> entry : customRequest.getBody().get(i).entrySet()) {
//                        addFormField(os, writer, entry.getKey(), URLEncoder.encode(mUtil.null2string(entry.getValue()), "UTF-8") );
                        addFormField(os, writer, entry.getKey(), mUtil.null2string(entry.getValue()));
                    }
                }

                if (customRequest.getAlFilePath() != null) {
                    for (int iRow = 0; iRow < customRequest.getAlFilePath().size(); iRow++) {
                        addFilePart(os, writer, customRequest.getFileParamName(), new File(customRequest.getAlFilePath().get(iRow)));
                    }
                } else if (!customRequest.getFilePath().equals("")) {
                    addFilePart(os, writer, customRequest.getFileParamName(), new File(customRequest.getFilePath()));
                }

//                writer.append(LINE_FEED).flush();
                writer.append(delimiter + boundary + delimiter).append(LINE_FEED);
                writer.close();
            } else {
                if (customRequest.getHttpMethod().equals(CustomRequest.HTTP_METHOD_PUT) || customRequest.getHttpMethod().equals(CustomRequest.HTTP_METHOD_POST)) {
                    httpConn.setDoInput(true);
                    httpConn.setDoOutput(true);

                    OutputStream os = httpConn.getOutputStream();

                    if (!mUtil.null2string(customRequest.getBodyStr()).equals("")) {
                        Dlog.v("customRequest.getBodyStr().toString() = " + customRequest.getBodyStr().toString());
                        os.write(customRequest.getBodyStr().getBytes());
                    } else {
                        Dlog.v("mUtil.convPostBodyJson(request.getBody()).toString()= " + mUtil.convPostBodyJson(customRequest.getBody()).toString());
                        os.write(mUtil.convPostBodyJson(customRequest.getBody()).toString().getBytes());
                    }
                }
            }


            String content = "";
            try {
                content = ResponseHelper.read(httpConn.getInputStream());
            } catch (IOException e1) {
                content = ResponseHelper.read(httpConn.getErrorStream());
            }
            Dlog.v(content);
            getCookies(httpConn);
            Dlog.v(String.format("httpConn.getResponseMessage(): %s", httpConn.getResponseMessage()));
            Dlog.v(String.format("httpConn.getResponseCode(): %s", httpConn.getResponseCode()));

            retval = new Response(httpConn.getResponseCode(), httpConn.getResponseMessage(), content);
        } catch (IOException e) {
            e.printStackTrace();
            try {
                CookieManager.getInstance().setCookie(url.toString(), sTempCookies);
                retval = new Response(httpConn.getResponseCode(), httpConn.getResponseMessage(), "");
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        } catch (Exception e) {
            CookieManager.getInstance().setCookie(url.toString(), sTempCookies);
            e.printStackTrace();
        } finally {
            if (httpConn != null)
                httpConn.disconnect();
        }

        return retval;
    }

    /**
     * HTTP header property 추가.
     *
     * @param os     {@link OutputStream}
     * @param writer {@link PrintWriter}
     * @param name   Property name
     * @param value  Property value
     */
    public void addHeaderField(OutputStream os, PrintWriter writer, String name, String value) {

        writer.append(name + ": " + value).append(LINE_FEED);
        writer.flush();
    }

    /**
     * HTTP body field 추가
     *
     * @param os     {@link OutputStream}
     * @param writer {@link PrintWriter}
     * @param name   Property name
     * @param value  Property value
     */
    public void addFormField(OutputStream os, PrintWriter writer, String name, String value) {

        writer.append(delimiter + boundary).append(LINE_FEED);
        writer.append("Content-Disposition: form-data; name=\"" + name + "\"").append(LINE_FEED);
        writer.append("Content-Type: text/plain; charset=" + charset).append(LINE_FEED);
        writer.append(LINE_FEED);
        writer.append(value).append(LINE_FEED);
        writer.flush();
    }

    /**
     * HTTP multipart 파일 추가
     *
     * @param os         {@link OutputStream}
     * @param writer     {@link PrintWriter}
     * @param fieldName  파일 파라미터명
     * @param uploadFile 업로드할 파일
     */
    public void addFilePart(OutputStream os, PrintWriter writer, String fieldName, File uploadFile) throws IOException {

        String fileName = uploadFile.getName();
        writer.append(delimiter + boundary).append(LINE_FEED);
        writer.append("Content-Disposition: form-data; name=\"" + fieldName + "\"; filename=\"" + getFileName(fileName) + "\"").append(LINE_FEED);
        //        writer.append("Content-Disposition: form-data; name=\"" + fieldName + "\"; filename=\"" + fieldName + "\"").append(LINE_FEED);
        writer.append("Content-Type: " + URLConnection.guessContentTypeFromName(fileName)).append(LINE_FEED);
        writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED);
        writer.append(LINE_FEED);
        writer.flush();

        FileInputStream inputStream = new FileInputStream(uploadFile);
        byte[] buffer = new byte[4096];
        int bytesRead = -1;
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            os.write(buffer, 0, bytesRead);
        }
        os.flush();
        inputStream.close();

        writer.append(LINE_FEED);
        writer.flush();
    }

    /**
     * 파일명 가져오기.
     *
     * @param strPathFilename 파일 경로
     * @return 파일명
     */
    private static String getFileName(String strPathFilename) {

        final char cPathSeparator = '/';
        String strResult;

        int nSep = strPathFilename.lastIndexOf(cPathSeparator);
        if (nSep == -1) {
            strResult = new String(strPathFilename);
        } else {
            strResult = strPathFilename.substring(nSep + 1, strPathFilename.length());
        }

        return strResult;
    }

    /**
     * Request를 받되 세션 유지를 위해 쿠키를 저장한다.
     *
     * @param httpConn {@link HttpURLConnection}
     */
    public void getCookies(HttpURLConnection httpConn) {

        // / 맵에다 Http헤더를 받아냄
        Map<String, List<String>> imap = httpConn.getHeaderFields();
        String sCookies = "";
        // / 그리고 거길 뒤져서 쿠키를 찾아냄
        if (imap.containsKey("Set-Cookie")) {
            // / 쿠키를 스트링으로 쫙 저장함
            List<String> lString = imap.get("Set-Cookie");
            for (int i = 0; i < lString.size(); i++) {
                sCookies += lString.get(i);
            }
        }

        if (!sCookies.trim().equals("")) {
            CookieManager.getInstance().setCookie(httpConn.getURL().toString(), sCookies);
        }
        Dlog.v("set cookies" + sCookies);
    }

    /**
     * HTTPS 모든 인증서 처리
     */
    private static void trustAllHosts() {
        // Create a trust manager that does not validate certificate chains
        TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {

                return new java.security.cert.X509Certificate[]{};
            }

            @Override
            public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws java.security.cert.CertificateException {


            }

            @Override
            public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws java.security.cert.CertificateException {


            }
        }
        };

        // Install the all-trusting trust manager
        try {
            SSLContext sc = SSLContext.getInstance("TLS");
            sc.init(null, trustAllCerts, new java.security.SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
