/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.javafxdata.datasources.protocol;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import javafx.application.Platform;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.util.Callback;
import org.javafxdata.datasources.io.DataSourceReader;

/**
 *
 * @author johan
 */
public class CSVDataSource<S, T> extends DataSource<String[]> implements ListWrapper, TableWrapper {

  // Once the TableColumns are created, they are cached
  private ObservableList<TableColumn<S, T>> tableColumns;
  // The list of data in this source, with each entry in the list a DataRow
  //  private List<DataRow<T[]>> data = new LinkedList<DataRow<T[]>>();
  // The list with all column names encountered in this CSV
  private List<String> columnNames = new LinkedList<String>();
  // The list with the column names that are asked by the Control 
  private List<String> selectedColumnNames = new LinkedList<String>();
  private static final char DEFAULT_SEPARATOR = ',';
  // By default we assume that there are no 'header' rows in the CSV file
  private static final int DEFAULT_FROM_LINE = 1;
  // Often-times the first row consists of table column names, so we ignore it
  private final int fromLine = DEFAULT_FROM_LINE;
  // The separator character to use whilst parsing in this instance.
  private final char separator = DEFAULT_SEPARATOR;

  public CSVDataSource(DataSourceReader reader) {
    this(reader, new String[0]);
  }

  public CSVDataSource(DataSourceReader reader, String... cols) {
    for (String col : cols) {
      selectedColumnNames.add(col);
    }

    DataRetriever retriever = new DataRetriever(reader);
    retriever.start();

  }

  // @Override
  public ObservableList<TableColumn<S, T>> getColumns() {
    if (selectedColumnNames == null) {
      return FXCollections.emptyObservableList();
    }

    if (tableColumns != null) {
      return tableColumns;
    }

    // build a tableColumn for each selectedColumnName
    tableColumns = FXCollections.observableArrayList();
    for (final String columnName : selectedColumnNames) {
      TableColumn<S, T> tc = new TableColumn<S, T>(columnName);

      tc.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<S, T>, ObservableValue<T>>() {

        @Override
        public ObservableValue<T> call(CellDataFeatures<S, T> cdf) {
          S myRow = cdf.getValue();
          return (ObservableValue<T>) new ReadOnlyStringWrapper(getMyElement(myRow, columnName));
        }
      });

      tableColumns.add(tc);
    }
    return tableColumns;
  }

  private String getMyElement(S myRow, String columnName) {
    T[] row = (T[]) myRow;
    int columnIndex = columnNames.indexOf(columnName);
    return (String) row[columnIndex];
  }

  class DataRetriever extends Service {

    private final DataSourceReader myReader;

    public DataRetriever(DataSourceReader reader) {
      this.myReader = reader;
    }

    @Override
    protected Task createTask() {
      Task task = new Task() {

        @Override
        protected Object call() throws Exception {
          try {
            BufferedReader br = new BufferedReader(new InputStreamReader(myReader.getInputStream()));
            String line;
            int row = 0;
            while ((line = br.readLine()) != null) {

              if (line.trim().isEmpty()) {
                continue;
              }

              final String[] rowData = line.split(String.valueOf(separator));

              // If we are on the first row, create TableColumn instances
              if (row == 0) {
                columnNames.addAll(Arrays.asList(rowData));
              }

              // line to start reading
              if (row++ < fromLine) {
                continue;
              }
              //          DataRow newRow = new DataRow(row - 2, rowData);
              Platform.runLater(new Runnable() {

                @Override
                public void run() {
                  add (rowData);
                }
              });
              
            }
          } catch (Exception e) {
          }
          return this;
        }
      };
      return task;
    }
  }
}
