package com.avg.zaap.crashlibrary.config;

import com.avg.zaap.crashlibrary.CrashLib;
import com.avg.zaap.crashlibrary.core.logger.CrashLogger;
import com.avg.zaap.crashlibrary.core.logger.ILogger;
import com.avg.zaap.crashlibrary.offline.Connection;
import com.avg.zaap.crashlibrary.offline.Strategy;
import com.avg.zaap.crashlibrary.provider.ICrashAsyncProvider;
import com.avg.zaap.crashlibrary.provider.ICrashSyncProvider;
import com.avg.zaap.crashlibrary.provider.memory.MemoryHeapProvider;
import com.avg.zaap.crashlibrary.publish.ICrashPublisher;

import java.util.ArrayList;
import java.util.List;

/**
 * Configuration of crash library.
 * <p/>
 * Created by marek.osvald on 7. 4. 2016.
 */
@SuppressWarnings("unused")
public class CrashLibConfig {

    private final String applicationVersion;
    private final EShowErrorType showErrorType;
    private final int errorMessage;
    private final int errorDetails;
    private final int notificationSmallIcon;
    private final ILogger logger;
    private final String crashReportURL;
    private final String crashUploadURL;
    private final String capProductID;
    private final List<ICrashSyncProvider> syncProviders;
    private final List<ICrashAsyncProvider> asyncProviders;
    private final List<ICrashPublisher> crashPublishers;
    private final boolean disableMemoryHeapProvider;
    private final boolean disableStackTraceCrashingThread;
    private final boolean disableStackTraceAllThreads;
    private final boolean disableCapPublisher;
    private final Strategy strategy;
    private final int maxAttempts;
    private final long delay; // milliseconds
    private final boolean offlineSuitable;
    private final Connection connectionLevelSuitable;
    private final boolean roamingSuitable;

    private CrashLibConfig(Builder builder) {
        this.applicationVersion = builder.applicationVersion;
        this.showErrorType = builder.showErrorType;
        this.errorMessage = builder.errorMessage;
        this.errorDetails = builder.errorDetails;
        this.notificationSmallIcon = builder.notificationSmallIcon;
        this.logger = builder.logger;
        this.crashReportURL = builder.crashReportURL;
        this.crashUploadURL = builder.crashUploadURL;
        this.capProductID = builder.capProductID;
        this.syncProviders = builder.syncProviders;
        this.asyncProviders = builder.asyncProviders;
        this.crashPublishers = builder.crashPublishers;
        this.disableMemoryHeapProvider = builder.disableMemoryHeapProvider;
        this.disableStackTraceCrashingThread = builder.disableStackTraceCrashingThread;
        this.disableStackTraceAllThreads = builder.disableStackTraceAllThreads;
        this.disableCapPublisher = builder.disableCapPublisher;
        this.strategy = builder.strategy;
        this.maxAttempts = builder.maxAttempts;
        this.delay = builder.delay;
        this.offlineSuitable = builder.offlineSuitable;
        this.connectionLevelSuitable = builder.connectionLevelSuitable;
        this.roamingSuitable = builder.roamingSuitable;
    }

    /**
     * Returns host application version.
     *
     * @return host application version
     */
    public String getApplicationVersion() {
        return applicationVersion;
    }

    /**
     * Returns error show type.
     *
     * @return error show type
     */
    public EShowErrorType getShowErrorType() {
        return showErrorType;
    }

    /**
     * Returns error title string.
     *
     * @return error title string
     */
    public int getErrorMessage() {
        return errorMessage;
    }

    /**
     * Returns error message string.
     *
     * @return error message string
     */
    public int getErrorDetails() {
        return errorDetails;
    }

    /**
     * Returns notification small icon resource ID.
     *
     * @return notification small icon resource ID
     */
    public int getNotificationSmallIcon() {
        return notificationSmallIcon;
    }

    /**
     * Returns CrashLib logger.
     *
     * @return CrashLib logger
     */
    public ILogger getLogger() {
        return logger;
    }

    /**
     * Returns a list of additional synchronous providers.
     *
     * @return a list of additional synchronous providers
     */
    public List<ICrashSyncProvider> getSyncProviders() {
        return syncProviders;
    }

