package it.integry.honeywellscannerlibrary;

import android.content.Context;
import android.os.Build;
import android.util.Pair;
import android.view.KeyEvent;

import com.honeywell.aidc.AidcManager;
import com.honeywell.aidc.BarcodeFailureEvent;
import com.honeywell.aidc.BarcodeReadEvent;
import com.honeywell.aidc.BarcodeReader;
import com.honeywell.aidc.ScannerUnavailableException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import it.integry.barcode_base_android_library.exception.BarcodeAdapterNotFoundException;
import it.integry.barcode_base_android_library.extension.RunnableArgs;
import it.integry.barcode_base_android_library.interfaces.BarcodeReaderInterface;
import it.integry.barcode_base_android_library.model.BarcodeScanDTO;
import it.integry.barcode_base_android_library.model.BarcodeSetting;

public class HoneyWellBarcodeReader implements BarcodeReaderInterface {

    private final Context mContext;
    private AidcManager manager;
    private BarcodeReader barcodeReader;

    private static RunnableArgs<BarcodeScanDTO> mOnScanSuccessfull;
    private static RunnableArgs<Exception> mOnScanFailed;

    private static final String TAG = HoneyWellBarcodeReader.class.getName();

    public HoneyWellBarcodeReader(final Context context) {
        this.mContext = context;
    }

    @Override
    public boolean isRightAdapter() {
        String model = Build.MODEL;
        List<String> compatibleModels = new ArrayList<String>();
        compatibleModels.add("EDA50");
        compatibleModels.add("EDA51");
        compatibleModels.add("CT60");

        return compatibleModels.contains(model);
    }

    @Override
    public void init(final Runnable onDeviceReady) throws BarcodeAdapterNotFoundException {
        if (isRightAdapter()) {

            AidcManager.create(mContext, aidcManager -> {

                manager = aidcManager;

                try {
                    barcodeReader = manager.createBarcodeReader();
                    registerListenersInternal();
                } catch (Exception e) {
//                    throw new Exception("Invalid Scanner Name Exception: " + e.getMessage());
                }


                onDeviceReady.run();
            });

        } else {
            throw new BarcodeAdapterNotFoundException(getAdapterName());
        }
    }

    @Override
    public void deinit() {
        if (barcodeReader != null) {
            // close BarcodeReader to clean up resources.
            barcodeReader.close();
            barcodeReader = null;
        }

        if (manager != null) {
            // close AidcManager to disconnect from the scanner service.
            // once closed, the object can no longer be used.
            manager.close();
        }
    }


    @Override
    public void register(RunnableArgs<BarcodeScanDTO> onScanSuccessfull, RunnableArgs<Exception> onScanFailed) {
        mOnScanSuccessfull = onScanSuccessfull;
        mOnScanFailed = onScanFailed;
    }


    @Override
    public String getAdapterName() {
        return "Honeywell";
    }

    @Override
    public void onKeyEvent(KeyEvent keyEvent) {

    }


    private void registerListenersInternal() throws Exception {
        // register bar code event listener
        barcodeReader.addBarcodeListener(new BarcodeReader.BarcodeListener() {
            @Override
            public void onBarcodeEvent(BarcodeReadEvent barcodeReadEvent) {
                dispatchEvent(barcodeReadEvent);
            }

            @Override
            public void onFailureEvent(BarcodeFailureEvent barcodeFailureEvent) {

            }
        });


        // set the trigger mode to client control
//        try {
//            barcodeReader.setProperty(BarcodeReader.PROPERTY_TRIGGER_CONTROL_MODE,
//                    BarcodeReader.TRIGGER_CONTROL_MODE_CLIENT_CONTROL);
//        } catch (UnsupportedPropertyException e) {
//            throw new Exception("Failed to apply properties");
//        }
    }


