package com.example.bpa.app;


import com.example.bpa.Model.IReadCallBack;
import com.example.bpa.Model.IWriteCallBack;
import com.example.bpa.helper.MessageLog;
import com.licheedev.modbus4android.ModbusCallback;
import com.licheedev.modbus4android.ModbusParam;
import com.licheedev.modbus4android.param.TcpParam;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.msg.ReadCoilsResponse;
import com.serotonin.modbus4j.msg.ReadHoldingRegistersResponse;
import com.serotonin.modbus4j.msg.WriteCoilResponse;
import com.serotonin.modbus4j.msg.WriteRegistersResponse;

import java.nio.ByteBuffer;
import java.util.Arrays;


public class ModbusTcpServer {
    private static volatile ModbusTcpServer instance = null;

    public static ModbusTcpServer get() {
        ModbusTcpServer manager = instance;
        if (manager == null) {
            synchronized (ModbusTcpServer.class) {
                manager = instance;
                if (manager == null) {
                    manager = new ModbusTcpServer();
                    instance = manager;
                }
            }
        }
        return manager;
    }

    private ModbusTcpServer() {
    }

    ModbusParam param;

    private int GetAddress(String address) {
        if (address == null) return -1;
        if (address.length() > 0) {
            address = address.trim();
            if (address.toUpperCase().contains("M") && address.length() >= 4) {
                String[] res = address.substring(1).split(".");
                if (res != null && res.length == 2) {
                    try {
                        int firstAdd = Integer.parseInt(res[0]);
                        int endAdd = Integer.parseInt(res[1]);
                        if (endAdd >= 0 && endAdd <= 7) {
                            return (firstAdd * 8) + 320 + endAdd;
                        }
                    } catch (NumberFormatException e) {
                        return -1;
                    }
                }
            } else if ((address.toUpperCase().contains("VW") || address.toUpperCase().contains("VD")) && address.length() >= 3) {
                String res = address.substring(2);
                if (res != null) {
                    try {
                        int tempAdd = Integer.parseInt(res);
                        return (tempAdd / 2) + 100;
                    } catch (NumberFormatException e) {
                        return -1;
                    }
                }
            } else {
                try {
                    return Integer.parseInt(address);
                } catch (NumberFormatException e) {
                    return -1;
                }
            }
        }
        return -1;
    }

    /**
     * 获取布尔位地址信息
     * 列：M2.5 = getBitSingleAdd("M",2,5);
     *
     * @param Prefix   地址标头
     * @param startAdd 起始地址编号
     * @param num      要获取的第几位数量
     * @return
     */
    public String getBitSingleAdd(String Prefix, int startAdd, int num) {
        if (num > 0) {
            int FirstAdd = num / 8;
            int EndAdd = num % 8;
            if (EndAdd == 0) {
                FirstAdd--;
                EndAdd = 7;
            } else {
                EndAdd--;
            }
            return Prefix + FirstAdd + startAdd + "." + EndAdd;
        }
        return "";
    }

    /***
     * Modbus 连接
     * @param host 设备IP
     * @param port 设备端口号
     */
    public void Connect(String host, int port) {
        param = TcpParam.create(host, port)
                .setTimeout(1000)
                .setRetries(0)
                .setEncapsulated(false)
                .setKeepAlive(true);
        ModbusTcpHelper.get().init(param, new ModbusCallback<ModbusMaster>() {
            @Override
            public void onSuccess(ModbusMaster modbusMaster) {
                MessageLog.ShowInfo("设备 " + host + " 连接成功");
            }

            @Override
            public void onFailure(Throwable tr) {
                MessageLog.ShowError("设备 " + host + " 连接失败：" + tr.getMessage());
            }

            @Override
            public void onFinally() {

            }
        });
    }

    /***
     *读取整形数据
     * @param Address the address
     * @param length 读取的长度
     * @param callback 读取成功的回调
     */