    /**
     * Returns a list of additional asynchronous providers.
     *
     * @return a list of additional asynchronous providers
     */
    public List<ICrashAsyncProvider> getAsyncProviders() {
        return asyncProviders;
    }

    /**
     * Returns a list of additional publishers.
     *
     * @return a list of additional publishers.
     */
    public List<ICrashPublisher> getPublishers() {
        return crashPublishers;
    }

    /**
     * Returns whether memory heap provider is disabled or not.
     *
     * @return true, provided that memory heap provider is disabled, false otherwise
     */
    public boolean isDisabledMemoryHeapProvider() {
        return disableMemoryHeapProvider;
    }

    /**
     * Returns whether stacktrace crashing thread provider is disabled or not.
     *
     * @return true, provided that stacktrace crashing thread provider is disables, false otherwise
     */
    public boolean isDisabledStackTraceCrashingThread() {
        return disableStackTraceCrashingThread;
    }

    /**
     * Returns whether stacktrace (all threads) provider is disabled or not.
     *
     * @return true, provided that stacktrace (all threads) provider is disabled, false otherwise
     */
    public boolean isDisabledStackTraceAllThreads() {
        return disableStackTraceAllThreads;
    }

    /**
     * Returns whether cap publisher is disabled or not.
     *
     * @return true, if cap publisher is disabled, false otherwise.
     */
    public boolean isDisabledCapPublisher() {
        return disableCapPublisher;
    }

    /**
     * Returns crash report URL.
     *
     * @return crash report URL
     */
    public String getCrashReportURL() {
        return crashReportURL;
    }

    /**
     * Returns crash upload URL.
     *
     * @return crash upload URL
     */
    public String getCrashUploadURL() {
        return crashUploadURL;
    }

    /**
     * Returns CAP Product ID.
     *
     * @return CAP Product ID
     */
    public String getCapProductID() {
        return capProductID;
    }

    /**
     * Returns publishing strategy.
     *
     * @return publishing strategy
     */
    public Strategy getStrategy() {
        return strategy;
    }

    /**
     * Returns the maximal number of publishing attempts.
     *
     * @return maximal number of publishing attempts
     */
    public int getMaxAttempts() {
        return maxAttempts;
    }

    /**
     * Returns the delay between publishing attempts in milliseconds.
     *
     * @return delay between publishing attempts in milliseconds
     */
    public long getDelay() {
        return delay;
    }

    /**
     * Returns whether no connection suitable for crash reporting.
     *
     * @return true, provided that no connection suitable for crash reporting, false otherwise
     */
    public boolean isOfflineSuitable() {
        return offlineSuitable;
    }

    /**
     * Returns minimal connection level suitable for crash reporting.
     *
     * @return minimal connection level suitable for crash reporting
     */
    public Connection getConnectionLevelSuitable() {
        return connectionLevelSuitable;
    }

    /**
     * Returns whether roaming connection suitable for crash reporting.
     *
     * @return true, provided that roaming connection suitable for crash reporting, false otherwise
     */
    public boolean isRoamingSuitable() {
        return roamingSuitable;
    }

    public static class Builder {

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

        private String applicationVersion;
        private EShowErrorType showErrorType;
        private int errorMessage;
        private int errorDetails;
        private int notificationSmallIcon;
        private ILogger logger;
        private String crashReportURL;
        private String crashUploadURL;
        private String capProductID;
        private List<ICrashSyncProvider> syncProviders;
        private List<ICrashAsyncProvider> asyncProviders;
        private List<ICrashPublisher> crashPublishers;
        private boolean disableMemoryHeapProvider;
        private boolean disableStackTraceCrashingThread;
        private boolean disableStackTraceAllThreads;
        private boolean disableCapPublisher;
        private Strategy strategy;
        private int maxAttempts;
        private long delay; // milliseconds
        private boolean offlineSuitable;
        private Connection connectionLevelSuitable;
        private boolean roamingSuitable;

