package com.handset.serialportsensor.bean;

import android.util.Log;

import com.handset.serialportlib.ByteUtils;
import com.handset.serialportsensor.constant.Constant;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MetaData {
    private static final String logTag = "HANDSET-JE002-EX";

    private static final BigDecimal T_CEILING = new BigDecimal(100.00f);
    private static final BigDecimal T_FLOOR = new BigDecimal(-60.00f);
    private static final BigDecimal H_CEILING = new BigDecimal(100.00f);
    private static final BigDecimal H_FLOOR = new BigDecimal(00.00f);
    private static final BigDecimal I_CEILING = new BigDecimal(300.00f);
    private static final BigDecimal I_FLOOR = new BigDecimal(00.00f);
    private static final BigDecimal O_CEILING = new BigDecimal(300.00f);
    private static final BigDecimal O_FLOOR = new BigDecimal(00.00f);
    private static final BigDecimal DP_CEILING = new BigDecimal(600.00f);
    private static final BigDecimal DP_FLOOR = new BigDecimal(-600.00f);
    private static final BigDecimal P20_CEILING = new BigDecimal(300.00f);
    private static final BigDecimal P20_FLOOR = new BigDecimal(00.00f);
    private static final BigDecimal H20_CEILING = new BigDecimal(100.00f);
    private static final BigDecimal H20_FLOOR = new BigDecimal(00.00f);

    private static final int[] temperatureConvertDatum = {
            18, 23, 28, 34, 41, 50, 61, 73, 88, 105,
            125, 148, 175, 207, 244, 286, 334, 390, 454, 528,
            611, 706, 814, 935, 1073, 1228, 1403, 1599, 1819, 2065,
            2339, 2645, 2985, 3362, 3781, 4244, 4756, 5321, 5942, 6626,
            7377, 8201, 9103, 10089, 11165, 12339, 13618, 15009, 16520, 18160, 19937
    };

    private Integer makerId;
    private String sensorNo;
    private String sensorNoRadix;
    private String eggNo;
    private String eggType;
    private String coneNo;
    private String coneUsedTimes;
    private Float temperature;
    private Float humidity;
    private Float innerPressure;
    private Float outerPressure;
    private Float diffPressure;
    private Float degree20Pressure;
    private Float degree20Humidity;

    public void doCalcMeta(String data) {
        byte[] bytes = ByteUtils.hexStrToBytes(data.substring(0, data.length() - 4));
        switch (bytes[7] & 0xff) {
            case Constant.Command.COMMAND_29:
                this.setTemperature((float) parseValue(bytes) / 100);
                break;
            case Constant.Command.COMMAND_2A:
                this.setInnerPressure((float) parseValue(bytes) / 1000);
                break;
            case Constant.Command.COMMAND_2B:
                this.setHumidity((float) parseValue(bytes) / 100);
                break;
            case Constant.Command.COMMAND_2C:
                this.setOuterPressure((float) parseValue(bytes) / 1000);
                break;
            case Constant.Command.COMMAND_2D:
                this.setDiffPressure((float) parseValue(bytes) / 1000);
                break;
            case Constant.Command.COMMAND_2E:
                this.setDegree20Humidity((float) parseValue(bytes) / 100);
                break;
            case Constant.Command.COMMAND_2F:
                this.setDegree20Pressure((float) parseValue(bytes) / 1000);
                break;
            default:
                break;
        }
    }

    public void doCalcMetaEx() {
        calcPressDiff();
        calcPressOfDegree20();
        calcHumidityOfDegree20();
    }
    public void calcPressDiff() {
        try {
            if(this.diffPressure == 0) {
                Log.i(logTag, "Diff pressure not report, need to be calculated");
                this.diffPressure = this.innerPressure - this.outerPressure;
            }
        } catch (Exception e) {
            e.printStackTrace();
            this.diffPressure = 0.00f;
        }
    }

    public void calcPressOfDegree20() {
        try {
            if(this.degree20Pressure == 0) {
                Log.i(logTag, "20 degree pressure not report, need to be calculated");
                int tempMustInt = (int) (this.temperature * 10);
                double temp = (293000.00) / ((tempMustInt + 400) + 2330);
                double innerPress20Degree = (temp * (this.innerPressure * 1000)) / 100 / 1000;

                DecimalFormat df = new DecimalFormat(Constant.FORMAT_FLOAT_PATTERN);
                df.setRoundingMode(RoundingMode.FLOOR);
                this.degree20Pressure = Float.valueOf(df.format(innerPress20Degree));
            }
        } catch (Exception e) {
            e.printStackTrace();
            this.degree20Pressure = 0.00f;
        }
    }

    public void calcHumidityOfDegree20() {
        try {
            if(this.degree20Humidity == 0) {
                Log.i(logTag, "20 degree humidity not report, need to be calculated");
                int tmp = (((int) (this.temperature * 10) + 400) / 10) / 2;
                tmp = (((int) (this.temperature * 10) + 400) / 10) % 2 == 0 ? tmp : tmp + 1;
                tmp = tmp > 50 || tmp < 0 ? 50 : tmp;

                double degree20Humidity = (temperatureConvertDatum[tmp] * ((double) (this.humidity * 100) / 100)) / 2339;
                DecimalFormat df2 = new DecimalFormat(Constant.FORMAT_FLOAT_PATTERN);
                df2.setRoundingMode(RoundingMode.FLOOR);
                this.degree20Humidity = Float.valueOf(df2.format(degree20Humidity));
            }
        } catch (Exception e) {
            e.printStackTrace();
            this.degree20Humidity = 0.00f;
        }

        this.degree20Humidity = new BigDecimal(this.degree20Humidity).compareTo(H20_CEILING) >=0 ? 100.f : this.degree20Humidity;
    }

    private int parseValue(byte[] bytes) {
        return (bytes[9] & 0xff) | ((bytes[10] & 0xff) << 8) | ((bytes[11] & 0xff) << 16) | ((bytes[12] & 0xff) << 24);
    }

    public boolean isValid() {
        BigDecimal t = new BigDecimal(this.temperature);
        BigDecimal h = new BigDecimal(this.humidity);
        BigDecimal i = new BigDecimal(this.innerPressure);
        BigDecimal o = new BigDecimal(this.outerPressure);
        BigDecimal dp = new BigDecimal(this.diffPressure);
        BigDecimal p20 = new BigDecimal(this.degree20Pressure);
        BigDecimal h20 = new BigDecimal(this.degree20Humidity);
        Log.i(logTag, "t:" + t.floatValue() + ",h:" + h.floatValue() + ",i:" + i.floatValue() + ",o:" + o.floatValue() + ",dp:" + dp.floatValue()  + ",p20:" + p20.floatValue()+ ",h20:" + h20.floatValue());
        return t.compareTo(T_CEILING) <= 0 && t.compareTo(T_FLOOR) >= 0
                && h.compareTo(H_CEILING) <= 0 && h.compareTo(H_FLOOR) >= 0
                && i.compareTo(I_CEILING) <= 0 && i.compareTo(I_FLOOR) >= 0
                && o.compareTo(O_CEILING) <= 0 && o.compareTo(O_FLOOR) >= 0
                && dp.compareTo(DP_CEILING) <= 0 && dp.compareTo(DP_FLOOR) >= 0
                && p20.compareTo(P20_CEILING) <= 0 && p20.compareTo(P20_FLOOR) >= 0
                && h20.compareTo(H20_CEILING) <= 0 && h20.compareTo(H20_FLOOR) >= 0
                ;
    }

    public void resetData() {
        this.setSensorNo(null);
        this.setSensorNoRadix(null);
        this.temperature = 0.0F;
        this.humidity = 0.0F;
        this.innerPressure = 0.0F;
        this.outerPressure = 0.0F;
        this.diffPressure = 0.0F;
        this.degree20Humidity = 0.0F;
        this.degree20Pressure = 0.0F;
    }

    public void resetData20Degree() {
        this.diffPressure = 0.0F;
        this.degree20Humidity = 0.0F;
        this.degree20Pressure = 0.0F;
    }
}
