// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.opendata.core.io.geographic;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
import java.util.List;

import org.geotools.data.shapefile.dbf.DbaseFileReader;
import org.geotools.data.shapefile.dbf.DbaseFileReader.Row;
import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.gui.progress.ProgressMonitor;
import org.openstreetmap.josm.io.IllegalDataException;
import org.openstreetmap.josm.plugins.opendata.core.OdConstants;
import org.openstreetmap.josm.plugins.opendata.core.datasets.AbstractDataSetHandler;
import org.openstreetmap.josm.plugins.opendata.core.io.tabular.SpreadSheetHandler;
import org.openstreetmap.josm.plugins.opendata.core.io.tabular.SpreadSheetReader;
import org.openstreetmap.josm.tools.Logging;

/**
 * MapInfo TAB reader
 *
 */
public class TabReader extends AbstractMapInfoReader {

    private Charset datCharset;
    private final AbstractDataSetHandler handler;

    public TabReader(AbstractDataSetHandler handler) {
        this.handler = handler;
    }

    public static DataSet parseDataSet(InputStream in, File file,
            AbstractDataSetHandler handler, ProgressMonitor instance) throws IOException, IllegalDataException {
        return new TabReader(handler).parse(in, file, instance, Charset.forName(OdConstants.ISO8859_15));
    }

    private class TabOsmReader extends SpreadSheetReader {

        private final DbaseFileReader dbfReader;
        TabOsmReader(SpreadSheetHandler handler, TabFiles tabFiles) throws IOException {
            super(handler);
            this.dbfReader = new DbaseFileReader(tabFiles, false, datCharset, null);
        }

        @Override
        protected void initResources(InputStream in,
                ProgressMonitor progressMonitor) throws IOException {
        }

        @Override
        protected String[] readLine(ProgressMonitor progressMonitor)
                throws IOException {
            if (!dbfReader.hasNext()) {
                return null;
            }
            List<String> result = new ArrayList<>();
            Row row = dbfReader.readRow();
            for (int i = 0; i < columns.size(); i++) {
                Object o = row.read(i);
                if (o != null) {
                    result.add(o.toString());
                } else {
                    result.add("");
                }
            }
            return result.toArray(new String[0]);
        }
    }

    private DataSet parse(InputStream in, File file, ProgressMonitor instance, Charset charset) throws IOException, IllegalDataException {
        headerReader = new BufferedReader(new InputStreamReader(in, charset));
        parseHeader();
        try {
            File dataFile = getDataFile(file, ".dat");
            ds = new TabOsmReader(handler != null ? handler.getSpreadSheetHandler() : null, new TabFiles(file, dataFile)).
                    doParse(columns.toArray(new String[0]), instance);
        } catch (IOException e) {
            Logging.error(e.getMessage());
            Logging.debug(e);
        }
        return ds;
    }

    @Override
    protected void parseHeaderLine(String[] words) throws IOException {
        if ("!table".equalsIgnoreCase(words[0])) {
            // Do nothing
        } else if ("!version".equalsIgnoreCase(words[0])) {
            parseVersion(words);
        } else if ("!charset".equalsIgnoreCase(words[0])) {
            parseCharset(words);
        } else if (numcolumns > 0) {
            parseField(words);
        } else if ("Definition".equalsIgnoreCase(words[0])) {
            // Do nothing
        } else if ("Type".equalsIgnoreCase(words[0])) {
            parseType(words);
        } else if ("Fields".equalsIgnoreCase(words[0])) {
            parseColumns(words);
        } else if (!line.isEmpty()) {
            Logging.warn("Line "+lineNum+". Unknown clause in header: "+line);
        }
    }

    private void parseField(String[] words) {
        columns.add(words[0]);
        --numcolumns;
    }

    private void parseType(String[] words) throws IllegalCharsetNameException, UnsupportedCharsetException {
        if ("NATIVE".equalsIgnoreCase(words[1]) && "Charset".equalsIgnoreCase(words[2])) {
            datCharset = parseCharset(words, 3);
        } else {
            Logging.warn("Line "+lineNum+". Unknown Type clause in header: "+line);
        }
    }
}
