package com.avg.zaap.crashlibrary.publish.externalstorage;

import android.content.Context;
import android.support.annotation.NonNull;

import com.avg.zaap.crashlibrary.ReportData;
import com.avg.zaap.crashlibrary.core.logger.CrashLogger;
import com.avg.zaap.crashlibrary.publish.ICrashPublisher;
import com.avg.zaap.crashlibrary.publish.ICrashPublisherCallback;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * This class publishes the crash report data to the external storage.
 * <p/>
 * Created by marek.osvald on 29.4.2016.
 */
    public class ExternalStorageCrashPublisher implements ICrashPublisher {

    private static final String TAG = ExternalStorageCrashPublisher.class.getSimpleName();
    private static final int BUFFER_SIZE = 4096; // 4 kilobytes

    private IExternalStorageCrashPublisherFactory mFactory;

    /**
     * Default constructor. Uses the default dependency factory.
     */
    @SuppressWarnings("unused")
    public ExternalStorageCrashPublisher() {
        this(new ExternalStorageCrashPublisherFactory());
    }

    /**
     * Parametric constructor. Sets dependency factory to be used.
     *
     * @param factory dependency factory to use
     */
    public ExternalStorageCrashPublisher(IExternalStorageCrashPublisherFactory factory) {
        mFactory = factory;
    }

    @Override
    public void publish(Context context, @NonNull ReportData reportData, @NonNull String filePath, ICrashPublisherCallback callback) {
        CrashLogger.d(TAG, "publish() called, filePath: %s", filePath);

        final File destinationDirectory = context.getExternalFilesDir(null);

        if (destinationDirectory == null) {
            CrashLogger.e(TAG, "Unable to get external storage directory. External storage directory is null.");
            callback.onCrashPublisherFailure(TAG, reportData, null);
            return;
        }

        InputStream inputStream = null;
        OutputStream outputStream = null;

        try {
            String destinationDirectoryPath = destinationDirectory.getAbsolutePath();

            CrashLogger.d(TAG, "Destination directory: %s", destinationDirectoryPath);
            CrashLogger.d(TAG, "Crash code: %s", reportData.getCrashCode());

            File inputFile = mFactory.openFile(filePath);
            String outputFileName = inputFile.getName();

            File outputFile = mFactory.openFile(destinationDirectory, outputFileName);

            if (outputFile.exists()) {
                if (!outputFile.delete()) {
                    CrashLogger.e(TAG, String.format("Unable to delete log file: %s.", outputFile.getAbsolutePath()));
                    callback.onCrashPublisherFailure(getClass().getSimpleName(), reportData, null);
                    return;
                }
            }

            if (!outputFile.createNewFile()) {
                CrashLogger.e(TAG, String.format("Unable to create the log file: %s.", outputFile.getAbsolutePath()));
                callback.onCrashPublisherFailure(getClass().getSimpleName(), reportData, null);
                return;
            }

            inputStream = mFactory.openFileInputStream(inputFile);
            outputStream = mFactory.openFileOutputStream(outputFile);

            int read;
            byte[] buffer = mFactory.getBuffer(BUFFER_SIZE);

            while ((read = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, read);
            }

            callback.onCrashPublisherSuccess(TAG, reportData);
        } catch (FileNotFoundException ex) {
            CrashLogger.e(TAG, "File not found, original exception %s: %s", ex.getClass().getName(), ex.getMessage());
            callback.onCrashPublisherFailure(TAG, reportData, ex);
        } catch (IOException ex) {
            CrashLogger.e(TAG, "IOException, original exception %s: %s", ex.getClass().getName(), ex.getMessage());
            callback.onCrashPublisherFailure(TAG, reportData, ex);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException ignored) {
                }
            }

            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException ignored) {
                }
            }
        }
    }

}
