package com.avg.zaap.crashlibrary.offline.publisher.metadata;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.avg.zaap.crashlibrary.ReportData;
import com.avg.zaap.crashlibrary.core.logger.CrashLogger;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.util.Arrays;
import java.util.List;

/**
 * This class serves as the default crash metadata reader.
 * <p/>
 * Created by marek.osvald on 29.7.2016.
 */
public class CrashMetadataReader implements ICrashMetadataReader {

    private static final String TAG = CrashMetadataReader.class.getSimpleName();

    private ICrashMetadataHelper mCrashMetadataHelper;
    private ICrashMetadataReaderFactory mFactory;

    /**
     * Default constructor. Uses default dependency factory.
     */
    public CrashMetadataReader() {
        this(new CrashMetadataReaderFactory());
    }

    /**
     * Parametric constructor. Sets dependency factory to be used.
     *
     * @param factory dependency factory to be used
     */
    public CrashMetadataReader(ICrashMetadataReaderFactory factory) {
        mCrashMetadataHelper = factory.getCrashMetadataHelper();
        mFactory = factory;
    }

    @Override
    public ReportData readMetadata(@NonNull File file) {
        File parent = file.getParentFile();

        String crashMetadataFileName = mCrashMetadataHelper.getMetadataFileName(file);
        CrashLogger.d(TAG, "Crash metadata filename: %s", crashMetadataFileName);
        File metadataFile = mFactory.openFile(parent, crashMetadataFileName);

        return processMetadataFile(metadataFile);
    }

    @Nullable
    private ReportData processMetadataFile(@NonNull File file) {
        ReportData reportData = mFactory.getReportData();

        Reader reader;
        BufferedReader bufferedReader = null;

        try {
            reader = mFactory.getReader(file);
            bufferedReader = mFactory.getBufferedReader(reader);

            String line;

            // common
            line = bufferedReader.readLine();
            if (line != null) {
                CrashLogger.d(TAG, "Setting crash code: %s", line);
                reportData.setCrashCode(line);
            } else {
                CrashLogger.e(TAG, "Unexpected end of file.");
                return null;
            }

            line = bufferedReader.readLine();
            if (line != null) {
                CrashLogger.d(TAG, "Setting error name: %s", line);
                reportData.setErrorName(line);
            } else {
                CrashLogger.e(TAG, "Unexpected end of file.");
                return null;
            }

            line = bufferedReader.readLine();
            if (line != null) {
                CrashLogger.d(TAG, "Setting error message: %s", line);
                reportData.setErrorMessage(line);
            } else {
                CrashLogger.e(TAG, "Unexpected end of file.");
                return null;
            }

            line = bufferedReader.readLine();
            if (line != null) {
                CrashLogger.d(TAG, "Setting error details: %s", line);
                reportData.setErrorDetails(unescapeNewLines(line));
            } else {
                CrashLogger.e(TAG, "Unexpected end of file.");
                return null;
            }

            // filesToPack handling
            line = bufferedReader.readLine();
            if (line != null) {
                final String[] fileArray = line.split(String.valueOf(ICrashMetadataHelper.FILE_NAME_SEPARATOR));
                List<String> filesToPack = mFactory.getList(fileArray.length);
                filesToPack.addAll(Arrays.asList(fileArray));

                CrashLogger.d(TAG, "Setting filesToPack: %s", line);
                reportData.setFilesToPack(filesToPack);
            } else {
                CrashLogger.e(TAG, "Unexpected end of file.");
                return null;
            }

            // CAP
            line = bufferedReader.readLine();
            if (line != null) {
                CrashLogger.d(TAG, "Setting CAP exception code: %s", line);
                reportData.setCapExceptionCode(line);
            } else {
                CrashLogger.e(TAG, "Unexpected end of file.");
                return null;
            }

            line = bufferedReader.readLine();
            if (line != null) {
                CrashLogger.d(TAG, "Setting CAP faulting offset: %s", line);
                reportData.setCapFaultingOffset(Integer.valueOf(line));
            } else {
                CrashLogger.e(TAG, "Unexpected end of file.");
                return null;
            }

            line = bufferedReader.readLine();
            if (line != null) {
                CrashLogger.d(TAG, "Setting CAP module name: %s", line);
                reportData.setCapModuleName(line);
            } else {
                CrashLogger.e(TAG, "Unexpected end of file.");
                return null;
            }

            line = bufferedReader.readLine();
            if (line != null) {
                CrashLogger.d(TAG, "Setting CAP module version: %s", line);
                reportData.setCapModuleVersion(line);
            } else {
                CrashLogger.e(TAG, "Unexpected end of file.");
                return null;
            }

            line = bufferedReader.readLine();
            if (line != null) {
                CrashLogger.d(TAG, "Setting CAP process name: %s", line);
                reportData.setCapProcessName(line);
            } else {
                CrashLogger.e(TAG, "Unexpected end of file.");
                return null;
            }

            line = bufferedReader.readLine();
            if (line != null) {
                CrashLogger.d(TAG, "Setting CAP process version: %s", line);
                reportData.setCapProcessVersion(line);
            } else {
                CrashLogger.e(TAG, "Unexpected end of file.");
                return null;
            }

            line = bufferedReader.readLine();
            if (line != null) {
                CrashLogger.d(TAG, "Setting CAP GUID: %s", line);
                reportData.setCapGUID(line);
            } else {
                CrashLogger.e(TAG, "Unexpected end of file.");
                return null;
            }

            // offline
            line = bufferedReader.readLine();
            if (line != null) {
                CrashLogger.d(TAG, "Setting publish attempts: %s", line);
                reportData.setPublishAttempts(Integer.valueOf(line));
            } else {
                CrashLogger.e(TAG, "Unexpected end of file.");
                return null;
            }

            line = bufferedReader.readLine();
            if (line != null) {
                CrashLogger.d(TAG, "Setting last attempt timestamp: %s", line);
                reportData.setLastAttemptTimestamp(Long.parseLong(line));
            } else {
                CrashLogger.e(TAG, "Unexpected end of file.");
                return null;
            }

            return reportData;
        } catch (IOException ex) {
            CrashLogger.e(TAG, "Unable to read metadata, original exception %s: %s", ex.getClass().getSimpleName(), ex.getMessage());
        } catch (NumberFormatException ex) {
            CrashLogger.e(TAG, "Malformed number, original exception %s: %s", ex.getClass().getSimpleName(), ex.getMessage());
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException ex) {
                    CrashLogger.w(TAG, "Unable to close buffered reader, original exception %s: %s", ex.getClass().getSimpleName(), ex.getMessage());
                }
            }
        }

        return null;
    }

    private String unescapeNewLines(@NonNull String source) {
        return source.replace("\\n", "\n");
    }

}
