package ab.issue.model.formats;

import android.arch.persistence.room.TypeConverter;
import android.content.Context;

import com.google.common.base.Predicate;
import com.google.firebase.ml.vision.barcode.FirebaseVisionBarcode;
import com.google.zxing.Result;
import com.google.zxing.client.result.ParsedResult;

import java.util.ArrayList;
import java.util.List;

import ab.issue.model.values.BarcodeFormatValue;
import ab.issue.model.values.CalendarEventBarcodeFormat;
import ab.issue.model.values.ContactInfoBarcodeFormat;
import ab.issue.model.values.EmailBarcodeFormat;
import ab.issue.model.values.GeoBarcodeFormat;
import ab.issue.model.values.PhoneBarcodeFormat;
import ab.issue.model.values.SmsBarcodeFormat;
import ab.issue.model.values.TextBarcodeFormat;
import ab.issue.model.values.UrlBarcodeFormat;
import ab.issue.model.values.WifiBarcodeFormat;
import app.issue.commons.java.ValueEnum;

@SuppressWarnings("Guava")
public interface BarcodeFormat {

    BarcodeFormatType getType();

    String getName(Context context);

    BarcodeFormatValue getValue();

    com.google.zxing.BarcodeFormat getZxingBarcodeFormat();

    void setValue(List<CalendarEventBarcodeFormat> calendarEvents,
                  List<ContactInfoBarcodeFormat> contacts,
                  List<EmailBarcodeFormat> emails,
                  List<GeoBarcodeFormat> geo,
                  List<PhoneBarcodeFormat> phones,
                  List<SmsBarcodeFormat> sms,
                  List<UrlBarcodeFormat> urls,
                  List<WifiBarcodeFormat> wifi);

    void setValue(BarcodeFormatValue formatValue);

    @TypeConverter
    static BarcodeFormat toBarcodeFormat(int value) {
        return ab.issue.model.formats.BarcodeFormat.getBarcodeFormat(ValueEnum.fromValue(BarcodeFormatType.values(), value));
    }

    @TypeConverter
    static int toBarcodeFormatTypeValue(BarcodeFormat barcodeFormat) {
        return barcodeFormat.getType().getValue();
    }

    static List<BarcodeFormat> getSupportedFormats() {
        List<BarcodeFormat> result = new ArrayList<>();
        for (BarcodeFormatType type : BarcodeFormatType.values()) {
            if (type == BarcodeFormatType.UNKNOWN) {
                continue;
            }
            BarcodeFormat format = getBarcodeFormat(type);
            if (format.getType() == BarcodeFormatType.UNKNOWN) {
                continue;
            }
            result.add(format);
        }
        return result;
    }

    static BarcodeFormat getBarcodeFormat(BarcodeFormatType type) {
        switch (type) {
            case AZTEC:
                return new AztecBarcodeFormat(new TextBarcodeFormat());
            case CODABAR:
                return new CodabarBarcodeFormat(new TextBarcodeFormat());
            case CODE_128:
                return new Code128BarcodeFormat(new TextBarcodeFormat());
            case CODE_39:
                return new Code39BarcodeFormat(new TextBarcodeFormat());
            case CODE_93:
                return new Code93BarcodeFormat(new TextBarcodeFormat());
            case DATA_MATRIX:
                return new DataMatrixBarcodeFormat(new TextBarcodeFormat());
            case EAN_13:
                return new EAN13BarcodeFormat(new TextBarcodeFormat());
            case EAN_8:
                return new EAN8BarcodeFormat(new TextBarcodeFormat());
            case ITF:
                return new ITFBarcodeFormat(new TextBarcodeFormat());
            case PDF_417:
                return new PDF417BarcodeFormat(new TextBarcodeFormat());
            case QR_CODE:
                return new QRCodeBarcodeFormat(new TextBarcodeFormat());
            case UPC_A:
                return new UPCABarcodeFormat(new TextBarcodeFormat());
            case UPC_E:
                return new UPCEBarcodeFormat(new TextBarcodeFormat());
            case UNKNOWN:
            default:
                return new UnknownBarcodeFormat();
        }
    }

