/*
 * Copyright (C) 2018 Android WallPad Project
 *
 * FileName  : SslClient.java
 * Project   : Android WallPad Project
 * Company   : HDC I-CONTROLS ( www.icontrols.co.kr )
 * Author    : Kang Sang Ho  , lairu@icontrols.co.kr
 */

package kr.co.icontrols.ilp300_700.ethernet;

import android.util.Log;

import java.net.InetAddress;
import java.net.Socket;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

/**
 * @description SSL Client 용 소켓 생성을 지원하는 api 이다.
 * @import kr.co.icontrols.sslapi.SslClient
 */
public class SSLClient {

    private final String TAG = "SSLClient";

    private Socket SSLSocket = null;
    private byte[] hServerIP = null;
    /**
     * 신뢰할 수 있는 인증기관의 공개키 인지 검증하는 코드를 사용자 코드로 대체
     * 아무런 동작을 하지 않으며 모든 서버 연결을 신뢰한다는 의미이다.
     */
    private TrustManager[] mTrustManager = new TrustManager[]{
            new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() {
                    X509Certificate[] mX509Certificate = new X509Certificate[0];
                    return mX509Certificate;
                }

                @Override
                public void checkClientTrusted(X509Certificate[] certs, String authType) {
                }

                @Override
                public void checkServerTrusted(X509Certificate[] certs, String authType) {
                }
            }
    };

    /**
     * SSL 암호화된 Socket 을 생성한다. (client 전용)
     *
     * @param strHost - 소켓에 연결되어야 하는 호스트 주소
     * @param nPort - 소켓이 접속되고있는 호스트의 포트 번호
     *             <p>
     *             <br><br>
     * @Example <pre class="prettyprint">
     * // SslClient 정의
     * SslClient sslClientSocket = new SslClient("10.0.1.1", 10000);
     * // Socket 가져오기
     * Socket socket = sslClientSocket.getSocket();
     * </pre>
     */
    public SSLClient(String strHost, int nPort) {
        try {
            // 1. Param Check
            if (strHost == null) {
                Log.w(TAG, "[SSLClient] strHost is null!!");
                return;
            }

            // 2. IP Convert & Check
            byte[] hIPInfo = convertIPInfo(strHost);
            if (hIPInfo == null) {
                Log.w(TAG, "[SSLClient] nPort is null!!");
                return;
            }
            hServerIP = hIPInfo;

            // 3. TLS Socket Make
            SSLContext mSSLContext = SSLContext.getInstance("TLS");
            mSSLContext.init(null, mTrustManager, null);
            SSLSocketFactory mSSLSocketFactory = mSSLContext.getSocketFactory();

            // [2018-02-08]
            // * 서버에 DNS Server (bind9)를 설치하여 문제는 해결하였으나, 불필요한 DNS Lookup을 막고자
            //   호스트네임이 정의된 InetAddress 로 IP를 부여함.
            // DNS Lookup 이 일어나지 않게 하기 위하여 호스트네임이 정의된 InetAddress 로 주소를 부여함.
            // IP 만을 "10.0.1.1" 이런 형태로 부여할 경우 Cache 에 접속정보가 없을경우 DNS Lookup이 일어나게되며,
            // DNS Server 가 없을 경우 지연현상이 발생됨.
            InetAddress mInetAddress = InetAddress.getByAddress("", hServerIP);
            SSLSocket = mSSLSocketFactory.createSocket(mInetAddress, nPort);
        } catch (Exception e) {
            Log.e(TAG, "[Exception] SSLClient(String strHost, int nPort)");
            e.printStackTrace();
        }
    }

    /**
     * 생성된 SSL Socket 을 반환한다.
     *
     * @return (Socket) sslSocket
     */
    public Socket getSocket() {
        return SSLSocket;
    }

    /**
     * String IP 를 Byte [] IP 로 변환한다.
     *
     * @param strIP - (String) input IP
     * @return (byte []) output IP
     */
    private byte[] convertIPInfo(String strIP) {
        try {
            // 1. Param Check
            if (strIP == null) return null;

            // 2. Convert
            char[] CharArray = strIP.toCharArray();
            if (CharArray == null) return null;
            for (int i = 0; i < CharArray.length; i++) {
                if (CharArray[i] == '.') CharArray[i] = ';';
            }

            String newIP = new String(CharArray);
            String[] splitData = newIP.split(";");

            if (splitData == null) return null;
            if (splitData.length != 4) return null;

            byte[] outIParray = new byte[4];
            for (int i = 0; i < splitData.length; i++) {
                outIParray[i] = (byte) Integer.parseInt(splitData[i]);
            }

            // 3. return
            return outIParray;
        } catch (Exception e) {
            Log.e(TAG, "[Exception] convertIPInfo(String strIP)");
            e.printStackTrace();
            return null;
        }
    }
}
