/**
 * The MIT License
 * Copyright © 2020 Luis Andrés Lange <http://javacomm.net>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.nexuswob.gui;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.table.AbstractTableModel;
import org.nexuswob.util.Adapter;
import org.nexuswob.util.Cell;
import org.nexuswob.util.Util.Record;



public class TableModel3 extends AbstractTableModel implements Adapter {

  private static final long serialVersionUID = -4536795610555897947L;
//  private final static Logger log = LogManager.getLogger(TableModel3.class);

  private int columnCount = 0;
  private List<String> header;
  private List<Record> records;

  public TableModel3() {
    header = new ArrayList<>();
    records = new ArrayList<Record>();
  }



  /**
   * Die Spaltenüberschriften werden übergeben. Über diese Methode können nur
   * Überschriften vergeben werden, wenn keine vorhanden sind.
   *
   *
   * @param columnNames
   *                    die Überschriften
   */
  public void setHeader(String... columnNames) {
    if (getColumnCount() > 0) throw new HeaderException("Überschriften sind bereits vorhanden");
    header.addAll(Arrays.asList(columnNames));
    setColumnCount(header.size());
    fireTableStructureChanged(); // die Tabellenstruktur wie die Überschriften haben sich geändert
  }



  @Override
  public String getColumnName(int column) {
    return getHeader().get(column);
  }



  /**
   * Ändere den Spaltennamen. Die erste Spalte hat den Index 0.
   *
   *
   * @param value
   *              der neue Spaltenname
   * @param der
   *              Spaltenindex
   */
  @Deprecated
  public void replaceColumnName(String value, int index) {
    if ((0 > index) || (index > this.getColumnCount()))
      throw new HeaderException("die Spalte <" + String.valueOf(index) + "> ist nicht vorhanden");
    getHeader().set(index, value);
    fireTableStructureChanged(); // die Tabellenstruktur wie die Überschriften haben sich geändert

  }



  /**
   * Die aktuellen Spaltenüberschriften werden zurückgegeben.
   *
   * @return alle Spaltenüberschriften
   */
  public List<String> getHeader() {
    return header;
  }



  void setColumnCount(int value) {
    columnCount = value;
  }



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



  @Override
  public int getColumnCount() {
    return columnCount;
  }



  @Override
  public Class<?> getColumnClass(int c) {
    return getValueAt(0, c).getClass();
  }



  @Override
  public Object getValueAt(int rowIndex, int columnIndex) {
    return records.get(rowIndex).getCellAt(columnIndex).getValue();
  }



  /**
   * Ein Datensatz wird am Ende der Tabelle eingefügt.
   *
   * @param record
   *               Datensatz
   */
  public void addRow(Record record) {
    if (record.countColumns() > getColumnCount())
      throw new RecordException("Der eingefügte Datensatz hat zu viele Spalten");
    records.add(record);
    fireTableRowsInserted(getRowCount() - 1, getRowCount() - 1);
  }



  /**
   * Alle Datensätze werden gelöscht. Die Tabelle ist leer.
   *
   */
  public void removeAll() {
    records.clear();
    this.fireTableDataChanged();
  }



  @Override
  public void setValueAt(Object value, int row, int column) {
    if (0 > row && row > getRowCount())
      throw new RecordException("Der Datensatz <" + String.valueOf(row) + "> ist nicht vorhanden");
    if (0 > column && column > getColumnCount())
      throw new RecordException("Die Spalte <" + String.valueOf(row) + "> ist nicht vorhanden");
    @SuppressWarnings("unchecked")
    Cell<Object> cell = (Cell<Object>) records.get(row).getCellAt(column);
    switch(cell.getValue().getClass().getSimpleName()) {
      case "String":
        cell.setValue(value.toString());
        break;
      case "Integer":
        cell.setValue(value);
        break;
      case "Boolean":
        cell.setValue(value);
        break;
      case "Long":
        cell.setValue(value);
        break;
      case "Byte":
        cell.setValue(value);
        break;
      case "Short":
        cell.setValue(value);
        break;
      case "Object":
        cell.setValue(value);
        break;
      case "Float":
        cell.setValue(value);
        break;
      case "Double":
        cell.setValue(value);
        break;
      default:
        cell.setValue(value);
//        throw new RecordException(
//            "Der Datentyp<" + cell.getValue().getClass().getSimpleName() + "> wird nicht unterstützt"
//        );
    }
    fireTableCellUpdated(row, column);
  }



  /**
   * Lies alle Datensätze aus der Tabelle.
   *
   * @return alle Records
   */
  @Override
  public List<Record> getRecords() {
    return records;
  }



  /**
   * Hole den n-ten Datensatz. {@literal 0} ist der erste Datensatz.
   *
   * @param n
   *          der n-te Datensatz
   * @return ein Datensatz
   */
  public Record getRecordAt(int n) {
    if (0 > n || n > getRecords().size())
      throw new RecordException("Der Datensatz <" + String.valueOf(n) + "> ist nicht vorhanden");
    return records.get(n);
  }



  /**
   * Lösche den n-ten Datensatz. Der 0-te Datensatz ist der erste Datensatz.
   *
   *
   * @param n
   *          der n-te Datensatz
   *
   * @return {@code true}, der Datensatz konnte gelöscht werden
   *
   */
  public boolean removeRowAt(int n) {
    Record toDelete = getRecordAt(n);
    if (!records.remove(toDelete)) return false;
    fireTableRowsDeleted(n, n);
    return true;
  }



  /**
   * Ersetze den n-ten Datensatz mit einem neuen Record. Der 0-te Datensatz ist
   * der 1-te Datensatz.
   *
   * @param row
   *                  der n-te Datensatz
   * @param newRecord
   *                  ein Record
   *
   *
   */
  public void replaceRecord(int row, Record newRecord) {
    if (
      0 > row || row > getRecords().size()
    ) throw new RecordException(
        "Der Datensatz <" + String.valueOf(row) + "> ist nicht vorhanden und kann nicht getauscht werden"
    );
    records.set(row, newRecord);
    this.fireTableRowsUpdated(row, row);
  }

}
