// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.public_transport.models;

import static org.openstreetmap.josm.tools.I18n.tr;

import java.util.Iterator;
import java.util.Vector;

import javax.swing.table.DefaultTableModel;

import org.openstreetmap.josm.data.DataSource;
import org.openstreetmap.josm.data.coor.LatLon;
import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.data.osm.Node;
import org.openstreetmap.josm.gui.MainApplication;
import org.openstreetmap.josm.plugins.public_transport.actions.GTFSImporterAction;

/**
 * GTFS stop table model
 */
public class GTFSStopTableModel extends DefaultTableModel {

    public Vector<Node> nodes = new Vector<>();

    public Vector<LatLon> coors = new Vector<>();

    private int idCol = -1;

    private int nameCol = -1;

    private int latCol = -1;

    private int lonCol = -1;

    private char separator = ',';

    public GTFSStopTableModel(GTFSImporterAction controller, String columnConfig) {
        int pos = columnConfig.indexOf(separator);
        if (pos == -1) {
            separator = ';';
            pos = columnConfig.indexOf(separator);
        }
        if (pos == -1) {
            separator = '\t';
            pos = columnConfig.indexOf(separator);
        }
        int oldPos = 0;
        int i = 0;
        while (pos > -1) {
            String title = stripQuot(columnConfig.substring(oldPos, pos));
            if ("stop_id".equals(title))
                idCol = i;
            else if ("stop_name".equals(title))
                nameCol = i;
            else if ("stop_lat".equals(title))
                latCol = i;
            else if ("stop_lon".equals(title))
                lonCol = i;
            ++i;
            oldPos = pos + 1;
            pos = columnConfig.indexOf(separator, oldPos);
        }
        String title = columnConfig.substring(oldPos);
        if ("stop_id".equals(title))
            idCol = i;
        else if ("stop_name".equals(title))
            nameCol = i;
        else if ("stop_lat".equals(title))
            latCol = i;
        else if ("stop_lon".equals(title))
            lonCol = i;

        addColumn(tr("Id"));
        addColumn(tr("Name"));
        addColumn(tr("State"));
    }

    @Override
    public boolean isCellEditable(int row, int column) {
        return false;
    }

    @Override
    public void addRow(Object[] obj) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void insertRow(int insPos, Object[] obj) {
        throw new UnsupportedOperationException();
    }

    public void addRow(String s) {
        insertRow(-1, s, new Vector<Node>());
    }

    public void addRow(String s, Vector<Node> existingStops) {
        insertRow(-1, s, existingStops);
    }

    /**
     * tokenizes a line as follows: any comma outside a pair of double quotation marks is taken as field separator.
     * In particular, neither \" nor \, have a special meaning.
     * @param s the string to tokenize
     * @param startPos  the position of the last field separator plus 1 or the value 0
     * @return the position of the next field separator, if any. Otherwise it returns -1
     */
    private int tokenize(String s, int startPos) {
        int pos = startPos;
        boolean insideDoubleQuoted = false;
        while (pos < s.length()) {
            if ('"' == s.charAt(pos))
                insideDoubleQuoted = !insideDoubleQuoted;
            else if (separator == s.charAt(pos) && !insideDoubleQuoted)
                break;
            ++pos;
        }
        if (pos < s.length())
            return pos;
        else
            return -1;
    }

    private String stripQuot(String s) {
        int pos = s.indexOf('"');
        while (pos > -1) {
            s = s.substring(0, pos) + s.substring(pos + 1);
            pos = s.indexOf('"');
        }
        return s;
    }

    public void insertRow(int insPos, String s, Vector<Node> existingStops) {
        String[] buf = {"", "", tr("pending")};
        int pos = tokenize(s, 0);
        int oldPos = 0;
        int i = 0;
        double lat = 0;
        double lon = 0;
        while (pos > -1) {
            if (i == idCol)
                buf[0] = stripQuot(s.substring(oldPos, pos));
            else if (i == nameCol)
                buf[1] = stripQuot(s.substring(oldPos, pos));
            else if (i == latCol)
                lat = Double.parseDouble(stripQuot(s.substring(oldPos, pos)));
            else if (i == lonCol)
                lon = Double.parseDouble(stripQuot(s.substring(oldPos, pos)));
            ++i;
            oldPos = pos + 1;
            pos = tokenize(s, oldPos);
        }
        if (i == idCol)
            buf[0] = stripQuot(s.substring(oldPos));
        else if (i == nameCol)
            buf[1] = stripQuot(s.substring(oldPos));
        else if (i == latCol)
            lat = Double.parseDouble(stripQuot(s.substring(oldPos)));
        else if (i == lonCol)
            lon = Double.parseDouble(stripQuot(s.substring(oldPos)));

        LatLon coor = new LatLon(lat, lon);

        DataSet ds = MainApplication.getLayerManager().getEditDataSet();
        if (ds != null) {
            boolean inside = false;
            Iterator<DataSource> iter = ds.getDataSources().iterator();
            while (iter.hasNext()) {
                if (iter.next().bounds.contains(coor)) {
                    inside = true;
                    break;
                }
            }
            if (!inside)
                buf[2] = tr("outside");
        }

        boolean nearBusStop = false;
        Iterator<Node> iter = existingStops.iterator();
        while (iter.hasNext()) {
            Node node = iter.next();
            if (coor.greatCircleDistance(node) < 1000) {
                nearBusStop = true;
                break;
            }
        }

        if (insPos == -1) {
            if (nearBusStop || !tr("pending").equals(buf[2]))
                nodes.addElement(null);
            else {
                Node node = GTFSImporterAction.createNode(coor, buf[0], buf[1]);
                nodes.addElement(node);
                buf[2] = tr("added");
            }
            coors.addElement(coor);
            super.addRow(buf);
        } else {
            if (nearBusStop || !tr("pending").equals(buf[2]))
                nodes.insertElementAt(null, insPos);
            else {
                Node node = GTFSImporterAction.createNode(coor, buf[0], buf[1]);
                nodes.insertElementAt(node, insPos);
                buf[2] = tr("added");
            }
            coors.insertElementAt(coor, insPos);
            super.insertRow(insPos, buf);
        }
    }

    public void clear() {
        nodes.clear();
        super.setRowCount(0);
    }
}
