package usda.weru.weps.location;

import de.schlichtherle.truezip.file.TFile;
import de.schlichtherle.truezip.file.TFileReader;
import java.beans.PropertyChangeEvent;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import javax.measure.Quantity;
import tec.uom.se.quantity.Quantities;
import javax.measure.quantity.Length;
import systems.uom.common.USCustomary;
import si.uom.SI;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; 

import usda.weru.gis.latlong.LatLong;
import usda.weru.util.ConfigData;

/**
 *
 * @author Joseph Levin <joelevin@weru.ksu.edu>
 */
public class CligenDataModel extends AbstractStationDataModel {

    private final static Logger LOGGER = LogManager.getLogger(WindgenDataModel.class);
    private final Object DATA_LOCK = new Object();
    private static CligenDataModel c_instance;
    private List<CligenStation> c_stations;

    public static final synchronized CligenDataModel getInstance() {
        if (c_instance == null) {
            c_instance = new CligenDataModel();
        }
        return c_instance;
    }

    public CligenStation getStation(long state, long id) {
        for (CligenStation station : stations()) {
            if (station.getId() == id && station.getState() == (state)) {
                return station;
            }
        }
        return null;
    }

    @Override
    protected void configDataPropertyChange(PropertyChangeEvent event) {
        super.configDataPropertyChange(event);
        switch (event.getPropertyName()) {
            case ConfigData.CliIndex:
                //the backing data has changed.
                clearStations();
                break;
            case ConfigData.GISData:
                fireGISDataChanged();
                break;
        }
    }

    private void clearStations() {
        synchronized (DATA_LOCK) {
            if (c_stations == null) {
                //nothing has changed
                return;
            }
            c_stations = null;
        }
        clearCache();
        fireStationDataChanged();
    }

    @Override
    public List<CligenStation> stations() {
        //lock around the data
        synchronized (DATA_LOCK) {
            if (c_stations == null) {
                c_stations = new LinkedList<CligenStation>();
                readFromFile(c_stations);
            }
            return c_stations;
        }
    }

    private void readFromFile(List<CligenStation> stations) {
        String path = ConfigData.getDefault().getDataParsed(ConfigData.CliIndex);
        if (path == null) {
            return;
        }
        TFile file = new TFile(path);
        if (!file.canRead()) {
            LOGGER.warn("Can not read file: " + file.getAbsolutePath());
        }
        BufferedReader in = null;
        try {
            in = new BufferedReader(new TFileReader(file.getCanOrAbsFile()));
            for (String line = in.readLine(); line != null; line = in.readLine()) {
                line = line.trim();
                if (line.startsWith("#") || line.length() == 0) {
                    //skip comments and blank lines
                    continue;
                }

                String[] parts = line.split("\\s+");

                int offset = 0;
                long state = Long.parseLong(parts[offset + 0].trim());
                long id = Long.parseLong(parts[offset + 1].trim());

                double lat = parseCoordinate(parts, offset + 3);
                double lon = parseCoordinate(parts, offset + 6);

                LatLong latlong = LatLong.valueOf(lat, lon, USCustomary.DEGREE_ANGLE);

                double elevationMeters = Double.parseDouble(parts[offset + 9].trim());
                Quantity<Length> elevation = Quantities.getQuantity(elevationMeters, SI.METRE);

                String name = join(parts, offset + 11);

                CligenStation station = new CligenStation(latlong, elevation, name, state, id);

                if (!stations.contains(station)) {
                    stations.add(station);
                } else {
                    LOGGER.warn("Duplicate cligen station id.  Skipping record: " + station);
                }

            }

            in.close();
        } catch (FileNotFoundException fnfe) {
            LOGGER.error("Cligen index file not found:" + file.getPath());
        } catch (IOException | NumberFormatException e) {
            LOGGER.error("Error reading the cligen index file:" + file.getPath(), e);
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    LOGGER.error("Unable to close stream.", e);
                }
            }
        }
    }

    private String join(String[] parts, int start) {
        StringBuilder buffer = new StringBuilder();
        for (int i = start; i < parts.length; i++) {
            buffer.append(parts[i]);
            buffer.append(" ");
        }
        return buffer.toString().trim();

    }

    private double parseCoordinate(String[] parts, int start) {
        double degree = Double.parseDouble(parts[start].trim());
        double minutes = Double.parseDouble(parts[start + 1].trim());

        double value = degree + (minutes / 60);

        String quadrant = parts[start + 2].trim();
        if ("S".equals(quadrant) || "W".equals(quadrant)) {
            value = value * -1;
        }

        return value;
    }

}