        /**
         * Default constructor. Initializes the builder and sets default values.
         */
        public Builder() {
            // prepare builder
            List<ICrashSyncProvider> defaultSyncProviders = new ArrayList<>();
            defaultSyncProviders.add(new MemoryHeapProvider());

            applicationVersion = CrashLib.DEFAULT_APPLICATION_VERSION;
            showErrorType = CrashLib.DEFAULT_SHOW_ERROR_TYPE;
            errorMessage = CrashLib.DEFAULT_ERROR_MESSAGE;
            errorDetails = CrashLib.DEFAULT_ERROR_DETAILS;
            notificationSmallIcon = CrashLib.DEFAULT_NOTIFICATION_ICON;
            logger = CrashLib.DEFAULT_LOGGER;
            crashReportURL = CrashLib.DEFAULT_CRASH_REPORT_URL;
            crashUploadURL = CrashLib.DEFAULT_CRASH_UPLOAD_URL;
            capProductID = CrashLib.DEFAULT_CAP_PRODUCT_ID;
            syncProviders = defaultSyncProviders;
            disableMemoryHeapProvider = false;
            disableStackTraceAllThreads = false;
            disableStackTraceCrashingThread = false;
            disableCapPublisher = false;
            strategy = Strategy.BEST_EFFORT;
            maxAttempts = CrashLib.DEFAULT_MAX_ATTEMPTS;
            delay = CrashLib.DEFAULT_DELAY;
            offlineSuitable = CrashLib.DEFAULT_OFFLINE_SUITABLE;
            connectionLevelSuitable = CrashLib.DEFAULT_CONNECTION_LEVEL_SUITABLE;
            roamingSuitable = CrashLib.DEFAULT_ROAMING_SUITABLE;
        }

        /**
         * Sets host application version.
         *
         * @param applicationVersion application version to be set
         * @return this instance
         */
        @SuppressWarnings("UnusedReturnValue")
        public Builder applicationVersion(String applicationVersion) {
            this.applicationVersion = applicationVersion;
            return this;
        }

        /**
         * Sets error show type.
         *
         * @param showErrorType error show type to be set
         * @return this instance
         */
        @SuppressWarnings("UnusedReturnValue")
        public Builder showErrorType(EShowErrorType showErrorType) {
            this.showErrorType = showErrorType;
            return this;
        }

        /**
         * Sets error message.
         *
         * @param errorMessage error message to be set
         * @return this instance
         */
        @SuppressWarnings("UnusedReturnValue")
        public Builder errorMessage(int errorMessage) {
            this.errorMessage = errorMessage;
            return this;
        }

        /**
         * Sets error details.
         *
         * @param errorDetails error details to be set
         * @return this instance
         */
        @SuppressWarnings("UnusedReturnValue")
        public Builder errorDetails(int errorDetails) {
            this.errorDetails = errorDetails;
            return this;
        }

        /**
         * Sets notification small icon.
         *
         * @param notificationSmallIcon notification small icon to be set
         * @return this instance
         */
        @SuppressWarnings("UnusedReturnValue")
        public Builder notificationSmallIcon(int notificationSmallIcon) {
            this.notificationSmallIcon = notificationSmallIcon;
            return this;
        }

        /**
         * Sets CrashLib's logger.
         *
         * @param logger CrashLib logger class to be set
         * @return this instance
         */
        @SuppressWarnings("UnusedReturnValue")
        public Builder logger(Class<ILogger> logger) {
            try {
                this.logger = logger.newInstance();
            } catch (InstantiationException e) {
                CrashLogger.e(TAG, "Can't create logger: %s", e.getMessage());
            } catch (IllegalAccessException e) {
                CrashLogger.e(TAG, "Illegal access during logger creation: %s", e.getMessage());
            }
            return this;
        }

        /**
         * Sets CrashLib's logger.
         *
         * @param logger CrashLib logger to be set
         * @return this instance
         */
        @SuppressWarnings("UnusedReturnValue")
        public Builder logger(ILogger logger) {
            this.logger = logger;
            return this;
        }

        /**
         * Sets crash report URL.
         *
         * @param crashReportURL crash report URL to be set
         * @return this instance
         */
        @SuppressWarnings("UnusedReturnValue")
        public Builder crashReportURL(String crashReportURL) {
            this.crashReportURL = crashReportURL;
            return this;
        }

        /**
         * Sets crash upload URL.
         *
         * @param crashUploadURL crash upload URL to be set
         * @return this instance
         */
        @SuppressWarnings("UnusedReturnValue")
        public Builder crashUploadURL(String crashUploadURL) {
            this.crashUploadURL = crashUploadURL;
            return this;
        }

