package com.github.ghmxr.apkextractor.tasks;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.provider.DocumentFile;
import android.widget.Toast;

import com.github.ghmxr.apkextractor.Constants;
import com.github.ghmxr.apkextractor.Global;
import com.github.ghmxr.apkextractor.items.FileItem;
import com.github.ghmxr.apkextractor.items.ImportItem;
import com.github.ghmxr.apkextractor.ui.ToastManager;
import com.github.ghmxr.apkextractor.utils.EnvironmentUtil;
import com.github.ghmxr.apkextractor.utils.OutputUtil;
import com.github.ghmxr.apkextractor.utils.SPUtil;
import com.github.ghmxr.apkextractor.utils.StorageUtil;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class ImportTask extends Thread {

    private final ArrayList<ImportItem>importItemArrayList=new ArrayList<>();
    private Context context;
    private volatile boolean isInterrupted=false;
    //private ArrayList<String>write_paths=new ArrayList<>();
    private String currentWritePath;
    private FileItem currentWrtingFileItem;
    private long progress=0;
    private long progress_check_length=0;
    private long speed_bytes=0;
    private long speed_check_time=0;
    private final boolean isExternal;

    private final StringBuilder error_info=new StringBuilder();

    private ImportTaskCallback callback;
    private Uri apkUri;
    private int apk_num=0;

    public ImportTask(@NonNull Context context, @NonNull List<ImportItem>importItems,ImportTaskCallback callback) {
        super();
        this.context=context;
        importItemArrayList.addAll(importItems);
        this.callback=callback;
        isExternal= SPUtil.getIsSaved2ExternalStorage(context);
    }

    @Override
    public void run() {
        super.run();
        for(ImportItem importItem:importItemArrayList){
            if(isInterrupted)break;
            try{
                ZipInputStream zipInputStream=new ZipInputStream(importItem.getZipInputStream());
                ZipEntry zipEntry=zipInputStream.getNextEntry();
                while (zipEntry!=null&&!isInterrupted){
                    try{
                        String entryPath=zipEntry.getName().replaceAll("\\*", "/");
                        if((entryPath.toLowerCase().startsWith("android/data"))&&!zipEntry.isDirectory()&&importItem.importData){
                            unZipToFile(zipInputStream,entryPath);
                        }
                        else if((entryPath.toLowerCase().startsWith("android/obb"))&&!zipEntry.isDirectory()&&importItem.importObb){
                            unZipToFile(zipInputStream,entryPath);
                        }
                        else if((entryPath.toLowerCase().endsWith(".apk"))&&!zipEntry.isDirectory()&&!entryPath.contains("/")&&importItem.importApk){
                            OutputStream outputStream;
                            final String fileName=entryPath.substring(entryPath.lastIndexOf("/")+1);
                            if(isExternal){
                                String writeFileName=getApkFileNameWithNum(fileName);
                                DocumentFile checkFile=OutputUtil.getExportPathDocumentFile(context).findFile(writeFileName);
                                while (checkFile!=null&&checkFile.exists()){
                                    apk_num++;
                                    writeFileName=getApkFileNameWithNum(fileName);
                                    checkFile=OutputUtil.getExportPathDocumentFile(context).findFile(writeFileName);
                                }
                                DocumentFile writeDocumentFile=OutputUtil.getExportPathDocumentFile(context)
                                        .createFile("application/vnd.android.package-archive", writeFileName);
                                outputStream=OutputUtil.getOutputStreamForDocumentFile(context
                                        ,writeDocumentFile);
                                currentWritePath=SPUtil.getDisplayingExportPath(context)+"/"+writeFileName;
                                currentWrtingFileItem=new FileItem(context,writeDocumentFile);
                                apkUri=writeDocumentFile.getUri();
                            }else{
                                String writePath=SPUtil.getInternalSavePath(context)+"/"+ getApkFileNameWithNum(fileName);
                                File writeFile=new File(writePath);
                                while (writeFile.exists()){
                                    apk_num++;
                                    writeFile=new File(SPUtil.getInternalSavePath(context)+"/"+ getApkFileNameWithNum(fileName));
                                }
                                outputStream=new FileOutputStream(writeFile);
                                currentWritePath=writeFile.getAbsolutePath();
                                currentWrtingFileItem=new FileItem(writeFile);
                                if(Build.VERSION.SDK_INT<=23)apkUri=Uri.fromFile(writeFile);
                                else apkUri= EnvironmentUtil.getUriForFileByFileProvider(context,writeFile);
                            }
                            BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(outputStream);
                            byte [] buffer=new byte[1024];
                            int len;
                            while ((len=zipInputStream.read(buffer))!=-1&&!isInterrupted){
                                bufferedOutputStream.write(buffer,0,len);
                                progress+=len;
                                checkSpeedAndPostToCallback(len);
                                checkProgressAndPostToCallback();
                            }
                            bufferedOutputStream.flush();
                            bufferedOutputStream.close();
                            if(!isInterrupted)currentWrtingFileItem=null;
                        }
                        if(!isInterrupted)zipEntry=zipInputStream.getNextEntry();
                        else break;
                    }catch (Exception e){
                        e.printStackTrace();
                        error_info.append(currentWritePath);
                        error_info.append(":");
                        error_info.append(e.toString());
                        error_info.append("\n\n");
                        try{
                            currentWrtingFileItem.delete();
                        }catch (Exception ee){}
                    }
                }
                zipInputStream.close();
            }catch (Exception e){
                e.printStackTrace();
                error_info.append(String.valueOf(importItem.getFileItem().getPath()));
                error_info.append(":");
                error_info.append(e.toString());
                error_info.append("\n\n");
            }

        }
        if(isInterrupted){
            try{
                if(currentWrtingFileItem!=null){
                    currentWrtingFileItem.delete();
                }
            }catch (Exception e){e.printStackTrace();}
        }else if(callback!=null)Global.handler.post(new Runnable() {
            @Override
            public void run() {
                callback.onImportTaskFinished(error_info.toString());
                context.sendBroadcast(new Intent(Constants.ACTION_REFRESH_IMPORT_ITEMS_LIST));
                context.sendBroadcast(new Intent(Constants.ACTION_REFRESH_AVAILIBLE_STORAGE));
                try{
                    if(importItemArrayList.size()==1&&apkUri!=null&&error_info.toString().trim().length()==0){
                        Intent intent=new Intent(Intent.ACTION_VIEW);
                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                        intent.setDataAndType(apkUri,"application/vnd.android.package-archive");
                        context.startActivity(intent);
                    }
                }catch (Exception e){
                    e.printStackTrace();
                    ToastManager.showToast(context,e.toString(), Toast.LENGTH_SHORT);
                }
            }
        });
    }

    private void unZipToFile(ZipInputStream zipInputStream,String entryPath) throws Exception{
        File folder=new File(StorageUtil.getMainExternalStoragePath()+"/"+entryPath.substring(0,entryPath.lastIndexOf("/")));
        if(!folder.exists())folder.mkdirs();
        String writePath=StorageUtil.getMainExternalStoragePath()+"/"+entryPath;
        File writeFile=new File(writePath);
        OutputStream outputStream=new BufferedOutputStream(new FileOutputStream(writeFile));
        currentWritePath=writePath;
        currentWrtingFileItem=new FileItem(writeFile);
        byte [] buffer=new byte[1024];
        int len;
        while ((len=zipInputStream.read(buffer))!=-1&&!isInterrupted){
            outputStream.write(buffer,0,len);
            progress+=len;
            checkSpeedAndPostToCallback(len);
            checkProgressAndPostToCallback();
        }
        outputStream.flush();
        outputStream.close();
        if(!isInterrupted)currentWrtingFileItem=null;
    }

    private void checkProgressAndPostToCallback(){
        if(progress-progress_check_length>100*1024){
            progress_check_length=progress;
            if(callback!=null)Global.handler.post(new Runnable() {
                @Override
                public void run() {
                    callback.onImportTaskProgress(currentWritePath,progress);
                }
            });
        }
    }

    private void checkSpeedAndPostToCallback(long speed_plus_value){
        speed_bytes+=speed_plus_value;
        long current=System.currentTimeMillis();
        if(current-speed_check_time>1000){
            speed_check_time=current;
            final long speed_post=speed_bytes;
            speed_bytes=0;
            if(callback!=null)Global.handler.post(new Runnable() {
                @Override
                public void run() {
                    callback.onRefreshSpeed(speed_post);
                }
            });
        }
    }

    public void setInterrupted(){
        this.isInterrupted=true;
    }

    private String getApkFileNameWithNum(String originName){
        return originName.substring(0,originName.lastIndexOf("."))+(apk_num>0?apk_num:"")+".apk";
    }

    public interface ImportTaskCallback{
        void onImportTaskStarted();
        void onRefreshSpeed(long speed);
        void onImportTaskProgress(@NonNull String writePath,long progress);
        void onImportTaskFinished(@NonNull String errorMessage);
    }
}