    public void ReadShort(String Address, int length, IReadCallBack<short[]> callback) {
        int add = GetAddress(Address);
        if (add < 0) return;
        ModbusTcpHelper.get().readHoldingRegisters(1, add, length, new ModbusCallback<ReadHoldingRegistersResponse>() {
            @Override
            public void onSuccess(ReadHoldingRegistersResponse readHoldingRegistersResponse) {
                short[] data = readHoldingRegistersResponse.getShortData();
                if (data.length == length)
                    callback.onSuccess(data);
            }

            @Override
            public void onFailure(Throwable tr) {
                MessageLog.ShowError("ReadShort onFailure，Address=" + Address + ",length=" + length + ",msg:" + tr.toString());
            }

            @Override
            public void onFinally() {
            }
        });
    }

    public void ReadBool(String Address, int length, IReadCallBack<boolean[]> callback) {
        int add = GetAddress(Address);
        if (add < 0) return;
        ModbusTcpHelper.get().readCoil(1, add, length, new ModbusCallback<ReadCoilsResponse>() {
            @Override
            public void onSuccess(ReadCoilsResponse readCoilsResponse) {
                boolean[] data = readCoilsResponse.getBooleanData();
                boolean[] result = Arrays.copyOfRange(data, 0, length);
                if (result.length == length)
                    callback.onSuccess(result);
            }

            @Override
            public void onFailure(Throwable tr) {
                MessageLog.ShowError("ReadBool onFailure，Address=" + Address + ",length=" + length + ",msg:" + tr.toString());
            }

            @Override
            public void onFinally() {
            }
        });
    }

    public void ReadFloat(String Address, int length, IReadCallBack<float[]> callback) {
        int add = GetAddress(Address);
        if (add < 0) return;
        ModbusTcpHelper.get().readHoldingRegisters(1, add, length * 2, new ModbusCallback<ReadHoldingRegistersResponse>() {
            @Override
            public void onSuccess(ReadHoldingRegistersResponse readHoldingRegistersResponse) {
                byte[] data = readHoldingRegistersResponse.getData();
                float[] tempValues = new float[length];
                for (int i = 0; i < length; i++) {
                    byte[] tempData = new byte[4];
                    for (int m = 0; m < 4; m++) {
                        tempData[m] = data[i * 4 + m];
                    }
                    tempValues[i] = ByteBuffer.wrap(tempData).getFloat();
                }
                if (tempValues.length == length)
                    callback.onSuccess(tempValues);
            }

            @Override
            public void onFailure(Throwable tr) {
                MessageLog.ShowError("ReadFloat onFailure，Address=" + Address + ",length=" + length + ",msg:" + tr.toString());
            }

            @Override
            public void onFinally() {

            }
        });

    }