    static BarcodeFormat create(Result result, ParsedResult parsedResult) {
        switch (result.getBarcodeFormat()) {
            case AZTEC:
                return new AztecBarcodeFormat(new TextBarcodeFormat());
            case CODABAR:
                return new CodabarBarcodeFormat(new TextBarcodeFormat());
            case CODE_39:
                return new Code39BarcodeFormat(new TextBarcodeFormat());
            case CODE_93:
                return new Code93BarcodeFormat(new TextBarcodeFormat());
            case CODE_128:
                return new Code128BarcodeFormat(new TextBarcodeFormat());
            case DATA_MATRIX:
                return new DataMatrixBarcodeFormat(new TextBarcodeFormat());
            case EAN_8:
                return new EAN8BarcodeFormat(new TextBarcodeFormat());
            case EAN_13:
                return new EAN13BarcodeFormat(new TextBarcodeFormat());
            case ITF:
                return new ITFBarcodeFormat(new TextBarcodeFormat());
            case PDF_417:
                return new PDF417BarcodeFormat(new TextBarcodeFormat());
            case QR_CODE:
                return new QRCodeBarcodeFormat(BarcodeFormatValue.create(parsedResult));
            case UPC_A:
                return new UPCABarcodeFormat(new TextBarcodeFormat());
            case UPC_E:
                return new UPCEBarcodeFormat(new TextBarcodeFormat());
            // TODO: support this?
            case UPC_EAN_EXTENSION:
            case RSS_EXPANDED:
            case RSS_14:
            case MAXICODE:
            default:
                return new UnknownBarcodeFormat();
        }
    }

    static BarcodeFormat create(FirebaseVisionBarcode firebaseVisionBarcode) {
        switch (firebaseVisionBarcode.getFormat()) {
            case FirebaseVisionBarcode.FORMAT_QR_CODE:
                return new QRCodeBarcodeFormat(BarcodeFormatValue.create(firebaseVisionBarcode));
            case FirebaseVisionBarcode.FORMAT_PDF417:
                return new PDF417BarcodeFormat(new TextBarcodeFormat());
            case FirebaseVisionBarcode.FORMAT_UPC_A:
                return new UPCABarcodeFormat(new TextBarcodeFormat());
            case FirebaseVisionBarcode.FORMAT_UPC_E:
                return new UPCEBarcodeFormat(new TextBarcodeFormat());
            case FirebaseVisionBarcode.FORMAT_ITF:
                return new ITFBarcodeFormat(new TextBarcodeFormat());
            case FirebaseVisionBarcode.FORMAT_DATA_MATRIX:
                return new DataMatrixBarcodeFormat(new TextBarcodeFormat());
            case FirebaseVisionBarcode.FORMAT_EAN_8:
                return new EAN8BarcodeFormat(new TextBarcodeFormat());
            case FirebaseVisionBarcode.FORMAT_EAN_13:
                return new EAN13BarcodeFormat(new TextBarcodeFormat());
            case FirebaseVisionBarcode.FORMAT_CODE_128:
                return new Code128BarcodeFormat(new TextBarcodeFormat());
            case FirebaseVisionBarcode.FORMAT_CODE_93:
                return new Code93BarcodeFormat(new TextBarcodeFormat());
            case FirebaseVisionBarcode.FORMAT_CODE_39:
                return new Code39BarcodeFormat(new TextBarcodeFormat());
            case FirebaseVisionBarcode.FORMAT_CODABAR:
                return new CodabarBarcodeFormat(new TextBarcodeFormat());
            case FirebaseVisionBarcode.FORMAT_AZTEC:
                return new AztecBarcodeFormat(new TextBarcodeFormat());
            case FirebaseVisionBarcode.FORMAT_UNKNOWN:
            default:
                return new UnknownBarcodeFormat();

        }
    }

    Predicate<BarcodeFormat> KNOWN = input -> {
        switch (input.getType()) {
            default:
            case UNKNOWN:
                return false;
            case AZTEC:
            case CODABAR:
            case CODE_128:
            case CODE_39:
            case CODE_93:
            case DATA_MATRIX:
            case EAN_13:
            case EAN_8:
            case ITF:
            case PDF_417:
            case QR_CODE:
            case UPC_A:
            case UPC_E:
                return true;
        }
    };
}
