/*
 * Copyright (c) 2017 Oracle and/or its affiliates.  All rights reserved.
 *
 * This software is dual-licensed to you under the MIT License (MIT) and
 * the Universal Permissive License (UPL).  See the LICENSE file in the root
 * directory for license terms.  You may choose either license, or both.
 */

package com.oracle.iot.sample.obd;

/**
 * This enum represents all the OBD PIDs that are present in the OBD DM.
 * It also stores the associated DM attribute name and the corresponding UnitSystem value for the PID.
 * It also has helper functions for converting the unit system.
 */
public enum ObdPID {
    SPEED("10D", "Speed", Integer.class, ObdDeviceModelAttributes.VEHICLE_SPEED_ATTRIBUTE.toString(),true,
            UnitSystem.SPEED),
    RPM("10C", "RPM",Double.class, ObdDeviceModelAttributes.ENGINE_RPM_ATTRIBUTE.toString(),true,
            UnitSystem.NONE),
    THROTTLE("111", "Throttle",Double.class, ObdDeviceModelAttributes.THROTTLE_POSITION_ATTRIBUTE.toString(),true,
            UnitSystem.PERCENTAGE),
    MASS_AIRFLOW_RATE("110","Mass Airflow Rate",Double.class, ObdDeviceModelAttributes.MAF.toString(),true,
            UnitSystem.MAF),
    RUNTIME("11F", "Eng Runtime",Integer.class, ObdDeviceModelAttributes.RUNTIME_SINCE_ENGINE_START.toString(),true,
            UnitSystem.SECONDS),
    DISTANCE_DTC("131","Clear Distance",Integer.class, ObdDeviceModelAttributes.DISTANCE_SINCE_DTC_CLEARED.toString(),true,
            UnitSystem.DISTANCE),
    ENGINE_COOLANT_TEMP("105","Eng Coolant Temp",Integer.class, ObdDeviceModelAttributes.ENGINE_COOLANT_TEMPERATURE.toString(),true,
            UnitSystem.TEMPERATURE),
    LATITUDE(null,"Latitude",Double.class, ObdDeviceModelAttributes.LATITUDE_ATTRIBUTE.toString(),true,
            UnitSystem.DEGREE),
    LONGITUDE(null,"Longitude",Double.class, ObdDeviceModelAttributes.LONGITUDE_ATTRIBUTE.toString(),true,
            UnitSystem.DEGREE),
    ALTITUDE(null,"Altitude",Double.class, ObdDeviceModelAttributes.ALTITUDE_ATTRIBUTE.toString(),true,
            UnitSystem.ALTITUDE),
    NUMBER_OF_DTC("101","Number Of DTC",Integer.class, ObdDeviceModelAttributes.NUMBER_OF_DTC.toString(),true,
            UnitSystem.NONE);

    private final String pid;
    private final String description;
    private final Class type;
    private final String attribute;
    private boolean isAttribute;
    private UnitSystem unitSystem;

    private ObdPID(final String pid, String description, Class type, String attribute, boolean isAttribute, UnitSystem unitSystem) {
        this.pid = pid;
        this.description = description;
        this.type = type;
        this.attribute = attribute;
        this.isAttribute = isAttribute;
        this.unitSystem = unitSystem;
    }

    public Class getType() {
        return type;
    }

    public String getAttribute() {
        return attribute;
    }

    public String getDescription() {
        return  description;
    }

    @Override
    public String toString() {
        return pid;
    }

    public String getUnits() { return unitSystem.toString(); }

    public boolean isAttribute() {return isAttribute; };

    /**
     * This function provides the formatted value to be displayed in the UI.
     * @param pid:
     * @param val
     * @return the formatted string to be displayed in the tableview in UI.
     */
    public static String getFormattedValue(ObdPID pid, Object val) {
        String valueStr = val.toString();
        String displayString = "";
        switch (pid) {
            case SPEED: {
                displayString = valueStr;
                break;
            }
            case RPM: {
                Double dblVal = (Double) val;
                int intVal = dblVal.intValue();
                displayString = String.format("%d",intVal);
                break;
            }
            case THROTTLE: {
                Double dblVal = (Double) val;
                Integer throttle = dblVal.intValue();
                displayString = throttle.toString();
                break;
            }
            case MASS_AIRFLOW_RATE: {
                Double dblVal;
                if (valueStr.contains(".")) {
                    dblVal = (Double) val;
                } else {
                    Integer intval = (Integer) val;
                    dblVal = intval.doubleValue();
                }
                String formattedVal = getFormattedValue(dblVal, 3, 4);
                displayString = formattedVal;
                break;
            }
//            case FUEL_LEVEL: {
//                displayString = valueStr;
//                break;
//            }
            case RUNTIME: {
                displayString = valueStr;
                break;
            }
            case DISTANCE_DTC: {
                displayString = valueStr;
                break;
            }
            case ENGINE_COOLANT_TEMP: {
                displayString = valueStr;
                break;
            }
            case LATITUDE:
            case LONGITUDE: {
                Double dblVal = (Double) val;
                String formattedVal = getFormattedValue(dblVal, 3, 6);
                displayString = formattedVal;
                break;
            }
            case ALTITUDE: {
                Double dblVal = (Double) val;
                String formattedVal = getFormattedValue(dblVal, 4, 6);
                displayString = formattedVal;
                break;
            }
            case NUMBER_OF_DTC:
                displayString = valueStr;
                break;
        }
        return displayString;
    }

    //private static boolean isMetricUnitSystem = false;
    private static boolean isMetricUnitSystem = true;

    static String getFormattedValue(Double val, int digits, int decimalPositions) {
        int total =  digits + 1 +decimalPositions;
        return String.format("%" + total + "." + decimalPositions+ "f", val);
    }

    public static ObdPID getCommand(String pid) {
        if (pid != null) {
            for (ObdPID cmd : ObdPID.values()) {
                if (pid.equalsIgnoreCase(cmd.pid)) {
                    return cmd;
                }
            }
        }
        return null;
    }

    /**
     * This enum represents the unit system values depending the Unit System followed.
     * The global variable isMetricUnitSystem defines the whether metric unit system is followed or not.
     */
    public enum UnitSystem {
        SECONDS("sec"),
        MINUTES("min"),
        SPEED("km/hr"),
        DISTANCE("km"),
        MAF("gm/sec"),
        FUEL_ECONOMY("km/lr"),
        PERCENTAGE("%"),
        NONE(" "),
        DEGREE("\u00B0"),
        ALTITUDE("m"),
        TEMPERATURE(DEGREE + "C");

        private String unit;
        UnitSystem(String unit) {
            this.unit = unit;
        }
        public String toString() {
            return unit;
        }
    }

}
