/*
 */
package com.teltonikaServer.protocol;

import com.teltonikaServer.lib.Utils;
import com.teltonikaServer.network.TCPServer;
import com.teltonikaServer.persistence.Asset;
import com.teltonikaServer.persistence.Persistor;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Americo
 */
public class CodecIoSection {

    protected ArrayList<IO_Component> ioComponents = new ArrayList<IO_Component>();
    protected int IO_id;
    protected Iterator<IO_Component> componentsIterator;
    protected boolean in1 = false;
    protected boolean in2 = false;
    protected boolean in3 = false;
    protected boolean out1 = false;
    protected boolean out2 = false;
    protected int ad = 0;
    protected int ad1;
    protected int immobilizerEventId;

    public void persistTripEvent(long logGpsDataId, GpsElementSection gps, Asset asset) {
        try {
            double tripExtension;
            long ignCounter;
            // boolean tripStart = (this.getTripType() == 1);
            ignCounter = this.getIgnitionCounter();
            boolean tripStop = this.getTripType() == 0;
            if (tripStop) {
                tripExtension = this.getTripOdometer();
            } else {
                tripExtension = 0;
            }
            TCPServer.persistor.persistTrip(gps, logGpsDataId, asset.getAssetId(), tripExtension, (this.getOdometer() + asset.getMileageOffset()), ignCounter, getEntryType(), asset);
        } catch (Exception generalException) {
            TCPServer.serverLog.logEvent(Level.SEVERE,
                    "Error 10001: No es posible persistir trip event [" + generalException.getMessage() + "]");
        }
    }

    public void persistIgnitionCounterEvent(GpsElementSection gps, Asset asset) {
        long ignitionCounterOffset;
        long previousIgnitionCount;
        long previousTimestamp;
        try {
            IgnitionCount prvIgnCount = TCPServer.persistor.getCurrentHourCount(asset.getAssetId());
            if (prvIgnCount != null) {
                ignitionCounterOffset = prvIgnCount.ignitionCountOffset;
                previousIgnitionCount = prvIgnCount.ignitionCount;
                previousTimestamp = prvIgnCount.timestamp;
            } else {
                ignitionCounterOffset = 0;
                previousIgnitionCount = 0;
                previousTimestamp = 0;
            }

            long currentIgnitionCount = this.getIgnitionCounter();
            int hourCountStatus;

            if (currentIgnitionCount - previousIgnitionCount <= gps.utcTimestamp - previousTimestamp) {
                if (previousIgnitionCount > currentIgnitionCount) {
                    ignitionCounterOffset += (previousIgnitionCount - currentIgnitionCount);
                    hourCountStatus = Teltonika.HOUR_COUNT_ADJ;
                } else {
                    hourCountStatus = Teltonika.HOUR_COUNT_OK;
                }
                TCPServer.persistor.persistIgnitionCounter(asset.getAssetId(), gps.getUtcTimestamp(),
                        currentIgnitionCount, ignitionCounterOffset, hourCountStatus);
            } else {
                hourCountStatus = Teltonika.HOUR_COUNT_ERROR;
                TCPServer.persistor.persistIgnitionCounter(asset.getAssetId(), gps.getUtcTimestamp(),
                        currentIgnitionCount, ignitionCounterOffset, hourCountStatus);
            }
        } catch (Exception generalException) {
            TCPServer.serverLog.logEvent(Level.SEVERE,
                    "Error 10002: No es posible persistir ignition count [" + generalException.getMessage() + "]");
        }
    }

    public void persistIddling(GpsElementSection gps, long logGpsDataId, Asset asset) {
        TCPServer.persistor.persistIdling(gps, logGpsDataId, asset.getAssetId(), this.getIdlingType(),
                this.getIgnitionCounter() + asset.getIgnitionCounterOffset());
    }

