package ab.issue.model.values;

import android.content.Context;
import android.support.annotation.DrawableRes;

import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.firebase.ml.vision.barcode.FirebaseVisionBarcode;
import com.google.zxing.client.result.AddressBookParsedResult;
import com.google.zxing.client.result.CalendarParsedResult;
import com.google.zxing.client.result.EmailAddressParsedResult;
import com.google.zxing.client.result.GeoParsedResult;
import com.google.zxing.client.result.ParsedResult;
import com.google.zxing.client.result.SMSParsedResult;
import com.google.zxing.client.result.TelParsedResult;
import com.google.zxing.client.result.URIParsedResult;
import com.google.zxing.client.result.WifiParsedResult;

import java.util.List;

import ab.issue.db.BarcodeDatabase;
import ab.issue.model.Barcode;
import androidx.navigation.NavController;

@SuppressWarnings("Guava")
public interface BarcodeFormatValue {

    BarcodeFormatValueType getType();

    String getName(Context context);

    String encode();

    @DrawableRes
    int getIcon();

    void setBarcodeId(long id);

    void save(BarcodeDatabase barcodeDatabase);

    void create(NavController navController);

    List<Barcode> search(BarcodeDatabase barcodeDatabase, String query);

    static List<BarcodeFormatValue> getAll() {
        return FluentIterable.from(BarcodeFormatValueType.values())
                .transform(BarcodeFormatValue::fromType)
                .toList();
    }

    static BarcodeFormatValue fromType(BarcodeFormatValueType type) {
        switch (type) {
            default:
            case UNKNOWN:
                return new UnknownBarcodeFormatValue();
            case CONTACT:
                return new ContactInfoBarcodeFormat();
            case EMAIL:
                return new EmailBarcodeFormat(new Email());
            case PHONE:
                return new PhoneBarcodeFormat(new Phone());
            case PRODUCT:
                return new ProductBarcodeType();
            case SMS:
                return new SmsBarcodeFormat();
            case TEXT:
                return new TextBarcodeFormat();
            case URL:
                return new UrlBarcodeFormat(new UrlBookmark());
            case WIFI:
                return new WifiBarcodeFormat();
            case GEO:
                return new GeoBarcodeFormat();
            case CALENDAR_EVENT:
                return new CalendarEventBarcodeFormat();
        }
    }

    static BarcodeFormatValue create(FirebaseVisionBarcode firebaseVisionBarcode) {
        switch (firebaseVisionBarcode.getValueType()) {
            case FirebaseVisionBarcode.TYPE_CONTACT_INFO:
                FirebaseVisionBarcode.ContactInfo contactInfo = firebaseVisionBarcode.getContactInfo();
                return ContactInfoBarcodeFormat.create(contactInfo);
            case FirebaseVisionBarcode.TYPE_EMAIL:
                return EmailBarcodeFormat.create(firebaseVisionBarcode.getEmail());
            case FirebaseVisionBarcode.TYPE_PHONE:
                return PhoneBarcodeFormat.create(firebaseVisionBarcode.getPhone());
            case FirebaseVisionBarcode.TYPE_PRODUCT:
                return new ProductBarcodeType();
            case FirebaseVisionBarcode.TYPE_SMS:
                return SmsBarcodeFormat.create(firebaseVisionBarcode.getSms());
            case FirebaseVisionBarcode.TYPE_TEXT:
                return new TextBarcodeFormat();
            case FirebaseVisionBarcode.TYPE_URL:
                return UrlBarcodeFormat.create(firebaseVisionBarcode.getUrl());
            case FirebaseVisionBarcode.TYPE_WIFI:
                return WifiBarcodeFormat.create(firebaseVisionBarcode.getWifi());
            case FirebaseVisionBarcode.TYPE_GEO:
                return GeoBarcodeFormat.create(firebaseVisionBarcode.getGeoPoint());
            case FirebaseVisionBarcode.TYPE_CALENDAR_EVENT:
                return CalendarEventBarcodeFormat.create(firebaseVisionBarcode.getCalendarEvent());
            case FirebaseVisionBarcode.TYPE_UNKNOWN:
            case FirebaseVisionBarcode.TYPE_DRIVER_LICENSE:
            case FirebaseVisionBarcode.TYPE_ISBN:
            default:
                return null;
        }
    }

    static BarcodeFormatValue create(ParsedResult parsedResult) {
        switch (parsedResult.getType()) {
            case ADDRESSBOOK:
                AddressBookParsedResult result = (AddressBookParsedResult) parsedResult;
                return ContactInfoBarcodeFormat.create(result);
            case EMAIL_ADDRESS:
                EmailAddressParsedResult emailResult = (EmailAddressParsedResult) parsedResult;
                return EmailBarcodeFormat.create(emailResult);
            case PRODUCT:
                return new ProductBarcodeType();
            case URI:
                URIParsedResult uriParsedResult = (URIParsedResult) parsedResult;
                return UrlBarcodeFormat.create(uriParsedResult);
            case TEXT:
                return new TextBarcodeFormat();
            case GEO:
                GeoParsedResult geoParsedResult = (GeoParsedResult) parsedResult;
                return GeoBarcodeFormat.create(geoParsedResult);
            case TEL:
                TelParsedResult telParsedResult = (TelParsedResult) parsedResult;
                return PhoneBarcodeFormat.create(telParsedResult);
            case SMS:
                SMSParsedResult smsParsedResult = (SMSParsedResult) parsedResult;
                return SmsBarcodeFormat.create(smsParsedResult);
            case CALENDAR:
                CalendarParsedResult calendarParsedResult = (CalendarParsedResult) parsedResult;
                return CalendarEventBarcodeFormat.create(calendarParsedResult);
            case WIFI:
                WifiParsedResult wifiParsedResult = (WifiParsedResult) parsedResult;
                return WifiBarcodeFormat.create(wifiParsedResult);
            // TODO: support ISBN as it is common between firebase and zxing
            case ISBN:
            case VIN:
            default:
                return null;
        }
    }
    Predicate<BarcodeFormatValue> KNOWN = input -> {
        switch (input.getType()) {
            default:
            case UNKNOWN:
            case PRODUCT:
                return false;
            case TEXT:
            case CONTACT:
            case EMAIL:
            case PHONE:
            case SMS:
            case URL:
            case WIFI:
            case GEO:
            case CALENDAR_EVENT:
                return true;
        }
    };

    Predicate<BarcodeFormatValue> NOT_TEXT = input -> input.getType() != BarcodeFormatValueType.TEXT;
}
