/*
 * =================================================================================================
 *                             Copyright (C) 2014 Martin Albedinsky
 * =================================================================================================
 *         Licensed under the Apache License, Version 2.0 or later (further "License" only).
 * -------------------------------------------------------------------------------------------------
 * You may use this file only in compliance with the License. More details and copy of this License
 * you may obtain at
 *
 * 		http://www.apache.org/licenses/LICENSE-2.0
 *
 * You can redistribute, modify or publish any part of the code written within this file but as it
 * is described in the License, the software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES or CONDITIONS OF ANY KIND.
 *
 * See the License for the specific language governing permissions and limitations under the License.
 * =================================================================================================
 */
package com.albedinsky.android.dialog;

import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.AttrRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.annotation.StyleRes;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.albedinsky.android.dialog.R;
import com.albedinsky.android.dialog.view.ProgressDialogView;

/**
 * A {@link SimpleDialog} implementation that can be used to show a linear progress bar with
 * content text. This dialog uses default {@link ProgressDialogView} implementation as its content
 * view which consists of {@link com.albedinsky.android.ui.widget.LinearProgressBar LinearProgressBar}
 * with progress and time indicators (that are optional) and content above the progress bar.
 * <p>
 * Current progress for progress bar and also indicator (only shows text value of progress) can be
 * specified via {@link #setProgress(int...)} and the current value can be obtained via {@link #getProgress()}.
 * You can also change the content text dynamically via {@link #setContent(CharSequence)} or its
 * resource equivalent {@link #setContent(int)}.
 * <p>
 * See {@link ProgressDialog.ProgressOptions} for options that can be supplied to the ProgressDialog
 * from the desired context via {@link #newInstance(ProgressDialog.ProgressOptions)} or via
 * {@link #setOptions(DialogOptions)}.
 * <p>
 * <b>Note</b>, that current state of this dialog will be always preserved after orientation change,
 * as all dialog views from this library save theirs current state for that matter.
 * <p>
 * <h3>XML options</h3>
 * This dialog parses its global options from the current dialog theme referenced via
 * {@link R.attr#dialogProgressOptions dialogProgressOptions} attribute. This attribute should reference
 * to a style that can contain following attributes, including attributes of the parent dialog:
 * <ul>
 * <li>{@link R.attr#dialogProgressFormat dialogProgressFormat}</li>
 * <li>{@link R.attr#dialogTimeFormat dialogTimeFormat}</li>
 * <li>{@link R.attr#dialogProgressIndicator dialogProgressIndicator}</li>
 * </ul>
 *
 * @author Martin Albedinsky
 */
public class ProgressDialog extends SimpleDialog implements ProgressBarDialog {

    /**
     * Interface ===================================================================================
     */

    /**
     * Constants ===================================================================================
     */

    /**
     * Log TAG.
     */
    // private static final String TAG = "ProgressDialog";

    /**
     * Static members ==============================================================================
     */

    /**
     * Members =====================================================================================
     */

    /**
     * Progress dialog view implementation.
     */
    private ProgressDialogView mProgressView;

    /**
     * Constructors ================================================================================
     */

    /**
     * Creates a new instance of ProgressDialog with single horizontal {@link android.widget.ProgressBar}.
     * <p>
     * Options style attribute: {@link R.attr#dialogProgressOptions dialogProgressOptions}.
     */
    public ProgressDialog() {
        super(R.attr.dialogProgressOptions);
    }

    /**
     * Methods =====================================================================================
     */

    /**
     * Creates a new instance of ProgressDialog with the specified options.
     *
     * @param options Specific options for this type of dialog.
     * @return New ProgressDialog instance.
     */
    @NonNull
    public static ProgressDialog newInstance(@NonNull ProgressOptions options) {
        final ProgressDialog dialog = new ProgressDialog();
        dialog.setOptions(options);
        return dialog;
    }

    /**
     */
    @NonNull
    @Override
    protected DialogOptions onCreateDefaultOptions(@NonNull Resources resources) {
        return new ProgressOptions(resources);
    }

