package com.marverenic.music.model.playlistrules;
import android.annotation.SuppressLint;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.IntDef;
import com.google.gson.TypeAdapter;
import com.google.gson.annotations.SerializedName;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.marverenic.music.data.store.MusicStore;
import com.marverenic.music.data.store.PlayCountStore;
import com.marverenic.music.data.store.PlaylistStore;
import com.marverenic.music.model.Song;
import java.io.IOException;
import java.util.List;
import rx.Observable;
public abstract class AutoPlaylistRule implements Parcelable {
public static final int PLAYLIST = 0;
public static final int SONG = 1;
public static final int ARTIST = 2;
public static final int ALBUM = 3;
public static final int GENRE = 4;
public static final int ID = 5;
public static final int NAME = 6;
public static final int PLAY_COUNT = 7;
public static final int SKIP_COUNT = 8;
public static final int YEAR = 9;
public static final int DATE_ADDED = 10;
public static final int DATE_PLAYED = 11;
public static final int EQUALS = 12;
public static final int NOT_EQUALS = 13;
public static final int CONTAINS = 14;
public static final int NOT_CONTAINS = 15;
public static final int LESS_THAN = 16;
public static final int GREATER_THAN = 17;
@IntDef(value = {PLAYLIST, SONG, ARTIST, ALBUM, GENRE})
public @interface Type {
}
@IntDef(value = {ID, NAME, PLAY_COUNT, SKIP_COUNT, YEAR, DATE_ADDED, DATE_PLAYED})
public @interface Field {
}
@IntDef(value = {EQUALS, NOT_EQUALS, CONTAINS, NOT_CONTAINS, LESS_THAN, GREATER_THAN})
public @interface Match {
}
@Type
@SerializedName("type")
private final int mType;
@Field
@SerializedName("field")
private final int mField;
@Match
@SerializedName("match")
private final int mMatch;
@SerializedName("value")
private final String mValue;
private final long mNumericValue;
protected AutoPlaylistRule(@Type int type, @Field int field, @Match int match, String value) {
mType = type;
mField = field;
mMatch = match;
mValue = value;
mNumericValue = parseNumericValue();
}
@SuppressWarnings("WrongConstant")
protected AutoPlaylistRule(Parcel in) {
mType = in.readInt();
mField = in.readInt();
mMatch = in.readInt();
mValue = in.readString();
mNumericValue = parseNumericValue();
}
private long parseNumericValue() {
try {
return Long.parseLong(getValue());
} catch (NumberFormatException e) {
return 0;
}
}
@Type
public int getType() {
return mType;
}
@Field
public int getField() {
return mField;
}
@Match
public int getMatch() {
return mMatch;
}
@SuppressLint("SwitchIntDef")
protected boolean checkId(long actual) {
switch (getMatch()) {
case EQUALS:
return actual == mNumericValue;
case NOT_EQUALS:
return actual != mNumericValue;
}
throw new IllegalArgumentException("Cannot compare ids with match type " + getMatch());
}
@SuppressLint("SwitchIntDef")
protected boolean checkString(String actual) {
switch (getMatch()) {
case EQUALS:
return actual.equalsIgnoreCase(getValue());
case NOT_EQUALS:
return !actual.equalsIgnoreCase(getValue());
case CONTAINS:
return actual.toLowerCase().contains(getValue().toLowerCase());
case NOT_CONTAINS:
return !actual.toLowerCase().contains(getValue().toLowerCase());
}
throw new IllegalArgumentException("Cannot compare Strings with match type " + getMatch());
}
@SuppressLint("SwitchIntDef")
protected boolean checkInt(long actual) {
switch (getMatch()) {
case EQUALS:
return actual == mNumericValue;
case NOT_EQUALS:
return actual != mNumericValue;
case LESS_THAN:
return actual < mNumericValue;
case GREATER_THAN:
return actual > mNumericValue;
}
throw new IllegalArgumentException("Cannot compare integers with match type" + getMatch());
}
public String getValue() {
return mValue;
}
public abstract Observable<List<Song>> applyFilter(PlaylistStore playlistStore,
MusicStore musicStore,
PlayCountStore playCountStore);
@SuppressWarnings("SimplifiableIfStatement")
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (other == null || getClass() != other.getClass()) return false;
AutoPlaylistRule that = (AutoPlaylistRule) other;
if (mType != that.mType) return false;
if (mField != that.mField) return false;
if (mMatch != that.mMatch) return false;
return mValue != null ? mValue.equals(that.mValue) : that.mValue == null;
}
@Override
public int hashCode() {
int result = mType;
result = 31 * result + mField;
result = 31 * result + mMatch;
result = 31 * result + (mValue != null ? mValue.hashCode() : 0);
return result;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(mType);
parcel.writeInt(mField);
parcel.writeInt(mMatch);
parcel.writeString(mValue);
}
public static final Creator<AutoPlaylistRule> CREATOR = new Creator<AutoPlaylistRule>() {
@Override
public AutoPlaylistRule createFromParcel(Parcel in) {
//noinspection WrongConstant
return new Factory()
.setType(in.readInt())
.setField(in.readInt())
.setMatch(in.readInt())
.setValue(in.readString())
.build();
}
@Override
public AutoPlaylistRule[] newArray(int size) {
return new AutoPlaylistRule[size];
}
};
public static final class RuleTypeAdapter extends TypeAdapter<AutoPlaylistRule> {
@Override
public void write(JsonWriter out, AutoPlaylistRule rule) throws IOException {
out.beginObject();
out.name("type").value(rule.getType());
out.name("match").value(rule.getMatch());
out.name("field").value(rule.getField());
out.name("value").value(rule.getValue());
out.endObject();
}
@SuppressWarnings("WrongConstant")
@Override
public AutoPlaylistRule read(JsonReader in) throws IOException {
Factory factory = new Factory();
in.beginObject();
while (in.hasNext()) {
switch (in.nextName()) {
case "type":
factory.setType(in.nextInt());
break;
case "match":
factory.setMatch(in.nextInt());
break;
case "field":
factory.setField(in.nextInt());
break;
case "value":
factory.setValue(in.nextString());
}
}
in.endObject();
return factory.build();
}
}
public static class Factory {
@Type private int mType;
@Field private int mField;
@Match private int mMatch;
private String mValue;
public Factory() {
}
public Factory(AutoPlaylistRule from) {
mType = from.getType();
mField = from.getField();
mMatch = from.getMatch();
mValue = from.getValue();
}
@Type
public int getType() {
return mType;
}
public Factory setType(@Type int type) {
mType = type;
return this;
}
@Field
public int getField() {
return mField;
}
public Factory setField(@Field int field) {
mField = field;
return this;
}
@Match
public int getMatch() {
return mMatch;
}
public Factory setMatch(@Match int match) {
mMatch = match;
return this;
}
public String getValue() {
return mValue;
}
public Factory setValue(String value) {
mValue = value;
return this;
}
public AutoPlaylistRule build() {
switch (mType) {
case PLAYLIST:
return new PlaylistRule(mField, mMatch, mValue);
case SONG:
return new SongRule(mField, mMatch, mValue);
case ALBUM:
return new AlbumRule(mField, mMatch, mValue);
case ARTIST:
return new ArtistRule(mField, mMatch, mValue);
case GENRE:
return new GenreRule(mField, mMatch, mValue);
}
throw new IllegalArgumentException("Cannot construct rule over type " + mType);
}
}
}