/*
 * #%L
 * gitools-ui-app
 * %%
 * Copyright (C) 2013 Universitat Pompeu Fabra - Biomedical Genomics group
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public 
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #L%
 */
package org.gitools.ui.app.fileimport.wizard.text.reader;

import org.gitools.api.analysis.IProgressMonitor;
import org.gitools.api.resource.IResourceLocator;
import org.gitools.utils.readers.FileField;
import org.gitools.utils.readers.FileHeader;
import org.gitools.utils.readers.MatrixReaderProfile;
import org.gitools.utils.readers.profile.ReaderProfile;
import org.gitools.utils.readers.profile.Separator;
import org.gitools.utils.readers.profile.TableReaderProfile;
import org.gitools.utils.readers.text.CSVParser;
import org.gitools.utils.readers.text.CSVReader;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import static org.gitools.utils.readers.profile.Separator.COMMA;
import static org.gitools.utils.readers.profile.Separator.TAB;

public class FlatTextImporter implements Closeable {

    private final IResourceLocator locator;
    private CSVReader reader = null;
    private List<FileHeader> headers;
    private int previewLength = 20;
    private List<List<FileField>> preview;
    private ReaderProfile readerProfile;
    private ReaderAssistant readerAssistant;
    private boolean previewMode;
    private String[] currentLine;
    private IProgressMonitor monitor;


    public FlatTextImporter(IResourceLocator locator, IProgressMonitor monitor, boolean previewMode, ReaderProfile profile) {
        super();
        this.monitor = monitor;
        this.locator = locator;
        this.readerProfile = profile == null ? new TableReaderProfile() : profile;
        this.previewMode = previewMode;
        guessSeparator(locator);
        loadHead(monitor);
        if (profile == null) {
            this.readerProfile = guessDataLayout();
        }
        setAssistant(readerProfile);
    }

    public FlatTextImporter(IResourceLocator locator, IProgressMonitor monitor, boolean previewMode) {
        this(locator, monitor, previewMode, null);
    }

    private ReaderProfile guessDataLayout() {

        // if top left field is empty, layout is probably a matrix
        if (getFileHeaders().get(0).getLabel().equals("")) {
            return MatrixReaderProfile.fromProfile(readerProfile);
        } else {
            return readerProfile;
        }
    }

    private void guessSeparator(IResourceLocator locator) {
        // Choose COMMA separator if it's a CSV
        if ("csv".equalsIgnoreCase(locator.getExtension())) {
            readerProfile.setSeparator(COMMA);
        } else if ("tsv".equalsIgnoreCase(locator.getExtension()) || "tab".equalsIgnoreCase(locator.getExtension())) {
            readerProfile.setSeparator(TAB);
        } else {
            // try to guess:
            int recordOfFields = 0;
            for (Separator sep : Separator.values()) {
                Separator oldSep = readerProfile.getSeparator();
                readerProfile.setSeparator(sep);
                loadHead(monitor);
                if (headers.size() < recordOfFields) {
                    readerProfile.setSeparator(oldSep);
                } else {
                    recordOfFields = headers.size();
                }
            }
        }
    }

    private void setAssistant(ReaderProfile profile) {
        this.readerAssistant = profile.getLayout().equals(ReaderProfile.MATRIX) ?
                new MatrixReaderAssistant(this) : new TableReaderAssistant(this);
    }


    public void loadHead(IProgressMonitor monitor) {
        monitor.begin("Loading preview: " + locator.getName(), 0);
        reader = null;
        try {
            reader = newCSVReader(monitor);


            // Load file headers
            String[] line = reader.readNext();
            headers = new ArrayList<>(line.length);
            for (int i = 0; i < line.length; i++) {
                headers.add(new FileHeader(line[i], i));
            }

            //Load preview
            if (previewLength > 0 && previewMode) {
                preview = new ArrayList<>();
                for (int i = 0; i < previewLength; i++) {
                    line = reader.readNext();
                    if (line != null) {
                        ArrayList<FileField> fields = new ArrayList<FileField>(line.length);
                        int previewFields = line.length;
                        for (int j = 0; j < previewFields; j++) {
                            fields.add(new FileField(line[j], j, i));
                        }
                        preview.add(fields);
                    } else {
                        previewLength = i;
                    }
                }
                reader.close();
            }

            if (readerAssistant != null) {
                readerAssistant.update();
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private CSVReader newCSVReader(IProgressMonitor monitor) throws IOException {
        CSVReader reader = new CSVReader(
                new InputStreamReader(locator.openInputStream(monitor)),
                readerProfile.getSeparator().getChar(),
                CSVParser.DEFAULT_QUOTE_CHARACTER,
                CSVParser.DEFAULT_ESCAPE_CHARACTER,
                readerProfile.getCommentChar(),
                readerProfile.getSkipLines(),
                CSVParser.DEFAULT_STRICT_QUOTES,
                CSVParser.DEFAULT_IGNORE_LEADING_WHITESPACE);
        return reader;
    }

    public boolean readNext() throws IOException {

        if (reader == null) {
            loadHead(monitor);
        }

        currentLine = reader.readNext();
        if (currentLine == null) {
            return false;
        }
        return true;
    }

    public List<FileHeader> getFileHeaders() {
        return headers;
    }

    public List<List<FileField>> getPreview() {
        return preview;
    }

    public IResourceLocator getLocator() {
        return locator;
    }

    public int getLastRowNum() {
        //TODO
        return 0;
    }

    public ReaderProfile getReaderProfile() {
        return readerProfile;
    }

    public void setReaderProfile(ReaderProfile readerProfile) {
        this.readerProfile = readerProfile;
        setAssistant(readerProfile);
    }

    public ReaderAssistant getReaderAssistant() {
        return readerAssistant;
    }

    @Override
    public void close() throws IOException {
        if (reader != null) {
            reader.close();
        }
    }

    public void setPreviewMode(boolean previewMode) {
        this.previewMode = previewMode;
        loadHead(monitor);
    }

    public String[] getCurrentLine() {
        return currentLine;
    }
}
