package com.bolan.android.modules.update;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Process;
import android.util.Log;

import com.bolan.android.modules.Utils;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

@SuppressWarnings("unused")
public class Updater extends ReactContextBaseJavaModule {

    private static volatile boolean updating = false;
    private static volatile boolean cancel = false;

    public Updater(ReactApplicationContext reactContext) {
        super(reactContext);
    }

    @Override
    public String getName() {
        return "Updater";
    }

    private void emit(String eventName, Object params) {
        getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, params);
    }

    @ReactMethod
    public synchronized void update(final String sUrl, final Promise promise) {
        if (!updating) {
            updating = true;
            cancel = false;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    InputStream is = null;
                    FileOutputStream fos = null;
                    File cacheFile = Utils.getDiskCacheDir(getCurrentActivity());
                    File downloadFile = new File(cacheFile, "download");
                    File apkFile = new File(downloadFile, "bolan.apk");
                    try {
                        if (!downloadFile.exists()) {
                            if (!downloadFile.mkdir()) {
                                promise.reject("ERROR_UPDATE", "Unable to create the download directory.");
                                return;
                            }
                        }
                        File[] files = downloadFile.listFiles();
                        for (File file : files) {
                            if (!file.delete()) {
                                Log.w("Updater", "Unable to delete the old downloaded file: " + file.getAbsolutePath());
                            }
                        }
                        URL url = new URL(sUrl);
                        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                        connection.connect();
                        int length = connection.getContentLength();
                        is = connection.getInputStream();
                        fos = new FileOutputStream(apkFile);
                        int count = 0;
                        int lastProgress = -1;
                        byte buf[] = new byte[1024 * 64];
                        WritableMap event;
                        do {
                            int numRead = is.read(buf);
                            if (numRead <= 0) {
                                event = Arguments.createMap();
                                event.putInt("progress", 100);
                                event.putInt("current", count);
                                event.putInt("all", length);
                                emit("Updater/downloaded", event);
                                break;
                            }
                            count += numRead;
                            int progress = (int) (((float) count / length) * 100);
                            if (progress != lastProgress) {
                                lastProgress = progress;
                                event = Arguments.createMap();
                                event.putInt("progress", progress);
                                event.putInt("current", count);
                                event.putInt("all", length);
                                emit("Updater/downloading", event);
                            }
                            fos.write(buf, 0, numRead);
                        } while (!cancel);
                        if (cancel) {
                            return;
                        }
                        Intent i = new Intent(Intent.ACTION_VIEW);
                        i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        i.setDataAndType(Uri.parse("file://" + apkFile.toString()), "application/vnd.android.package-archive");
                        Context context = getCurrentActivity();
                        if (context == null) {
                            promise.reject("ERROR_UPDATE", "Unable to get the context.");
                            return;
                        }
                        getCurrentActivity().startActivity(i);
                        Process.killProcess(Process.myPid());
                        promise.resolve(null);
                    } catch (MalformedURLException e) {
                        promise.reject("ERROR_UPDATE", "invalid url: " + sUrl);
                    } catch (Exception e) {
                        promise.reject(e);
                    } finally {
                        cancel = false;
                        updating = false;
                        if (fos != null) {
                            try {
                                fos.close();
                            } catch (IOException e) {
                                Log.e("Updater", "Unable to close file stream.", e);
                            }
                        }
                        if (is != null) {
                            try {
                                is.close();
                            } catch (IOException e) {
                                Log.e("Updater", "Unable to close url stream.", e);
                            }
                        }
                    }
                }
            }).start();
        } else {
            promise.reject("ERROR_UPDATE", "There is another updater worker running already.");
        }
    }

    @ReactMethod
    public synchronized void cancel(final Promise promise) {
        cancel = true;
        promise.resolve(null);
    }
}