    /**
     */
    @Override
    @SuppressWarnings("ResourceType")
    protected void onProcessOptionsStyle(@NonNull Context context, @StyleRes int optionsStyle) {
        super.onProcessOptionsStyle(context, optionsStyle);
        if (mOptions instanceof ProgressOptions) {
            final TypedArray optionsArray = context.obtainStyledAttributes(optionsStyle, R.styleable.Dialog_Options_Progress);
            if (optionsArray != null) {
                final ProgressOptions options = (ProgressOptions) mOptions;
                final int n = optionsArray.getIndexCount();
                for (int i = 0; i < n; i++) {
                    int index = optionsArray.getIndex(i);
                    if (index == R.styleable.Dialog_Options_Progress_dialogProgressFormat) {
                        if (!options.isSet(ProgressOptions.PROGRESS_FORMAT)) {
                            options.progressFormat(optionsArray.getString(index));
                        }
                    } else if (index == R.styleable.Dialog_Options_Progress_dialogTimeFormat) {
                        if (!options.isSet(ProgressOptions.TIME_FORMAT)) {
                            options.timeFormat(optionsArray.getString(index));
                        }
                    } else if (index == R.styleable.Dialog_Options_Progress_dialogProgressIndicator) {
                        if (!options.isSet(ProgressOptions.PROGRESS_INDICATOR)) {
                            options.progressIndicator(optionsArray.getInteger(index, options.progressIndicator));
                        }
                    } else if (index == R.styleable.Dialog_Options_Progress_dialogProgressMode) {
                        if (!options.isSet(ProgressOptions.PROGRESS_MODE)) {
                            options.progressMode(optionsArray.getInteger(index, options.progressMode));
                        }
                    }
                }
                optionsArray.recycle();
            }
        }
    }