    public void WriteShort(String Address, short Value, IWriteCallBack callback) {
        int add = GetAddress(Address);
        if (add < 0) return;
        short[] send = new short[1];
        send[0] = Value;
        ModbusTcpHelper.get().writeRegisters(1, add, send, new ModbusCallback<WriteRegistersResponse>() {
            @Override
            public void onSuccess(WriteRegistersResponse writeRegistersResponse) {
                MessageLog.ShowInfo("WriteShort onSuccess，Address=" + Address + ",Value=" + Value);
                callback.onSuccess();
            }

            @Override
            public void onFailure(Throwable tr) {
                MessageLog.ShowError("WriteShort onFailure，Address=" + Address + ",Value=" + Value + ",msg:" + tr.toString());
                callback.onFailure(tr.toString());
                try {
                    Thread.sleep(3000);
                    WriteShort(Address, Value, callback);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

            @Override
            public void onFinally() {

            }
        });
    }

    public void WriteBool(String Address, boolean Value, IWriteCallBack callback) {
        int add = GetAddress(Address);
        if (add < 0) return;
        ModbusTcpHelper.get().writeCoil(1, add, Value, new ModbusCallback<WriteCoilResponse>() {
            @Override
            public void onSuccess(WriteCoilResponse writeCoilResponse) {
                MessageLog.ShowInfo("WriteBool onSuccess，Address=" + Address + ",Value=" + Value);
                callback.onSuccess();
            }

            @Override
            public void onFailure(Throwable tr) {
                MessageLog.ShowError("WriteBool onFailure，Address=" + Address + ",Value=" + Value + ",msg:" + tr.toString());
                callback.onFailure(tr.toString());
                try {
                    Thread.sleep(3000);
                    WriteBool(Address, Value, callback);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

            @Override
            public void onFinally() {

            }
        });
    }

    public void WriteFloat(String Address, float Value, IWriteCallBack callback) {
        int add = GetAddress(Address);
        if (add < 0) return;
        int intBits = Float.floatToRawIntBits(Value);
        short[] send = new short[]{(short) ((intBits >> 16) & 0xffff), (short) (intBits & 0xffff)};
        ModbusTcpHelper.get().writeRegisters(1, add, send, new ModbusCallback<WriteRegistersResponse>() {
            @Override
            public void onSuccess(WriteRegistersResponse writeRegistersResponse) {
                MessageLog.ShowInfo("WriteFloat onSuccess，Address=" + Address + ",Value=" + Value);
                callback.onSuccess();
            }

            @Override
            public void onFailure(Throwable tr) {
                MessageLog.ShowError("WriteFloat onFailure，Address=" + Address + ",Value=" + Value + ",msg:" + tr.toString());
                callback.onFailure(tr.toString());
                try {
                    Thread.sleep(3000);
                    WriteFloat(Address, Value, callback);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

            @Override
            public void onFinally() {

            }
        });
    }

    public void WriteShort(String Address, short Value) {
        int add = GetAddress(Address);
        if (add < 0) return;
        short[] send = new short[1];
        send[0] = Value;
        ModbusTcpHelper.get().writeRegisters(1, add, send, new ModbusCallback<WriteRegistersResponse>() {
            @Override
            public void onSuccess(WriteRegistersResponse writeRegistersResponse) {
                MessageLog.ShowInfo("WriteShort onSuccess，Address=" + Address + ",Value=" + Value);
            }

            @Override
            public void onFailure(Throwable tr) {
                MessageLog.ShowError("WriteShort onFailure，Address=" + Address + ",Value=" + Value + ",msg:" + tr.toString());
                try {
                    Thread.sleep(3000);
                    WriteShort(Address, Value);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

            @Override
            public void onFinally() {

            }
        });
    }

    public void WriteBool(String Address, boolean Value) {
        int add = GetAddress(Address);
        if (add < 0) return;
        ModbusTcpHelper.get().writeCoil(1, add, Value, new ModbusCallback<WriteCoilResponse>() {
            @Override
            public void onSuccess(WriteCoilResponse writeCoilResponse) {
                MessageLog.ShowInfo("WriteBool onSuccess，Address=" + Address + ",Value=" + Value);
            }

            @Override
            public void onFailure(Throwable tr) {
                MessageLog.ShowError("WriteBool onFailure，Address=" + Address + ",Value=" + Value + ",msg:" + tr.toString());
                try {
                    Thread.sleep(3000);
                    WriteBool(Address, Value);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

            @Override
            public void onFinally() {

            }
        });
    }

    public void WriteFloat(String Address, float Value) {
        int add = GetAddress(Address);
        if (add < 0) return;
        int intBits = Float.floatToRawIntBits(Value);
        short[] send = new short[]{(short) ((intBits >> 16) & 0xffff), (short) (intBits & 0xffff)};
        ModbusTcpHelper.get().writeRegisters(1, add, send, new ModbusCallback<WriteRegistersResponse>() {
            @Override
            public void onSuccess(WriteRegistersResponse writeRegistersResponse) {
                MessageLog.ShowInfo("WriteFloat onSuccess，Address=" + Address + ",Value=" + Value);
            }

            @Override
            public void onFailure(Throwable tr) {
                MessageLog.ShowError("WriteFloat onFailure，Address=" + Address + ",Value=" + Value + ",msg:" + tr.toString());
                try {
                    Thread.sleep(3000);
                    WriteFloat(Address, Value);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

            @Override
            public void onFinally() {

            }
        });
    }

}