    private void dispatchEvent(BarcodeReadEvent barcodeReadEvent) {
//        mContext.runOnUiThread(() -> {

        BarcodeScanDTO barcodeScanDTO = new BarcodeScanDTO()
                .setByteValue(barcodeReadEvent.getBarcodeData().getBytes())
                .setStringValue(barcodeReadEvent.getBarcodeData())
                .setType(HoneywellBarcodeTypeMapper.map(barcodeReadEvent.getCodeId()))
                .setName(HoneywellBarcodeTypeMapper.map(barcodeReadEvent.getCodeId()).toString());

        mOnScanSuccessfull.run(barcodeScanDTO);
//        });
    }


    private Map<String, Object> getDefaultProperties() {
        Map<String, Object> properties = new HashMap<>();

        // Set Symbologies On/Off
        properties.put(BarcodeReader.PROPERTY_EAN_8_ENABLED, true);
        properties.put(BarcodeReader.PROPERTY_EAN_13_ENABLED, true);
        properties.put(BarcodeReader.PROPERTY_CODE_39_ENABLED, true);
        properties.put(BarcodeReader.PROPERTY_CODE_128_ENABLED, true);
        properties.put(BarcodeReader.PROPERTY_GS1_128_ENABLED, true);
        properties.put(BarcodeReader.PROPERTY_UPC_A_ENABLE, true);


        properties.put(BarcodeReader.PROPERTY_EAN_8_CHECK_DIGIT_TRANSMIT_ENABLED, true);
        properties.put(BarcodeReader.PROPERTY_EAN_13_CHECK_DIGIT_TRANSMIT_ENABLED, true);
        properties.put(BarcodeReader.PROPERTY_UPC_A_CHECK_DIGIT_TRANSMIT_ENABLED, true);

        return properties;
    }

    @Override
    public void changeSettings(List<Pair<String, Object>> settings) {
        Map<String, Object> properties = getDefaultProperties();
        String scanValue;
        for (Pair<String, Object> setting : settings) {
            switch (setting.first) {
                case BarcodeSetting.P_TRIGGER_SCAN_MODE:
                    scanValue = setting.second != null ? (String) setting.second : "";
                    String scanMode;
                    switch (scanValue) {
                        case BarcodeSetting.V_TRIGGER_SCAN_MODE_ONE_SHOT:
                            scanMode = BarcodeReader.TRIGGER_SCAN_MODE_ONESHOT;
                            break;
                        case BarcodeSetting.V_TRIGGER_SCAN_MODE_CONTINUOS:
                            scanMode = BarcodeReader.TRIGGER_SCAN_MODE_CONTINUOUS;
                            break;
                        case BarcodeSetting.V_TRIGGER_SCAN_MODE_READ_ON_SECOND_TRIGGER_PRESS:
                            scanMode = BarcodeReader.TRIGGER_SCAN_MODE_READ_ON_SECOND_TRIGGER_PRESS;
                            break;
                        case BarcodeSetting.V_TRIGGER_SCAN_MODE_READ_ON_RELEASE:
                            scanMode = BarcodeReader.TRIGGER_SCAN_MODE_READ_ON_RELEASE;
                            break;
                        default:
                            scanMode = BarcodeReader.TRIGGER_SCAN_MODE_ONESHOT;
                    }
                    properties.put(BarcodeReader.PROPERTY_TRIGGER_SCAN_MODE, scanMode);
                    break;
                case BarcodeSetting.P_TRIGGER_SCAN_DELAY:
                    scanValue = setting.second != null ? (String) setting.second : "0";
                    properties.put(BarcodeReader.PROPERTY_TRIGGER_SCAN_DELAY, Integer.parseInt(scanValue));
                    break;
            }
        }
//        properties.put(BarcodeReader.PROPERTY_TRIGGER_SCAN_DELAY, 500);
//        properties.put(BarcodeReader.PROPERTY_TRIGGER_SCAN_MODE, BarcodeReader.TRIGGER_SCAN_MODE_READ_ON_SECOND_TRIGGER_PRESS);


        // Apply the settings
        barcodeReader.setProperties(properties);

        try {
            barcodeReader.claim();
        } catch (ScannerUnavailableException e) {
            e.printStackTrace();
        }
    }
}
