package eftr2.dataservice2;

import android.util.Log;
import com.google.gson.Gson;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;
import com.loopj.android.http.RequestParams;
import org.apache.http.Header;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.StringEntity;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;

public abstract class BaseHttpClient {
    private static final String TAG = "eftr2.dataservice2.BaseHttpClient";
    final static String JsonMIMEType = "application/json";
    final static String USER_AGENT = "EFTRM";
    private final static String ENCODING = "UTF-8";

    private final IUserIdenityService identityService;
    private final AsyncHttpClient client;
    protected final Gson gson = new Gson();
    private final String baseUri;
    
    public BaseHttpClient(String baseUri, IUserIdenityService identityService){
    	this.baseUri = baseUri;
    	this.identityService = identityService;
    	String login = this.identityService.getLogin();
    	String password = this.identityService.getPassword();
    	this.client = AsyncHttpClientFactory.getAsyncHttpClient(login, password);
    }
    
    /**
     * @param path       - ресурс на бекенде
     * @param params     - параметры GET запроса
     * @param onResponce - callback для функции
     * @throws UnsupportedEncodingException
     */
    protected void GetAsync(String path, Map<String, String> params, final ResponseHandler<BaseResponse> onResponce) {
        // валидация входных параметров
        if (path == null || path.length() == 0)
            throw new InvalidParameterException("Invalid pathAndQuery parameter");

        if (params == null) params = new HashMap<String, String>();

        // отправляем запрос
        client.get(
                null,                    // android specific
                baseUri + path,            // endpoint
                new RequestParams(params),
                getResponseHandler(onResponce));
    }

    /**
     * @param path       - ресурс на бекенде
     * @param model      - модель объекта
     * @param onResponse - callback для функции
     * @throws UnsupportedEncodingException
     */
    protected void PostAsync(String path, Object model, final ResponseHandler<BaseResponse> onResponse)
    {
        // валидация входных параметров
        if (path == null || path.length() == 0)
            throw new InvalidParameterException("Invalid pathAndQuery parameter");

        if(model==null) model = new Object();
        
        try
        {
            Log.d(TAG, "url = " + baseUri + path);



	        client.post(
	        		null,
	                baseUri + path,            // endpoint
	                new StringEntity(gson.toJson(model), ENCODING),  // Body
	                JsonMIMEType,
	                getResponseHandler(onResponse));
        }
        catch(final Exception e){
        	if(onResponse!=null){
        		onResponse.OnDone(new BaseResponse(true, "599", e.getMessage(), null));
        	}
        }
    }

   
    private AsyncHttpResponseHandler getResponseHandler(final ResponseHandler<BaseResponse> onResponce) {
        return new AsyncHttpResponseHandler() {

            @Override
            public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
                Log.i(TAG, "");
                
                final boolean isFailed = statusCode > 300;
                onResponce.OnDone(
                        new BaseResponse(
                        		isFailed,
                        		isFailed ? String.valueOf(statusCode) : null,
                                null,
                                responseBody
                        )
                );
            }

            @Override
            public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
                Log.e(TAG, "");

                final boolean isFailed = statusCode > 300; 
                onResponce.OnDone(
                        new BaseResponse(
                        		isFailed,
                                String.valueOf(statusCode),
                                isFailed ? error.getMessage() : null,
                                responseBody
                        )
                );

            }
        };
    }
}

class AsyncHttpClientFactory
{
	public static AsyncHttpClient getAsyncHttpClient(String login, String password){
		AsyncHttpClient client = new AsyncHttpClient();
		
        KeyStore trustStore = null;
        try {
            trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            trustStore.load(null, null);
            final MySSLSocketFactory sf = new MySSLSocketFactory(trustStore);
            sf.setHostnameVerifier(MySSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            client.setSSLSocketFactory(sf);
        } catch (Exception e) {
            throw new RuntimeException("Can't initiate client");
        }

        client.addHeader("Accept", BaseHttpClient.JsonMIMEType);

        client.setBasicAuth(login, password);
        //этот метод НЕ работает с упреждающей посылкой basic auth хедера, он сначала пытается всё сделать без посыла авторизации, ожидая что его пошлют на авторизационный урл, и после этого он пошлёт авторизацию...
        //подробнее http://stackoverflow.com/questions/15110497/asynchttpclient-authentication-failed
        //так что тупо запихаем хедер руками
        //client.addHeader("Authorization", "Basic " + Base64.encodeToString((login+":"+password).getBytes(), Base64.NO_WRAP));
        
        //client.setEnableRedirects(true);
        client.setUserAgent(BaseHttpClient.USER_AGENT);
        
        return client;
	}

}

class MySSLSocketFactory extends SSLSocketFactory {
    SSLContext sslContext = SSLContext.getInstance("TLS");

    public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
        super(truststore);

        TrustManager tm = new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };

        sslContext.init(null, new TrustManager[] { tm }, null);
    }

    @Override
    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
        return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
    }

    @Override
    public Socket createSocket() throws IOException {
        return sslContext.getSocketFactory().createSocket();
    }
}