package info.nightscout.androidaps.plugins.PumpDanaR.comm;

import android.annotation.TargetApi;
import android.os.Build;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import info.nightscout.androidaps.Config;
import info.nightscout.utils.CRC;

/*
 *  00  01   02  03   04   05  06
 *
 *  7E  7E  len  F1  CMD  SUB data CRC CRC 2E  2E
 */

public class MessageBase {
    private static Logger log = LoggerFactory.getLogger(MessageBase.class);
    private byte[] buffer = new byte[512];
    private int position = 6;

    public boolean received = false;
    public boolean failed = false;

    public void SetCommand(int cmd) {
        this.buffer[4] = (byte) (cmd >> 8 & 0xFF);
        this.buffer[5] = (byte) (cmd & 0xFF);
    }

    public void AddParamByte(byte data) {
        this.buffer[this.position++] = data;
    }

    public void AddParamInt(int data) {
        this.buffer[this.position++] = (byte) (data >> 8 & 0xFF);
        this.buffer[this.position++] = (byte) (data & 0xFF);
    }

    public void AddParamDate(GregorianCalendar date) {
        AddParamByte((byte) (date.get(Calendar.YEAR) - 1900 - 100));
        AddParamByte((byte) (date.get(Calendar.MONTH) + 1));
        AddParamByte((byte) (date.get(Calendar.DAY_OF_MONTH)));
        AddParamByte((byte) (date.get(Calendar.HOUR)));
        AddParamByte((byte) (date.get(Calendar.MINUTE)));
    }

    public void AddParamDateTime(Date date) {
        AddParamByte((byte) (date.getSeconds()));
        AddParamByte((byte) (date.getMinutes()));
        AddParamByte((byte) (date.getHours()));
        AddParamByte((byte) (date.getDate()));
        AddParamByte((byte) (date.getMonth() + 1));
        AddParamByte((byte) (date.getYear() - 100));
    }

    public byte[] getRawMessageBytes() {
        this.buffer[0] = (byte) 0x7E;
        this.buffer[1] = (byte) 0x7E;

        int length = this.position - 3;

        this.buffer[2] = (byte) length;
        this.buffer[3] = (byte) 0xF1;

        this.AddParamInt(CRC.getCrc16(this.buffer, 3, length));

        this.buffer[length + 5] = (byte) 0x2E;
        this.buffer[length + 6] = (byte) 0x2E;

        return Arrays.copyOf(buffer, length + 7);
    }

    public String getMessageName() {
        return MessageOriginalNames.getName(getCommand());
    }

    public void handleMessage(byte[] bytes) {
        if (Config.logDanaMessageDetail) {
            if (bytes.length > 6) {
                int command = (bytes[5] & 0xFF) | ((bytes[4] << 8) & 0xFF00);
                log.debug("UNPROCESSED MSG: " + getMessageName() + " Command: " + String.format("%04X", command) + " Data: " + toHexString(bytes));
            } else {
                log.debug("MISFORMATTED MSG: " + toHexString(bytes));
            }
        }
    }

    public int getCommand() {
        int command = byteFromRawBuff(buffer, 5) | (byteFromRawBuff(buffer, 4) << 8);
        return command;
    }

    public static int byteFromRawBuff(byte[] buff, int offset) {
        return buff[offset] & 0xFF;
    }

    public static int intFromBuff(byte[] buff, int offset, int length) {
        offset += 6;
        switch (length) {
            case 1:
                return byteFromRawBuff(buff, offset);
            case 2:
                return (byteFromRawBuff(buff, offset) << 8) + byteFromRawBuff(buff, offset + 1);
            case 3:
                return (byteFromRawBuff(buff, offset + 2) << 16) + (byteFromRawBuff(buff, offset + 1) << 8) + byteFromRawBuff(buff, offset);
            case 4:
                return (byteFromRawBuff(buff, offset + 3) << 24) + (byteFromRawBuff(buff, offset + 2) << 16) + (byteFromRawBuff(buff, offset + 1) << 8) + byteFromRawBuff(buff, offset);
        }
        return 0;
    }

    public static Date dateTimeFromBuff(byte[] buff, int offset) {
        Date date =
                new Date(
                        100 + intFromBuff(buff, offset, 1),
                        intFromBuff(buff, offset + 1, 1) - 1,
                        intFromBuff(buff, offset + 2, 1),
                        intFromBuff(buff, offset + 3, 1),
                        intFromBuff(buff, offset + 4, 1),
                        0
                );
        return date;
    }

    public static Date dateTimeSecFromBuff(byte[] buff, int offset) {
        Date date =
                new Date(
                        100 + intFromBuff(buff, offset, 1),
                        intFromBuff(buff, offset + 1, 1) - 1,
                        intFromBuff(buff, offset + 2, 1),
                        intFromBuff(buff, offset + 3, 1),
                        intFromBuff(buff, offset + 4, 1),
                        intFromBuff(buff, offset + 5, 1)
                );
        return date;
    }

    public static Date dateFromBuff(byte[] buff, int offset) {
        Date date =
                new Date(
                        100 + intFromBuff(buff, offset, 1),
                        intFromBuff(buff, offset + 1, 1) - 1,
                        intFromBuff(buff, offset + 2, 1)
                );
        return date;
    }

    @TargetApi(Build.VERSION_CODES.KITKAT)
    public static String stringFromBuff(byte[] buff, int offset, int length) {
        byte[] strbuff = new byte[length];
        System.arraycopy(buff, offset + 6, strbuff, 0, length);
        return new String(strbuff, StandardCharsets.UTF_8);
    }

    @TargetApi(Build.VERSION_CODES.KITKAT)
    public static String asciiStringFromBuff(byte[] buff, int offset, int length) {
        byte[] strbuff = new byte[length];
        System.arraycopy(buff, offset + 6, strbuff, 0, length);
        for (int pos = 0; pos < length; pos++)
            strbuff[pos] += 65; // "A"
        return new String(strbuff, StandardCharsets.UTF_8);
    }

    public static String toHexString(byte[] buff) {
        StringBuffer sb = new StringBuffer();

        int count = 0;
        for (byte element : buff) {
            sb.append(String.format("%02x ", element));
            if (++count % 4 == 0) sb.append(" ");
        }

        return sb.toString();
    }
}