    public void persistGeoFenceZone(int zone, GpsElementSection gps, long logGpsDataId, Asset asset) {
        /* 
        try {
            int eventId = this.getGeoEventValue(zone);
            eventId = (eventId >= 0) ? eventId : 10;
            double mileage = this.getOdometer()+asset.getMileageOffset();
            if (eventId == 2 || eventId == 3) {
                TCPServer.persistor.persistOverspeeding(gps, logGpsDataId, asset.getAssetId(), gps.getSpeed(), mileage,
                        eventId);
            }
        } catch (Exception generalException) {
            TCPServer.serverLog.logEvent(Level.SEVERE, "Error 10003: No es posible persistir Geofence speeding Event ["
                    + generalException.getMessage() + "]");
        }
         */
    }

    public void persistSpeedingEvent(GpsElementSection gps, long logGpsDataId, Asset asset) {
        try {
            int speedingValue = this.getSpeedingValue();
            int maxAllowedSpeed = asset.getMaxAllowedSpeed();
            double mileage = this.getOdometer(TCPServer.METER) + asset.getMileageOffset(TCPServer.METER);
            int eventType = (speedingValue > maxAllowedSpeed) ? 1 : 0;
            TCPServer.persistor.persistOverspeeding(gps, logGpsDataId, asset.getAssetId(), mileage,
                    eventType, asset);
        } catch (Exception generalException) {
            TCPServer.serverLog.logEvent(Level.SEVERE,
                    "Error 10007: No es posible persistir speeding Event [" + generalException.getMessage() + "]");
        }
    }

    public Long persistImmobilizerEvent(GpsElementSection gps, long logGpsDataId, Asset asset) {
        try {
            String tagId = this.getTagId();
            this.immobilizerEventId = this.getImmobilizerEventId();
            long currentDriver = this.getCurrentDriver();
            TCPServer.persistor.persistImmobilizerEvent(gps, logGpsDataId, asset.getAssetId(),
                    this.immobilizerEventId, currentDriver, tagId);
            return currentDriver;
        } catch (Exception generalException) {
            TCPServer.serverLog.logEvent(Level.SEVERE, "Error 10009: No es posible persistir Immobilizer Event ["
                    + generalException.getMessage() + "]");
        }
        return null;
    }

    public void persistIbuttonEvent(GpsElementSection gps, long logGpsDataId, Asset asset) {
        try {
            String tagId = this.getTagId();
            long driverId = TCPServer.persistor.getDriverIdFromTagId(Utils.reverseHexPairs(tagId));
            TCPServer.persistor.persistImmobilizerEvent(gps, logGpsDataId, asset.getAssetId(), Teltonika.IBUTTON_EVENT,
                    driverId, tagId);
        } catch (Exception generalException) {
            TCPServer.serverLog.logEvent(Level.SEVERE, "Error 10010: No es posible persistir Ibutton Event ["
                    + generalException.getMessage() + "]");
        }
    }

    public boolean persist(long logGpsDataId, GpsElementSection gps, Asset asset) {
        try {
            //TCPServer.persistor.persistEvent(this.IO_id, logGpsDataId);

            TCPServer.serverLog.logEvent(Level.INFO, "Nuevo IO EVENT: " + IO_id + " GpsDataId: " + logGpsDataId);

            switch (this.IO_id) {
                case Teltonika.TRIP_EVENT -> {
                    persistTripEvent(logGpsDataId, gps, asset);
                }
                case Teltonika.IGNITION_COUNTER -> {
                    persistIgnitionCounterEvent(gps, asset);
                }

                case Teltonika.IDLING_EVENT -> {
                    persistIddling(gps, logGpsDataId, asset);
                }

                case Teltonika.GEOFENCE_ZONE_01 -> {
                    persistGeoFenceZone(1, gps, logGpsDataId, asset);
                }

                case Teltonika.SPEEDING_EVENT -> {
                    persistSpeedingEvent(gps, logGpsDataId, asset);
                }

                case Teltonika.IMMOBILIZER_EVENT -> {
                    Long currentDriver = persistImmobilizerEvent(gps, logGpsDataId, asset);
                    asset.setDriverId(currentDriver);
                }

                case Teltonika.IBUTTON_EVENT -> {
                    persistIbuttonEvent(gps, logGpsDataId, asset);
                }

                case Teltonika.INPUT1 -> {
                    in1 = this.getIn1();
                }

                case Teltonika.INPUT2 -> {
                    in2 = getIn2();
                    TCPServer.serverLog.logEvent(Level.FINE, "Input 2 changed value to [" + in2 + "]");
                }

                case Teltonika.INPUT3 -> {
                    in3 = getIn3();
                }

                case Teltonika.AD1 -> {
                    ad1 = getAd1();
                }

                case Teltonika.OUTPUT1 -> {
                    out1 = getOut1();
                }

                case Teltonika.OUTPUT2 -> {
                    out1 = getOut2();
                }
            }
        } catch (Exception generalException) {
            return false;
        }
        return true;
    }

