/* UtilLib - Copyright (c) 2014-2019 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package net.mapeadores.util.text.tableparser;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.NoSuchElementException;
import net.mapeadores.util.exceptions.NestedIOException;
import net.mapeadores.util.primitives.io.PrimitivesIOFactory;
import net.mapeadores.util.primitives.io.PrimitivesReader;
import net.mapeadores.util.primitives.io.PrimitivesWriter;


/**
 *
 * @author Vincent Calame
 */
public class CacheRowHandler implements RowHandler {

    private final File dataFile;
    private final OutputStream dataOutputStream;
    private final PrimitivesWriter dataPrimitivesWriter;
    private final RowChecker rowChecker;

    public CacheRowHandler(File dataFile, RowChecker rowChecker) {
        this.dataFile = dataFile;
        this.rowChecker = rowChecker;
        try {
            if (dataFile.exists()) {
                dataFile.delete();
            }
            this.dataOutputStream = new BufferedOutputStream(new FileOutputStream(dataFile));
        } catch (IOException ioe) {
            throw new NestedIOException(ioe);
        }
        this.dataPrimitivesWriter = PrimitivesIOFactory.newWriter(dataOutputStream);
    }

    @Override
    public void addRow(String[] row) {
        if (rowChecker != null) {
            row = rowChecker.checkRow(row);
            if (row == null) {
                return;
            }
        }
        int length = row.length;
        try {
            dataPrimitivesWriter.writeInt(length);
            for (int i = 0; i < length; i++) {
                dataPrimitivesWriter.writeString(row[i]);
            }
        } catch (IOException ioe) {
            throw new NestedIOException(ioe);
        }
    }

    public RowIteratorFactory toRowIteratorFactory() {
        try {
            dataOutputStream.close();
        } catch (IOException ioe) {
            throw new NestedIOException(ioe);
        }
        return new InternalRowIteratorFactory(dataFile);
    }


    private static class InternalRowIteratorFactory implements RowIteratorFactory {

        private final File dataFile;

        private InternalRowIteratorFactory(File dataFile) {
            this.dataFile = dataFile;
        }

        @Override
        public RowIterator newRowIterator() {
            return new InternalRowIterator(dataFile);
        }

    }


    private static class InternalRowIterator implements RowIterator {

        private final RandomAccessFile dataRandomAccessFile;
        private final PrimitivesReader dataPrimitivesReader;

        private InternalRowIterator(File dataFile) {
            try {
                this.dataRandomAccessFile = new RandomAccessFile(dataFile, "r");
                this.dataPrimitivesReader = PrimitivesIOFactory.newReader(dataRandomAccessFile);
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }
        }

        @Override
        public boolean hasNext() {
            try {
                return (dataRandomAccessFile.getFilePointer() < dataRandomAccessFile.length());
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }
        }

        @Override
        public String[] next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            try {
                int length = dataPrimitivesReader.readInt();
                String[] result = new String[length];
                for (int i = 0; i < length; i++) {
                    result[i] = dataPrimitivesReader.readString();
                }
                return result;
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }

        }

        @Override
        public void close() {
            try {
                dataRandomAccessFile.close();
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }
        }

    }

}
