package org.telegram.service;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.stealthcopter.networktools.Ping;
import com.stealthcopter.networktools.ping.PingResult;
import com.stealthcopter.networktools.ping.PingStats;

import org.telegram.io.Constants;
import org.telegram.io.CryptLib;
import org.telegram.io.Proxy;
import org.telegram.io.SingletonSocket;
import org.telegram.io.Utils;
import org.telegram.tgnet.ConnectionsManager;

import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.crypto.NoSuchPaddingException;

import io.socket.emitter.Emitter;
import rx.Observable;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;


/**
 * Created by Ahmad Nemati on 1/17/19.
 */
public class SocketService extends Service implements Emitter.Listener {
    private Gson gson;
    private CryptLib cryptLib;
    private static final String key = "6*sN_rZxHD4!X$=T";
    ConnectionsManager connectionsManager;
    Boolean work = false;
    Boolean workAmazon = false;


    @Override
    public void onCreate() {
        super.onCreate();
        gson = new Gson();
        connectionsManager = new ConnectionsManager();
        try {
            cryptLib = new CryptLib();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        }
        SingletonSocket.getInstance().getSocket().on(Constants.GET_JSON_FOR_PING, this);
        SingletonSocket.getInstance().getSocket().on(Constants.GET_JSON_FOR_PING_Amazon, new PingAmazon());
        SingletonSocket.getInstance().getSocket().connect();

    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }

    @Override
    public void call(Object... args) {

        if (args.length == 0 || work) return;


        Log.e("Ping from server Size", String.valueOf(args.length));
        String chiper = String.valueOf(args[0]);

        try {
            String decrypt = cryptLib.decryptCipherTextWithRandomIV(chiper, key);
            List<Proxy> proxyList = gson.fromJson(decrypt, new TypeToken<List<Proxy>>() {
            }.getType());
            proxyList = Utils.removeDuplicator(proxyList);
            Log.e("PingSizeOfRes", String.valueOf(proxyList.size()));
            work = true;
            initCheckProxy(proxyList);
        } catch (Exception e) {
            e.printStackTrace();
        }


    }


    public Observable<Proxy> getPingServerObservable(Proxy proxy) {


        return Observable.create(subscriber -> {
            ConnectionsManager.setCaller(status -> {
                Log.e("Proxy Connect", "IP :" + proxy.getIp());
                proxy.setConnect(true);
                subscriber.onNext(proxy);
                subscriber.onCompleted();

            });
            ConnectionsManager.setProxySettings(proxy.getIp(), proxy.getPort(), proxy.getSecret());


        });
    }

    public Observable<Proxy> getPingAmazonServerObservable(Proxy proxy) {


        return Observable.create(subscriber -> {
            Ping.onAddress(proxy.getIp()).setTimeOutMillis(1000).setTimes(10).doPing(new Ping.PingListener() {
                @Override
                public void onResult(PingResult pingResult) {

                }

                @Override
                public void onFinished(PingStats pingStats) {
                    if (pingStats.getPacketsLost() >=5)
                        proxy.setConnect(false);
                    else
                        proxy.setConnect(true);
                    subscriber.onNext(proxy);
                    subscriber.onCompleted();

                }

                @Override
                public void onError(Exception e) {

                }
            });

        });
    }


    public void initCheckProxy(List<Proxy> proxyList) {


        Observable.from(proxyList)
                .subscribeOn(Schedulers.newThread())
                .concatMap(proxy -> getPingServerObservable(proxy)
                        .subscribeOn(Schedulers.newThread())
                        .timeout(3, TimeUnit.SECONDS)
                        .onErrorResumeNext(throwable -> {
                            ConnectionsManager.setCaller(null);
                            proxy.setConnect(false);
                            return Observable.just(proxy);
                        }))


                .toList()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<List<Proxy>>() {
                    @Override
                    public void onCompleted() {
                        work = false;
                    }

                    @Override
                    public void onError(Throwable e) {
                        work = false;
                    }

                    @Override
                    public void onNext(List<Proxy> proxyList) {
                        int w = 0;
                        int n = 0;

                        for (Proxy proxy : proxyList) {
                            if (proxy.getConnect())
                                w++;
                            else
                                n++;
                        }


                        work = false;
                        Log.e("Status Result :", "Task Check Finished for " + proxyList.size() + " Ips And " + w + " Ips Work and " + n + " IPS not work");

                        try {
                            String cipherText = cryptLib.encryptPlainTextWithRandomIV(gson.toJson(proxyList), key);
                            SingletonSocket.getInstance().getSocket().emit("checkping", cipherText);

                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });


    }

    public void initCheckAmazonProxy(List<Proxy> proxyList) {


        Observable.from(proxyList)
                .subscribeOn(Schedulers.newThread())
                .concatMap(proxy -> getPingAmazonServerObservable(proxy)
                        .subscribeOn(Schedulers.newThread()))


                .toList()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<List<Proxy>>() {
                    @Override
                    public void onCompleted() {
                        workAmazon = false;
                    }

                    @Override
                    public void onError(Throwable e) {
                        workAmazon = false;
                    }

                    @Override
                    public void onNext(List<Proxy> proxyList) {
                        int w = 0;
                        int n = 0;

                        for (Proxy proxy : proxyList) {
                            if (proxy.getConnect())
                                w++;
                            else
                                n++;
                        }


                        work = false;
                        Log.e("Status Amazon Result :", "Task Check Finished for " + proxyList.size() + " Ips And " + w + " Ips Work and " + n + " IPS not work");

                        try {
                            String cipherText = cryptLib.encryptPlainTextWithRandomIV(gson.toJson(proxyList), key);
                            SingletonSocket.getInstance().getSocket().emit("checkAmazonPing", cipherText);

                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });


    }

    private class PingAmazon implements Emitter.Listener {

        @Override
        public void call(Object... args) {
            if (args.length == 0 || workAmazon) return;

            String chiper = String.valueOf(args[0]);
            try {
                String decrypt = cryptLib.decryptCipherTextWithRandomIV(chiper, key);
                List<Proxy> proxyList = gson.fromJson(decrypt, new TypeToken<List<Proxy>>() {
                }.getType());
                proxyList = Utils.removeDuplicator(proxyList);
                workAmazon = true;
                initCheckAmazonProxy(proxyList);
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }


}