    public int getEntryType() {
        int entryType = -1;
        if (this.IO_id == Teltonika.TRIP_EVENT) {
            return this.getTripValue();
        }
        return entryType;
    }

    private int getTripValue() {
        for (IO_Component component : ioComponents) {
            if (component.IO_Id == Teltonika.TRIP_EVENT) {
                return Integer.parseInt(component.IO_value, 16);
            }
        }
        return -1;
    }

    protected int getTripType() {
        for (IO_Component c : ioComponents) {
            if (c.IO_Id == Teltonika.TRIP_EVENT) {
                return Integer.parseInt(c.IO_value, 16);
            }
        }
        return -1;
    }

    protected int getIdlingType() {
        for (IO_Component c : ioComponents) {
            if (c.IO_Id == Teltonika.IDLING_EVENT) {
                return Integer.parseInt(c.IO_value, 16);
            }
        }
        return -1;
    }

    public double getTripOdometer() {
        return getTripOdometer(TCPServer.KILOMETER);
    }

    public double getTripOdometer(int unit) {
        for (IO_Component c : ioComponents) {
            if (c.IO_Id == Teltonika.TRIP_ODOMETER) {
                return Integer.parseInt(c.IO_value, 16) / 1000.0;
            }
        }
        return -1;
    }

    public double getOdometer() {
        return getOdometer(TCPServer.KILOMETER);
    }

    public double getOdometer(int unit) {
        for (IO_Component c : ioComponents) {
            if (c.IO_Id == Teltonika.TOTAL_ODOMETER) {
                return (double) Integer.valueOf(c.IO_value, 16) / unit;
            }
        }
        return -1;
    }

    public int getExternalPower() {
        for (IO_Component c : ioComponents) {
            if (c.IO_Id == Teltonika.EXTERNAL_POWER) {
                return Integer.valueOf(c.IO_value, 16);
            }
        }
        return -1;
    }

    public long getDriverId() {
        try {
            return this.getCurrentDriver();// TCPServer.persistor.getDriverIdFromTagId(Utils.reverseHexPairs(this.driverId));
        } catch (Exception ex) {
            ex.printStackTrace();
            return -1;
        }

    }

    public long getIgnitionCounter() {
        for (IO_Component c : ioComponents) {
            if (c.IO_Id == Teltonika.IGNITION_COUNTER) {
                return Long.parseLong(c.IO_value, 16);
            }
        }
        return -1;
    }

    public int getIgnitionStatus() {

        int ign = -1;

        try {
            for (IO_Component ioComponent : this.ioComponents) {
                if (ioComponent.getComponentId() == Teltonika.IGNITION_ID) {
                    ign = Integer.parseInt(ioComponent.getComponentValue(), 16);
                }
            }
        } catch (NumberFormatException ex) {
            Logger.getLogger(Persistor.class.getName()).log(Level.SEVERE, null, ex);
        }
        return ign;
    }

    public int getMovementStatus() {

        int moving = -1;

        try {
            for (IO_Component ioComponent : this.ioComponents) {
                if (ioComponent.getComponentId() == Teltonika.MOVEMENT_ID) {
                    moving = Integer.parseInt(ioComponent.getComponentValue(), 16);
                }
            }
        } catch (NumberFormatException ex) {
            Logger.getLogger(Persistor.class.getName()).log(Level.SEVERE, null, ex);
        }
        return moving;
    }

