package com.example.user.myapp;

import android.app.IntentService;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;

import com.ghgande.j2mod.modbus.facade.ModbusTCPMaster;
import com.ghgande.j2mod.modbus.procimg.InputRegister;
import com.ghgande.j2mod.modbus.util.BitVector;

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

/**
 * An {@link IntentService} subclass for handling asynchronous task requests in
 * a service on a separate handler thread.
 * <p>
 * TODO: Customize class - update intent actions, extra parameters and static
 * helper methods.
 */
public class MyIntentService extends IntentService {

    // read action
    private static final String ACTION_READ_COIL = "read.coil";
    private static final String ACTION_READ_INPUT_REGISTER = "read.input.register";
    private static final String ACTION_READ_DISCRETE_INPUT = "read.discrete.input";

    // write action
    private static final String ACTION_WRITE_COIL = "write.coil";

    private static final String ACTION_CHECK_COIL = "check.coil";

    private ModbusTCPMaster master;

    // TODO: Rename parameters
    private static final String EXTRA_IP_ADDRESS = "extra.ip.address";
    private static final String EXTRA_PORT = "extra.ip.port";
    private static final String EXTRA_REF = "extra.ref";
    private static final String EXTRA_COUNT = "extra.count";
    private static final String EXTRA_INTENT_NAME = "extra.intent.name";

    private static final String EXTRA_BIT_TO_SET = "extra.bit";
    //turns out modbus on siemens logo is crap and isnt responding for at least 50ms
    private static final Integer CAP_WAIT_TIME = 100;
    //this is not a type, idiot.

    public MyIntentService() {
        super("MyIntentService");
    }

    private void sendBooleanListToActivity(List<Boolean> booleanList, String intentName, String bundleName) {
        Intent intent = new Intent(intentName);
        Bundle bundle = new Bundle();
        bundle.putBooleanArray(bundleName, toPrimitiveArray(booleanList));
        intent.putExtras(bundle);
        sendLocationBroadcast(intent);
    }

    private void sendIntegerListToActivity (ArrayList<Integer> integerList, String intentName) {
        Intent intent = new Intent(intentName);
        Bundle bundle = new Bundle();

        bundle.putIntegerArrayList("irValues", integerList);
        intent.putExtras(bundle);
        sendLocationBroadcast(intent);
    }

    private void sendLocationBroadcast(Intent intent) {
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
    }

    public void readDiscreteInputAction (String ipAddress, int port, int ref, int count, String intentReceiveName) {
        try {
            master = new ModbusTCPMaster(ipAddress, port);
            master.connect();

            BitVector bv = master.readInputDiscretes(ref, count);
            List<Boolean> listBooleanBit = new ArrayList<>();
            for (int i = 0; i < count; i++) {
                System.out.println(i+" discret input "+bv.getBit(i));
                listBooleanBit.add(bv.getBit(i));
            }
            sendBooleanListToActivity(listBooleanBit, intentReceiveName, "discreteInputValues");
            Thread.sleep(CAP_WAIT_TIME);
            master.disconnect();
        } catch (Exception e) {
            System.out.println("Exception in reading discrete input " + e);
        }
    }

    public void readCoilAction (String ipAddress, int port, int ref, int count, String intentReceiveName) {
        try {
            master = new ModbusTCPMaster(ipAddress, port);
            master.connect();

            BitVector bv = master.readCoils(ref,count);
            List<Boolean> listBooleanBit = new ArrayList<>();
            for (int i = 0; i < count; i++) {
                System.out.println(i+" coil "+bv.getBit(i));
                listBooleanBit.add(bv.getBit(i));
            }
            sendBooleanListToActivity(listBooleanBit, intentReceiveName, "coilValues");
            Thread.sleep(CAP_WAIT_TIME);
            master.disconnect();
        } catch (Exception e) {
            System.out.println("Exception in reading coil " + e);
        }
    }

    public void readInputRegisterAction (String ipAddress, int port, int ref, int count, String intentReceiveName) {
        try {
            master = new ModbusTCPMaster(ipAddress, port);
            master.connect();

            InputRegister [] inputRegisters = master.readInputRegisters(ref, count);
            ArrayList<Integer> listIntegerInputRegister = new ArrayList<>();
            for (InputRegister ir : inputRegisters) {
                listIntegerInputRegister.add(ir.getValue());
                System.out.println(ir.getValue()+" input register "+Math.random());
            }
            sendIntegerListToActivity (listIntegerInputRegister, intentReceiveName);
            Thread.sleep(CAP_WAIT_TIME);
            master.disconnect();
        } catch (Exception e) {
            System.out.println("Exception in reading input register " + e);
        }
    }

    public void writeCoilAction (String ipAddress, int port, int ref, boolean value, String intentReceiveName, boolean withCheck) {
        try {
            master = new ModbusTCPMaster(ipAddress, port);
            master.connect();
            // writing the value
            boolean writtenValue = master.writeCoil(ref, value);
            if (withCheck) {
                System.out.println("I wrote " + writtenValue + " to coil " + ref);
                Thread.sleep(CAP_WAIT_TIME);

                GlobalState state = (GlobalState) getApplicationContext();

                int coilToRead = state.getMapMappingValue().get(ref);
                System.out.println("Checking the value of coil "+coilToRead);
                boolean currentValue = master.readCoils(coilToRead, 1).getBit(0);
                sendNewBitValueToActivity(currentValue, intentReceiveName);
            } else {
                System.out.println("I switched the manual mode to " + writtenValue);
                sendNewBitValueToActivity(writtenValue, intentReceiveName);
            }
            Thread.sleep(CAP_WAIT_TIME);
            master.disconnect();
        } catch (Exception e) {
            System.out.println("Exception in writing coil " + e);
        }
    }

    private void sendNewBitValueToActivity (boolean newValue, String intentReceive) {
        Intent intent = new Intent(intentReceive);
        intent.putExtra("coil", newValue);
        sendLocationBroadcast(intent);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            final String action = intent.getAction();
            final String ipAddress = intent.getStringExtra(EXTRA_IP_ADDRESS);
            final int port = intent.getIntExtra(EXTRA_PORT, 502);
            final int ref = intent.getIntExtra(EXTRA_REF, 8192);
            final int count = intent.getIntExtra(EXTRA_COUNT, 8);
            final String intentName = intent.getStringExtra(EXTRA_INTENT_NAME);

            // for write
            final Boolean value = intent.getBooleanExtra(EXTRA_BIT_TO_SET, false);

            final Boolean withCheck = intent.getBooleanExtra("extra.is.basic.coil", false);

            switch(action) {
                case ACTION_READ_DISCRETE_INPUT:
                    readDiscreteInputAction (ipAddress, port, ref, count, intentName);
                    break;
                case ACTION_READ_COIL :
                    readCoilAction(ipAddress, port, ref, count, intentName);
                    break;
                case ACTION_READ_INPUT_REGISTER :
                    readInputRegisterAction (ipAddress, port,ref, count, intentName);
                    break;
                case ACTION_WRITE_COIL:
                    writeCoilAction(ipAddress, port, ref, value, intentName, withCheck);
                    break;
            }
        }
    }


    private boolean[] toPrimitiveArray(final List<Boolean> booleanList) {
        final boolean[] primitives = new boolean[booleanList.size()];
        int index = 0;
        for (Boolean object : booleanList) {
            primitives[index++] = object;
        }
        return primitives;
    }
}
