package allen.town.podcast.core.service.download;

import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import allen.town.podcast.model.feed.Feed;
import allen.town.podcast.core.util.URLChecker;
import allen.town.podcast.model.feed.FeedMedia;

public class DownloadRequest implements Parcelable {
    public static final String REQUEST_ARG_PAGE_NR = "page";
    public static final String REQUEST_ARG_LOAD_ALL_PAGES = "loadAllPages";

    private final String destination;
    private final String source;
    private final String title;
    private String username;
    private String password;
    private String lastModified;
    private final boolean deleteOnFailure;
    private final long feedfileId;
    private final int feedfileType;
    private final Bundle arguments;

    private int progressPercent;
    private long soFar;
    private long size;
    private int statusMsg;
    private boolean mediaEnqueued;
    private boolean initiatedByUser;

    public boolean isNeedAutoSubscribe() {
        return needAutoSubscribe;
    }

    public void setNeedAutoSubscribe(boolean needAutoSubscribe) {
        this.needAutoSubscribe = needAutoSubscribe;
    }

    private boolean needAutoSubscribe;

    public String getItunesFeedId() {
        return itunesFeedId;
    }

    public void setItunesFeedId(String itunesFeedId) {
        this.itunesFeedId = itunesFeedId;
    }

    private String itunesFeedId;

    public DownloadRequest(@NonNull String destination, @NonNull String source, @NonNull String title, long feedfileId,
                           int feedfileType, String username, String password, boolean deleteOnFailure,
                           Bundle arguments, boolean initiatedByUser) {
        this(destination, source, title, feedfileId, feedfileType, null, deleteOnFailure, username, password, false,
             arguments, initiatedByUser,false,"");
    }

    private DownloadRequest(Builder builder) {
        this(builder.destination, builder.source, builder.title, builder.feedfileId, builder.feedfileType,
             builder.lastModified, builder.deleteOnFailure, builder.username, builder.password, false,
             builder.arguments != null ? builder.arguments : new Bundle(), builder.initiatedByUser,builder.needAutoSubscribe,builder.itunesFeedId);
    }

    private DownloadRequest(Parcel in) {
        this(in.readString(), in.readString(), in.readString(), in.readLong(), in.readInt(), in.readString(),
             in.readByte() > 0, nullIfEmpty(in.readString()), nullIfEmpty(in.readString()), in.readByte() > 0,
             in.readBundle(), in.readByte() > 0,in.readByte() > 0,in.readString());
    }

    private DownloadRequest(String destination, String source, String title, long feedfileId, int feedfileType,
                            String lastModified, boolean deleteOnFailure, String username, String password,
                            boolean mediaEnqueued, Bundle arguments, boolean initiatedByUser, boolean needAutoSubscribe,String itunesFeedId) {
        this.destination = destination;
        this.source = source;
        this.title = title;
        this.feedfileId = feedfileId;
        this.feedfileType = feedfileType;
        this.lastModified = lastModified;
        this.deleteOnFailure = deleteOnFailure;
        this.username = username;
        this.password = password;
        this.mediaEnqueued = mediaEnqueued;
        this.arguments = arguments;
        this.initiatedByUser = initiatedByUser;
        this.needAutoSubscribe = needAutoSubscribe;
        this.itunesFeedId = itunesFeedId;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(destination);
        dest.writeString(source);
        dest.writeString(title);
        dest.writeLong(feedfileId);
        dest.writeInt(feedfileType);
        dest.writeString(lastModified);
        dest.writeByte((deleteOnFailure) ? (byte) 1 : 0);
        // in case of null username/password, still write an empty string
        // (rather than skipping it). Otherwise, unmarshalling  a collection
        // of them from a Parcel (from an Intent extra to submit a request to DownloadService) will fail.
        //
        // see: https://stackoverflow.com/a/22926342
        dest.writeString(nonNullString(username));
        dest.writeString(nonNullString(password));
        dest.writeByte((mediaEnqueued) ? (byte) 1 : 0);
        dest.writeBundle(arguments);
        dest.writeByte(initiatedByUser ? (byte) 1 : 0);
        dest.writeByte(needAutoSubscribe ? (byte) 1 : 0);
        dest.writeString(itunesFeedId);
    }

    private static String nonNullString(String str) {
        return str != null ? str : "";
    }

    private static String nullIfEmpty(String str) {
        return TextUtils.isEmpty(str) ? null : str;
    }