    /**
     */
    @NonNull
    @Override
    protected View onCreateContentView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.dialog_content_progress, container, false);
    }

    /**
     */
    @Override
    void onBindContentView(View contentView, DialogOptions options) {
        super.onBindContentView(contentView, options);
        if (contentView instanceof ProgressDialogView && options instanceof ProgressOptions) {
            final ProgressDialogView progressView = (ProgressDialogView) contentView;
            final ProgressOptions progressOptions = (ProgressOptions) options;
            progressView.setProgressMode(progressOptions.progressMode);
            progressView.setProgressIndicator(progressOptions.progressIndicator);
            if (progressOptions.progressFormat != null) {
                progressView.setProgressFormat(progressOptions.progressFormat);
            }
            if (progressOptions.timeFormat != null) {
                progressView.setTimeFormat(progressOptions.timeFormat);
            }
        }
    }

    /**
     */
    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        final View contentView = getContentView();
        if (contentView instanceof ProgressDialogView) {
            this.mProgressView = (ProgressDialogView) contentView;
        }
    }

    /**
     */
    @Override
    public void setProgress(@NonNull int... progress) {
        if (mProgressView != null) mProgressView.setProgress(progress);
    }

    /**
     */
    @NonNull
    @Override
    public int[] getProgress() {
        return mProgressView != null ? mProgressView.getProgress() : new int[0];
    }

    /**
     */
    @Override
    public void setContent(@StringRes int resId) {
        setContent(getText(resId));
    }

    /**
     */
    @Override
    public void setContent(@Nullable CharSequence content) {
        if (mProgressView != null) mProgressView.setContent(content);
    }

    /**
     * Inner classes ===============================================================================
     */

    /**
     * A {@link DialogOptions} implementation specific for the {@link ProgressDialog}.
     * <p>
     * Below are listed all progress related options that can be supplied to the ProgressDialog
     * through these options:
     * <ul>
     * <li>{@link #progressIndicator(int)}</li>
     * <li>{@link #progressMode(int)}</li>
     * <li>{@link #progressFormat(String)}</li>
     * <li>{@link #progressFormat(int)}</li>
     * <li>{@link #timeFormat(String)}</li>
     * <li>{@link #timeFormat(int)}</li>
     * </ul>
     * <p>
     * <h3>XML attributes</h3>
     * <ul>
     * <li>{@link R.attr#dialogProgressIndicator dialogProgressIndicator}</li>
     * <li>{@link R.attr#dialogProgressMode dialogProgressMode}</li>
     * <li>{@link R.attr#dialogProgressFormat dialogProgressFormat}</li>
     * <li>{@link R.attr#dialogTimeFormat dialogTimeFormat}</li>
     * </ul>
     *
     * @author Martin Albedinsky
     */
    public static class ProgressOptions extends DialogOptions<ProgressOptions> {

        /**
         * Creator used to create an instance or array of instances of ProgressOptions from {@link android.os.Parcel}.
         */
        public static final Parcelable.Creator<ProgressOptions> CREATOR = new Parcelable.Creator<ProgressOptions>() {
            /**
             */
            @Override
            public ProgressOptions createFromParcel(@NonNull Parcel source) {
                return new ProgressOptions(source);
            }

            /**
             */
            @Override
            public ProgressOptions[] newArray(int size) {
                return new ProgressOptions[size];
            }
        };

        /**
         * Flag for <b>progress mode</b> parameter.
         */
        static final int PROGRESS_MODE = 0x00000001 << 16;

        /**
         * Flag for <b>progress indicator</b> parameter.
         */
        static final int PROGRESS_INDICATOR = 0x00000001 << 17;

        /**
         * Flag for <b>progress format</b> parameter.
         */
        static final int PROGRESS_FORMAT = 0x00000001 << 18;

        /**
         * Flag for <b>time format</b> parameter.
         */
        static final int TIME_FORMAT = 0x00000001 << 19;

        /**
         * Progress mode type.
         */
        int progressMode = ProgressDialogView.MODE_DETERMINATE;

        /**
         * Progress indicator type flags.
         */
        int progressIndicator = ProgressDialogView.INDICATOR_PROGRESS | ProgressDialogView.INDICATOR_TIME;

        /**
         * Format for progress value.
         */
        String progressFormat;

        /**
         * Format for time value.
         */
        String timeFormat;

        /**
         * Creates a new instance of ProgressOptions.
         */
        public ProgressOptions() {
            super();
        }

        /**
         * Creates a new instance of ProgressOptions.
         *
         * @param resources Application resources used to obtain values for these options when they
         *                  are requested by theirs resource ids.
         */
        public ProgressOptions(@NonNull Resources resources) {
            super(resources);
        }

        /**
         * Called form {@link #CREATOR} to create an instance of ProgressOptions form the given parcel
         * <var>source</var>.
         *
         * @param source Parcel with data for the new instance.
         */
        protected ProgressOptions(@NonNull Parcel source) {
            super(source);
            this.progressMode = source.readInt();
            this.progressIndicator = source.readInt();
            this.progressFormat = source.readString();
            this.timeFormat = source.readString();
        }

        /**
         */
        @Override
        public void writeToParcel(@NonNull Parcel dest, int flags) {
            super.writeToParcel(dest, flags);
            dest.writeInt(progressMode);
            dest.writeInt(progressIndicator);
            dest.writeString(progressFormat);
            dest.writeString(timeFormat);
        }

        /**
         */
        @Override
        public ProgressOptions merge(@NonNull DialogOptions options) {
            if (!(options instanceof ProgressOptions)) {
                return super.merge(options);
            }
            final ProgressOptions progressOptions = (ProgressOptions) options;
            if (progressOptions.isSet(PROGRESS_MODE)) {
                this.progressMode = progressOptions.progressMode;
                updateIsSet(PROGRESS_MODE);
            }
            if (progressOptions.isSet(PROGRESS_INDICATOR)) {
                this.progressIndicator = progressOptions.progressIndicator;
                updateIsSet(PROGRESS_INDICATOR);
            }
            if (progressOptions.isSet(PROGRESS_FORMAT)) {
                this.progressFormat = progressOptions.progressFormat;
                updateIsSet(PROGRESS_FORMAT);
            }
            if (progressOptions.isSet(TIME_FORMAT)) {
                this.timeFormat = progressOptions.timeFormat;
                updateIsSet(TIME_FORMAT);
            }
            return super.merge(options);
        }

        /**
         * Sets a progress mode in which should operate the progress bar of a dialog associated with
         * these options.
         *
         * @param mode The desired progress mode. One of {@link ProgressDialogView#MODE_DETERMINATE},
         *             {@link ProgressDialogView#MODE_INDETERMINATE}, {@link ProgressDialogView#MODE_BUFFER}
         *             or {@link ProgressDialogView#MODE_QUERY_INDETERMINATE_DETERMINATE}.
         * @return These options to allow methods chaining.
         * @see R.attr#dialogProgressMode dialog:dialogProgressMode
         * @see #progressMode()
         */
        public ProgressOptions progressMode(@ProgressDialogView.Mode int mode) {
            updateIsSet(PROGRESS_MODE);
            if (progressMode != mode) {
                this.progressMode = mode;
                notifyChanged();
            }
            return this;
        }

        /**
         * Returns the progress mode, set to these options.
         * <p>
         * Default value: <b>{@link ProgressDialogView#MODE_DETERMINATE}</b>
         *
         * @return Progress mode.
         * @see #progressMode(int)
         */
        @ProgressDialogView.Mode
        public int progressMode() {
            return progressMode;
        }

        /**
         * Sets a progress indicator type of a dialog associated with these options.
         *
         * @param indicator The desired progress indicator flags. One of {@link ProgressDialogView#INDICATOR_NONE},
         *                  {@link ProgressDialogView#INDICATOR_PROGRESS}, {@link ProgressDialogView#INDICATOR_TIME}
         *                  or theirs combination.
         * @return These options to allow methods chaining.
         * @see R.attr#dialogProgressIndicator dialog:dialogProgressIndicator
         * @see #progressIndicator()
         */
        public ProgressOptions progressIndicator(@ProgressDialogView.Indicator int indicator) {
            updateIsSet(PROGRESS_INDICATOR);
            if (progressIndicator != indicator) {
                this.progressIndicator = indicator;
                notifyChanged();
            }
            return this;
        }

        /**
         * Returns the progress indicator type, set to these options.
         * <p>
         * Default value: <b>{@link ProgressDialogView#INDICATOR_PROGRESS INDICATOR_PROGRESS} | {@link ProgressDialogView#INDICATOR_TIME INDICATOR_TIME}</b>
         *
         * @return Progress indicator flags.
         * @see #progressIndicator(int)
         */
        @ProgressDialogView.Indicator
        public int progressIndicator() {
            return progressIndicator;
        }

        /**
         * Same as {@link #progressFormat(String)} but with resource id.
         *
         * @param resId Resource id of the desired progress format.
         * @see R.attr#dialogProgressFormat dialog:dialogProgressFormat
         * @see #progressFormat()
         */
        public ProgressOptions progressFormat(@StringRes int resId) {
            return progressFormat(string(resId));
        }

        /**
         * Sets the format used when formatting progress value of a dialog associated with these options.
         *
         * @param format The desired progress format.
         * @return These options to allow methods chaining.
         * @see R.attr#dialogProgressFormat dialog:dialogProgressFormat
         * @see #progressFormat()
         */
        public ProgressOptions progressFormat(@Nullable String format) {
            updateIsSet(PROGRESS_FORMAT);
            if (progressFormat == null || !progressFormat.equals(format)) {
                this.progressFormat = format;
                notifyChanged();
            }
            return this;
        }

        /**
         * Returns the progress format, set to these options.
         *
         * @return Progress format.
         * @see #progressFormat(int)
         * @see #progressFormat(String)
         */
        @Nullable
        public String progressFormat() {
            return progressFormat;
        }

        /**
         * Same as {@link #timeFormat(String)} but with resource id.
         *
         * @param resId Resource id of the desired time format.
         * @see R.attr#dialogTimeFormat dialog:dialogTimeFormat
         * @see #timeFormat()
         */
        public ProgressOptions timeFormat(@StringRes int resId) {
            return timeFormat(string(resId));
        }

        /**
         * Sets the format used when formatting time value of a dialog associated with these options.
         *
         * @param format The desired time format.
         * @return These options to allow methods chaining.
         * @see R.attr#dialogTimeFormat dialog:dialogTimeFormat
         * @see #timeFormat()
         */
        public ProgressOptions timeFormat(@Nullable String format) {
            updateIsSet(TIME_FORMAT);
            if (timeFormat == null || !timeFormat.equals(format)) {
                this.timeFormat = format;
                notifyChanged();
            }
            return this;
        }

        /**
         * Returns the time format, set to these options.
         *
         * @return Time format.
         * @see #timeFormat(int)
         * @see #timeFormat(String)
         */
        @Nullable
        public String timeFormat() {
            return timeFormat;
        }

        /**
         */
        @Override
        @SuppressWarnings("ResourceType")
        protected void onParseXmlAttribute(@NonNull XmlResourceParser xmlParser, int index, @AttrRes int attr, @NonNull Resources resources, @Nullable Resources.Theme theme) {
            if (attr == R.attr.dialogProgressMode) {
                progressMode(xmlParser.getAttributeIntValue(index, progressMode));
            } else if (attr == R.attr.dialogProgressIndicator) {
                progressIndicator(xmlParser.getAttributeIntValue(index, progressIndicator));
            } else if (attr == R.attr.dialogProgressFormat) {
                progressFormat(obtainXmlAttributeString(xmlParser, index, resources));
            } else if (attr == R.attr.dialogTimeFormat) {
                timeFormat(obtainXmlAttributeString(xmlParser, index, resources));
            } else {
                super.onParseXmlAttribute(xmlParser, index, attr, resources, theme);
            }
        }
    }
}
