/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.camel.component.flatpack;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.AbstractList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Properties;
import java.util.function.DoubleSupplier;
import java.util.function.IntSupplier;
import java.util.function.LongSupplier;
import java.util.function.Supplier;

import net.sf.flatpack.DataError;
import net.sf.flatpack.DataSet;
import net.sf.flatpack.Record;
import net.sf.flatpack.ordering.OrderBy;

/**
 * The {@link DataSetList} wraps the {@link DataSet} as a Java {@link List} type so the data can easily be iterated. You
 * can access the {@link DataSet} API from this {@link DataSetList} as it implements {@link DataSet}.
 */
public class DataSetList extends AbstractList<Map<String, Object>> implements DataSet {
    private final DataSet dataSet;

    public DataSetList(DataSet dataSet) {
        this.dataSet = dataSet;
    }

    @Override
    public Map<String, Object> get(int index) {
        dataSet.absolute(index);
        return FlatpackConverter.toMap(dataSet);
    }

    @Override
    public int size() {
        return dataSet.getRowCount();
    }

    @Override
    public Iterator<Map<String, Object>> iterator() {
        dataSet.goTop();

        return new Iterator<Map<String, Object>>() {
            Optional<Record> nextData = Optional.empty();

            @Override
            public boolean hasNext() {
                if (nextData.isPresent()) {
                    return true;
                } else {
                    if (DataSetList.this.next()) {
                        nextData = dataSet.getRecord();
                    } else {
                        nextData = Optional.empty();
                    }
                    return nextData.isPresent();
                }
            }

            @Override
            public Map<String, Object> next() {
                if (nextData.isPresent() || hasNext()) {
                    final Record line = nextData.orElse(null);
                    nextData = Optional.empty();
                    return FlatpackConverter.toMap(line);
                } else {
                    throw new NoSuchElementException();
                }
            }
        };
    }

    // delegate methods
    // --------------------------------------------------------------

    @Override
    public void goTop() {
        dataSet.goTop();
    }

    @Override
    public void goBottom() {
        dataSet.goBottom();
    }

    @Override
    public boolean previous() {
        return dataSet.previous();
    }

    @Override
    public List<DataError> getErrors() {
        return dataSet.getErrors();
    }

    @Override
    public void remove() {
        dataSet.remove();
    }

    @Override
    public int getIndex() {
        return dataSet.getIndex();
    }

    @Override
    public int getRowCount() {
        return dataSet.getRowCount();
    }

    @Override
    public int getErrorCount() {
        return dataSet.getErrorCount();
    }

    @Override
    public boolean isAnError(int lineNo) {
        return dataSet.isAnError(lineNo);
    }

    @Override
    public void orderRows(OrderBy ob) {
        dataSet.orderRows(ob);
    }

    @Override
    public void setLowerCase() {
        dataSet.setLowerCase();
    }

    @Override
    public void setUpperCase() {
        dataSet.setUpperCase();
    }

    @Override
    public void absolute(int localPointer) {
        dataSet.absolute(localPointer);
    }

    @Override
    public void setStrictNumericParse(boolean strictNumericParse) {
        dataSet.setStrictNumericParse(strictNumericParse);
    }

    @Override
    public void setPZConvertProps(Properties props) {
        dataSet.setPZConvertProps(props);
    }

    @Override
    public void setValue(String column, String value) {
        dataSet.setValue(column, value);
    }

    @Override
    public void clearRows() {
        dataSet.clearRows();
    }

    @Override
    public void clearErrors() {
        dataSet.clearErrors();
    }

    @Override
    public void clearAll() {
        dataSet.clearAll();
    }

    @Override
    public String getString(String column) {
        return dataSet.getString(column);
    }

    @Override
    public double getDouble(String column) {
        return dataSet.getDouble(column);
    }

    @Override
    public BigDecimal getBigDecimal(String column) {
        return dataSet.getBigDecimal(column);
    }

    @Override
    public int getInt(String column) {
        return dataSet.getInt(column);
    }

    @Override
    public long getLong(String column) {
        return dataSet.getLong(column);
    }

    @Override
    public Date getDate(String column) throws ParseException {
        return dataSet.getDate(column);
    }

    @Override
    public Date getDate(String column, SimpleDateFormat sdf) throws ParseException {
        return dataSet.getDate(column, sdf);
    }

    @Override
    public Object getObject(String column, Class<?> classToConvertTo) {
        return dataSet.getObject(column, classToConvertTo);
    }

    @Override
    public String[] getColumns() {
        return dataSet.getColumns();
    }

    @Override
    public String[] getColumns(String recordID) {
        return dataSet.getColumns(recordID);
    }

    @Override
    public int getRowNo() {
        return dataSet.getRowNo();
    }

    @Override
    public boolean isRecordID(String recordID) {
        return dataSet.isRecordID(recordID);
    }

    @Override
    public boolean contains(String column) {
        return dataSet.contains(column);
    }

    @Override
    public boolean isRowEmpty() {
        return dataSet.isRowEmpty();
    }

    @Override
    public String getRawData() {
        return dataSet.getRawData();
    }

    @Override
    public boolean next() {
        return dataSet.next();
    }

    @Override
    public String getString(String column, Supplier<String> defaultSupplier) {
        return dataSet.getString(column, defaultSupplier);
    }

    @Override
    public double getDouble(String column, DoubleSupplier defaultSupplier) {
        return dataSet.getDouble(column, defaultSupplier);
    }

    @Override
    public BigDecimal getBigDecimal(String column, Supplier<BigDecimal> defaultSupplier) {
        return dataSet.getBigDecimal(column, defaultSupplier);
    }

    @Override
    public int getInt(String column, IntSupplier defaultSupplier) {
        return dataSet.getInt(column, defaultSupplier);
    }

    @Override
    public long getLong(String column, LongSupplier defaultSupplier) {
        return dataSet.getLong(column, defaultSupplier);
    }

    @Override
    public Date getDate(String column, Supplier<Date> defaultSupplier) throws ParseException {
        return dataSet.getDate(column, defaultSupplier);
    }

    @Override
    public Date getDate(String column, SimpleDateFormat sdf, Supplier<Date> defaultSupplier)
            throws ParseException {
        return dataSet.getDate(column, sdf, defaultSupplier);
    }

    @Override
    public LocalDate getLocalDate(String column, Supplier<LocalDate> defaultSupplier) throws ParseException {
        return dataSet.getLocalDate(column, defaultSupplier);
    }

    @Override
    public LocalDate getLocalDate(String column) throws ParseException {
        return dataSet.getLocalDate(column);
    }

    @Override
    public LocalDate getLocalDate(String column, DateTimeFormatter formatter) throws ParseException {
        return dataSet.getLocalDate(column, formatter);
    }

    @Override
    public LocalDate getLocalDate(String column, String dateFormat, Supplier<LocalDate> defaultSupplier)
            throws ParseException {
        return dataSet.getLocalDate(column, dateFormat, defaultSupplier);
    }

    @Override
    public LocalDate getLocalDate(String column, String dateFormat) throws ParseException {
        return dataSet.getLocalDate(column, dateFormat);
    }

    @Override
    public String getRecordID() {
        return dataSet.getRecordID();
    }

    @Override
    public Optional<Record> getRecord() {
        return dataSet.getRecord();
    }

}