    public static final Parcelable.Creator<DownloadRequest> CREATOR = new Parcelable.Creator<DownloadRequest>() {
        public DownloadRequest createFromParcel(Parcel in) {
            return new DownloadRequest(in);
        }

        public DownloadRequest[] newArray(int size) {
            return new DownloadRequest[size];
        }
    };


    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof DownloadRequest)) return false;

        DownloadRequest that = (DownloadRequest) o;

        if (lastModified != null ? !lastModified.equals(that.lastModified) : that.lastModified != null)
            return false;
        if (deleteOnFailure != that.deleteOnFailure) return false;
        if (feedfileId != that.feedfileId) return false;
        if (feedfileType != that.feedfileType) return false;
        if (progressPercent != that.progressPercent) return false;
        if (size != that.size) return false;
        if (soFar != that.soFar) return false;
        if (statusMsg != that.statusMsg) return false;
        if (!destination.equals(that.destination)) return false;
        if (password != null ? !password.equals(that.password) : that.password != null)
            return false;
        if (!source.equals(that.source)) return false;
        if (title != null ? !title.equals(that.title) : that.title != null) return false;
        if (username != null ? !username.equals(that.username) : that.username != null)
            return false;
        if (mediaEnqueued != that.mediaEnqueued) return false;
        if (initiatedByUser != that.initiatedByUser) return false;
        return true;
    }

    @Override
    public int hashCode() {
        int result = destination.hashCode();
        result = 31 * result + source.hashCode();
        result = 31 * result + (title != null ? title.hashCode() : 0);
        result = 31 * result + (username != null ? username.hashCode() : 0);
        result = 31 * result + (password != null ? password.hashCode() : 0);
        result = 31 * result + (lastModified != null ? lastModified.hashCode() : 0);
        result = 31 * result + (deleteOnFailure ? 1 : 0);
        result = 31 * result + (int) (feedfileId ^ (feedfileId >>> 32));
        result = 31 * result + feedfileType;
        result = 31 * result + arguments.hashCode();
        result = 31 * result + progressPercent;
        result = 31 * result + (int) (soFar ^ (soFar >>> 32));
        result = 31 * result + (int) (size ^ (size >>> 32));
        result = 31 * result + statusMsg;
        result = 31 * result + (mediaEnqueued ? 1 : 0);
        return result;
    }

    public String getDestination() {
        return destination;
    }

    public String getSource() {
        return source;
    }

    public String getTitle() {
        return title;
    }

    public long getFeedfileId() {
        return feedfileId;
    }

    public int getFeedfileType() {
        return feedfileType;
    }

    public int getProgressPercent() {
        return progressPercent;
    }

    public void setProgressPercent(int progressPercent) {
        this.progressPercent = progressPercent;
    }

    public long getSoFar() {
        return soFar;
    }

    public void setSoFar(long soFar) {
        this.soFar = soFar;
    }

    public long getSize() {
        return size;
    }

    public void setSize(long size) {
        this.size = size;
    }

    public void setStatusMsg(int statusMsg) {
        this.statusMsg = statusMsg;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public DownloadRequest setLastModified(@Nullable String lastModified) {
        this.lastModified = lastModified;
        return this;
    }

    @Nullable
    public String getLastModified() {
        return lastModified;
    }

    public boolean isDeleteOnFailure() {
        return deleteOnFailure;
    }

    public boolean isMediaEnqueued() {
        return mediaEnqueued;
    }

    public boolean isInitiatedByUser() {
        return initiatedByUser;
    }

    /**
     * Set to true if the media is enqueued because of this download.
     * The state is helpful if the download is cancelled, and undoing the enqueue is needed.
     */
    public void setMediaEnqueued(boolean mediaEnqueued) {
        this.mediaEnqueued = mediaEnqueued;
    }

    public Bundle getArguments() {
        return arguments;
    }

    public static class Builder {
        private final String destination;
        private final String source;
        private final String title;
        private String username;
        private String password;
        private String lastModified;
        private boolean deleteOnFailure = false;
        private final long feedfileId;
        private final int feedfileType;
        private Bundle arguments = new Bundle();
        private boolean initiatedByUser = true;
        private boolean needAutoSubscribe = false;
        private String itunesFeedId;

        public Builder(@NonNull String destination, @NonNull FeedMedia media) {
            this.destination = destination;
            this.source = URLChecker.prepareURL(media.getDownload_url());
            this.title = media.getHumanReadableIdentifier();
            this.feedfileId = media.getId();
            this.feedfileType = media.getTypeAsInt();
        }

        public Builder(@NonNull String destination, @NonNull Feed feed) {
            this.destination = destination;
            this.source = feed.isLocalFeed() ? feed.getDownload_url() : URLChecker.prepareURL(feed.getDownload_url());
            this.title = feed.getHumanReadableIdentifier();
            this.feedfileId = feed.getId();
            this.feedfileType = feed.getTypeAsInt();
            this.needAutoSubscribe = feed.isNeedAutoSubscribe();
            this.itunesFeedId = feed.getItunesId();
            arguments.putInt(REQUEST_ARG_PAGE_NR, feed.getPageNr());
        }

        public Builder withInitiatedByUser(boolean initiatedByUser) {
            this.initiatedByUser = initiatedByUser;
            return this;
        }

        public void setForce(boolean force) {
            if (force) {
                lastModified = null;
            }
        }

        public Builder deleteOnFailure(boolean deleteOnFailure) {
            this.deleteOnFailure = deleteOnFailure;
            return this;
        }

        public Builder lastModified(String lastModified) {
            this.lastModified = lastModified;
            return this;
        }

        public Builder withAuthentication(String username, String password) {
            this.username = username;
            this.password = password;
            return this;
        }

        public void loadAllPages(boolean loadAllPages) {
            if (loadAllPages) {
                arguments.putBoolean(REQUEST_ARG_LOAD_ALL_PAGES, true);
            }
        }

        public DownloadRequest build() {
            return new DownloadRequest(this);
        }
    }
}
