package ab.issue.model;

import android.arch.persistence.room.ColumnInfo;
import android.arch.persistence.room.Entity;
import android.arch.persistence.room.Index;
import android.arch.persistence.room.PrimaryKey;
import android.arch.persistence.room.TypeConverters;
import android.content.Context;
import android.support.annotation.NonNull;
import android.text.TextUtils;

import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
import com.google.common.collect.Ordering;
import com.google.firebase.ml.vision.barcode.FirebaseVisionBarcode;
import com.google.zxing.Result;
import com.google.zxing.client.result.ParsedResult;
import com.google.zxing.client.result.ResultParser;

import org.jetbrains.annotations.NotNull;
import org.joda.time.DateTime;

import java.util.Collections;
import java.util.Comparator;

import javax.annotation.Nonnull;

import ab.issue.db.DbConstants;
import ab.issue.model.formats.BarcodeFormat;
import ab.issue.model.formats.UnknownBarcodeFormat;

import static com.google.common.base.Optional.fromNullable;

@SuppressWarnings("Guava")
@Entity(
        tableName = DbConstants.BARCODE_TABLE_NAME,
        indices = {
                @Index(value = DbConstants.COLUMN_NAME_RAW_VALUE, unique = true)
        }
)
public class Barcode {

    public static final Predicate<Barcode> HAS_IMAGE = input -> !TextUtils.isEmpty(input.getImagePath());

    private static Comparator<Barcode> TIME_ASC = (lhs, rhs) -> Ordering.natural().compare(lhs.getLastUpdated(), rhs.getLastUpdated());
    public static Comparator<Barcode> TIME_DESC = Collections.reverseOrder(TIME_ASC);

    public static Barcode create(FirebaseVisionBarcode firebaseVisionBarcode) {
        Barcode barcode = new Barcode();
        barcode.displayValue = fromNullable(firebaseVisionBarcode.getDisplayValue()).or("").trim();
        barcode.rawValue = fromNullable(firebaseVisionBarcode.getRawValue()).or("");
        barcode.format = BarcodeFormat.create(firebaseVisionBarcode);
        return barcode;
    }

    public static Barcode create(Result result) {
        Barcode barcode = new Barcode();
        barcode.rawValue = fromNullable(result.getText()).or("");
        ParsedResult parsedResult = ResultParser.parseResult(result);
        barcode.displayValue = fromNullable(parsedResult.getDisplayResult()).or("").trim();
        barcode.format = BarcodeFormat.create(result, parsedResult);
        return barcode;
    }

    @ColumnInfo(name = DbConstants.COLUMN_NAME_BARCODE_ID)
    @PrimaryKey(autoGenerate = true)
    private long id;

    @ColumnInfo(name = DbConstants.COLUMN_NAME_NOTES)
    private String notes;

    @Nonnull
    @ColumnInfo(name = DbConstants.COLUMN_NAME_RAW_VALUE)
    private String rawValue = "";

    @Nonnull
    @ColumnInfo(name = DbConstants.COLUMN_NAME_DISPLAY_VALUE)
    private String displayValue = "";

    @Nonnull
    @ColumnInfo(name = DbConstants.COLUMN_NAME_FORMAT)
    @TypeConverters(BarcodeFormat.class)
    private BarcodeFormat format = new UnknownBarcodeFormat();

    @ColumnInfo(name = DbConstants.COLUMN_NAME_FAVORITE)
    private boolean isFavorite;

    @ColumnInfo(name = DbConstants.COLUMN_NAME_IMAGE_PATH)
    private String imagePath;

    @ColumnInfo(name = DbConstants.COLUMN_NAME_CREATE_TIME)
    private DateTime createTime;

    @ColumnInfo(name = DbConstants.COLUMN_NAME_LAST_UPDATED)
    private DateTime lastUpdated;

    @Override
    public int hashCode() {
        return Objects.hashCode(id);
    }

    @Override
    public boolean equals(Object obj) {
        return this == obj || obj instanceof Barcode && Objects.equal(id, ((Barcode) obj).id);
    }

    @NotNull
    @Override
    public String toString() {
        return MoreObjects.toStringHelper(this)
                .omitNullValues()
                .add("id", id)
                .add("notes", notes)
                .add("rawValue", rawValue)
                .add("displayValue", displayValue)
                .add("format", format)
                .add("isFavorite", isFavorite)
                .add("imagePath", imagePath)
                .add("lastUpdated", lastUpdated)
                .add("createTime", createTime)
                .toString();
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public DateTime getCreateTime() {
        return createTime;
    }

    public void setCreateTime(DateTime createTime) {
        this.createTime = createTime;
    }

    public DateTime getLastUpdated() {
        return lastUpdated;
    }

    public void setLastUpdated(DateTime lastUpdated) {
        this.lastUpdated = lastUpdated;
    }

    public String getNotes() {
        return notes;
    }

    public void setNotes(String notes) {
        this.notes = notes;
    }

    @NonNull
    public String getRawValue() {
        return rawValue;
    }

    public void setRawValue(@NonNull String rawValue) {
        this.rawValue = rawValue;
    }

    @NonNull
    public String getDisplayValue() {
        return displayValue;
    }

    public void setDisplayValue(@NonNull String displayValue) {
        this.displayValue = displayValue;
    }

    @NonNull
    public BarcodeFormat getFormat() {
        return format;
    }

    public void setFormat(@NonNull BarcodeFormat format) {
        this.format = format;
    }

    public boolean isFavorite() {
        return isFavorite;
    }

    public void setFavorite(boolean favorite) {
        isFavorite = favorite;
    }

    public String getImagePath() {
        return imagePath;
    }

    public void setImagePath(String imagePath) {
        this.imagePath = imagePath;
    }

    public String toShareableText() {
        // TODO
        return null;
    }

    public String getNotesOrFormat(Context context) {
        return TextUtils.isEmpty(notes) ? getFormat().getValue().getName(context) : notes;
    }
}