    public int getSpeedingValue() {
        for (IO_Component c : ioComponents) {
            if (c.IO_Id == Teltonika.SPEEDING_EVENT) {
                return Integer.parseInt(c.IO_value, 16);
            }
        }
        return -1;
    }

    public int getGeoEventValue(int zone) {
        for (IO_Component c : ioComponents) {
            if (c.IO_Id == Teltonika.GEOFENCE_ZONE_01 + zone - 1) {
                return Integer.parseInt(c.IO_value, 16);
            }
        }
        return -1;
    }

    private String getTagId() {
        for (IO_Component c : ioComponents) {
            if (c.IO_Id == Teltonika.IBUTTON_EVENT) {
                return c.IO_value;
            }
        }
        return "";
    }

    private int getImmobilizerEventId() {
        for (IO_Component c : ioComponents) {
            if (c.IO_Id == Teltonika.IMMOBILIZER_EVENT) {
                return Integer.parseInt(c.IO_value, 16);
            }
        }
        return -1;
    }

    public boolean getIn1() {
        for (IO_Component c : ioComponents) {
            if (c.IO_Id == Teltonika.INPUT1) {
                return Integer.parseInt(c.IO_value) == 1;
            }
        }
        return false;
    }

    public boolean getIn2() {
        for (IO_Component c : ioComponents) {
            if (c.IO_Id == Teltonika.INPUT2) {
                return Integer.parseInt(c.IO_value) == 1;
            }
        }
        return false;
    }

    public boolean getIn3() {
        for (IO_Component c : ioComponents) {
            if (c.IO_Id == Teltonika.INPUT3) {
                return Integer.parseInt(c.IO_value) == 1;
            }
        }
        return false;
    }

    public int getInputMask() {
        return ((getIn1()) ? 1 : 0) + ((getIn2()) ? 2 : 0) + ((getIn3()) ? 4 : 0);
    }

    public int getOutputMask() {
        return ((getOut1()) ? 1 : 0) + ((getOut2()) ? 2 : 0);
    }

    public boolean getOut1() {
        for (IO_Component c : ioComponents) {
            if (c.IO_Id == Teltonika.OUTPUT1) {
                return Integer.parseInt(c.IO_value) == 1;
            }
        }
        return false;
    }

    public boolean getOut2() {
        for (IO_Component c : ioComponents) {
            if (c.IO_Id == Teltonika.OUTPUT2) {
                return Integer.parseInt(c.IO_value) == 1;
            }
        }
        return false;
    }

    public boolean getOut3() {
        for (IO_Component c : ioComponents) {
            if (c.IO_Id == Teltonika.OUTPUT3) {
                return Integer.parseInt(c.IO_value) == 1;
            }
        }
        return false;
    }

    public int getAd1() {
        for (IO_Component c : ioComponents) {
            if (c.IO_Id == Teltonika.AD1) {
                return Integer.parseInt(c.IO_value, 16);
            }
        }
        return -1;
    }

    public int getIoId() {
        return this.IO_id;
    }

    public int getLastImmobilizerEventId() {
        return immobilizerEventId;
    }

    public boolean isIn1() {
        return in1;
    }

    public boolean isIn2() {
        return in2;
    }

    public boolean isIn3() {
        return in3;
    }

    public boolean isOut1() {
        return out1;
    }

    public boolean isOut2() {
        return out2;
    }

    public void setIn1(boolean in1) {
        this.in1 = in1;
    }

    public void setIn2(boolean in2) {
        this.in2 = in2;
    }

    public void setIn3(boolean in3) {
        this.in3 = in3;
    }

    public void setOut1(boolean out1) {
        this.out1 = out1;
    }

    public void setOut2(boolean out2) {
        this.out2 = out2;
    }

    public void setAd(int ad) {
        this.ad = ad;
    }

    public long getCurrentDriver() {
        return TCPServer.persistor.getDriverIdFromTagId(Utils.reverseHexPairs(this.getTagId()));
    }

    public CodecIoSection() {
    }

    public int getLength() {
        return this.ioComponents.size();
    }

}
