package com.topjohnwu.magisk.services;

import android.app.Service;
import android.content.Intent;
import android.net.Uri;
import android.os.IBinder;
import android.widget.Toast;

import com.topjohnwu.magisk.ClassMap;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.ProgressNotification;
import com.topjohnwu.magisk.container.Repo;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.net.Networking;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import androidx.annotation.Nullable;

public class DownloadModuleService extends Service {

    private boolean running = false;

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (flags == 0 && running) {
            Utils.toast(R.string.dl_one_module, Toast.LENGTH_LONG);
        } else {
            running = true;
            Shell.EXECUTOR.execute(() -> {
                Repo repo = intent.getParcelableExtra("repo");
                boolean install = intent.getBooleanExtra("install", false);
                dlProcessInstall(repo, install);
                stopSelf();
            });
        }
        return START_REDELIVER_INTENT;
    }

    private void dlProcessInstall(Repo repo, boolean install) {
        File output = new File(Const.EXTERNAL_PATH, repo.getDownloadFilename());
        ProgressNotification progress = new ProgressNotification(output.getName());
        startForeground(progress.hashCode(), progress.getNotification());
        try {
            InputStream in = Networking.get(repo.getZipUrl())
                    .setDownloadProgressListener(progress)
                    .execForInputStream().getResult();
            removeTopFolder(in, new BufferedOutputStream(new FileOutputStream(output)));
            if (install) {
                progress.dismiss();
                Intent intent = new Intent(this, ClassMap.get(FlashActivity.class));
                intent.setData(Uri.fromFile(output))
                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                        .putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
                startActivity(intent);
            } else {
                progress.dlDone();
            }
        } catch (Exception e) {
            e.printStackTrace();
            progress.dlFail();
        }
    }

    private void removeTopFolder(InputStream in, OutputStream out) throws IOException {
        try (ZipInputStream zin = new ZipInputStream(in);
             ZipOutputStream zout = new ZipOutputStream(out)) {
            ZipEntry entry;
            int off = -1;
            while ((entry = zin.getNextEntry()) != null) {
                if (off < 0)
                    off = entry.getName().indexOf('/') + 1;
                String path = entry.getName().substring(off);
                if (path.isEmpty())
                    continue;
                zout.putNextEntry(new ZipEntry(path));
                if (!entry.isDirectory())
                    ShellUtils.pump(zin, zout);
            }
        }
    }
}