        /**
         * Sets CAP Product ID.
         *
         * @param capProductID CAP Product ID to be set
         * @return this instance
         */
        @SuppressWarnings("UnusedReturnValue")
        public Builder capProductID(String capProductID) {
            this.capProductID = capProductID;
            return this;
        }

        /**
         * Sets a list of additional synchronous providers.
         *
         * @param syncProviders list of additional synchronous providers to be set
         * @return this instance
         */
        @SuppressWarnings("UnusedReturnValue")
        public Builder syncProviders(List<ICrashSyncProvider> syncProviders) {
            this.syncProviders = syncProviders;
            return this;
        }

        /**
         * Sets a list of additional asynchronous providers.
         *
         * @param asyncProviders list of additional asynchronous providers to be set
         * @return this instance
         */
        @SuppressWarnings("UnusedReturnValue")
        public Builder asyncProviders(List<ICrashAsyncProvider> asyncProviders) {
            this.asyncProviders = asyncProviders;
            return this;
        }

        /**
         * Sets a list of additional publishers.
         *
         * @param publishers list of additional publishers to be set.
         * @return this instance.
         */
        @SuppressWarnings("UnusedReturnValue")
        public Builder publishers(List<ICrashPublisher> publishers) {
            this.crashPublishers = publishers;
            return this;
        }

        /**
         * Disables the default memory heap provider.
         *
         * @return this instance
         */
        @SuppressWarnings("UnusedReturnValue")
        public Builder disableMemoryHeapProvider() {
            this.disableMemoryHeapProvider = true;
            return this;
        }

        /**
         * Disables the default stacktrace (crashing thread) provider.
         *
         * @return this instance
         */
        @SuppressWarnings("UnusedReturnValue")
        public Builder disableStackTraceCrashingThread() {
            this.disableStackTraceCrashingThread = true;
            return this;
        }

        /**
         * Disables the default stacktrace (all threads) provider.
         *
         * @return this instance
         */
        @SuppressWarnings("UnusedReturnValue")
        public Builder disableStackTraceAllThreads() {
            this.disableStackTraceAllThreads = true;
            return this;
        }

        /**
         * Disables the default value of for cap publisher.
         *
         * @return this instance.
         */
        @SuppressWarnings("UnusedReturnValue")
        public Builder disableCapPublisher() {
            this.disableCapPublisher = true;
            return this;
        }

        /**
         * Sets the publishing strategy.
         *
         * @param strategy publishing strategy to be set
         * @return this instance
         */
        public Builder strategy(Strategy strategy) {
            this.strategy = strategy;
            return this;
        }

        /**
         * Sets the maximal number of publishing attempts.
         *
         * @param maxAttempts maximal attempts to set
         * @return this instance
         */
        public Builder maxAttempts(int maxAttempts) {
            this.maxAttempts = maxAttempts;
            return this;
        }

        /**
         * Enforces delay between publishing attempts.
         *
         * @param delay delay to wait in between the attempts
         * @return this instance
         */
        public Builder delay(long delay) {
            this.delay = delay;
            return this;
        }

        /**
         * Sets whether no connection is suitable for crash reporting.
         *
         * @param offlineSuitable whether no connection is suitable for crash reporting
         * @return this instance
         */
        public Builder offlineSuitable(boolean offlineSuitable) {
            this.offlineSuitable = offlineSuitable;
            return this;
        }

        /**
         * Sets connection level which is suitable for crash reporting.
         *
         * @param connectionLevelSuitable connection level which is suitable for crash reporting
         * @return this instance
         */
        public Builder connectionLevelSuitable(Connection connectionLevelSuitable) {
            this.connectionLevelSuitable = connectionLevelSuitable;
            return this;
        }

        /**
         * Sets whether roaming connection is suitable for crash reporting.
         *
         * @param roamingSuitable whether no connection is suitable for crash reporting
         * @return this instance
         */
        public Builder roamingSuitable(boolean roamingSuitable) {
            this.roamingSuitable = roamingSuitable;
            return this;
        }

        /**
         * Builds configuration.
         *
         * @return CrashLib configuration
         */
        public CrashLibConfig build() {
            return new CrashLibConfig(this);
        }

    }

}
