/**
*
* Copyright 2013 Radek Henys
* 
* This file is part of Spelling Alphabet Trainer.
*
* Spelling Alphabet Trainer 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.
*
* Spelling Alphabet Trainer 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 Spelling Alphabet Trainer.  If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.mouseviator.spelling.alphabet.trainer;

import java.io.*;
import java.nio.charset.Charset;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.table.AbstractTableModel;

/**
 * Model for Spell Table Editor
 * 
 * @author Murdock
 */
public class SpellTableModel extends AbstractTableModel {
    private List<String[]> data = new ArrayList<>();
    private String properties_file;
    private String[] column_names;
    
    /**
     * Default constructor, sets column names
     */
    public SpellTableModel () {
        column_names = new String[2];
        column_names[0] = "Character";
        column_names[1] = "Spelling";
    }
    
    /**
     * Returns num of rows.
     * 
     * @return Num of rows.
     */
    @Override
    public int getRowCount() {
        return data.size();
    }

    /**
     * Returns num of columns.
     * 
     * @return Num of columns.
     */
    @Override
    public int getColumnCount() {
        return 2;
    }

    /**
     * Returns value at given position.
     * @param rowIndex Cell row index.
     * @param columnIndex Cell column index.
     * @return Returns given cell value.
     */
    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        if (columnIndex >= 0 && columnIndex < getColumnCount() &&
                rowIndex >= 0 && rowIndex < getRowCount()) {
            String[] row = data.get(rowIndex);
            return row[columnIndex];
        } else {
            return null;
        }
    }

    /**
     * Retruns name of properties file from which the data are loaded.
     * 
     * @return Name of properties file.
     */
    public String getPropertiesFile() {
        return properties_file;
    }

    /**
     * Sets the name of the properties file. Please note that no check of file existence is done.
     * 
     * @param properties_file Name of the properties file (full path, relative or absolute)
     */
    public void setPropertiesFile(String properties_file) {
        this.properties_file = properties_file;
    }
    
    /**
     * Sets the name of properties file from which to load the data. If the file is successfully loaded,
     * the data from file are transfered to model.
     * 
     * @param properties_file the properties_file Name of properties file from which to load the data.
     */
    public void loadPropertiesFile(String properties_file) {
        //Try to load properties
        Properties props = new Properties();
        FileInputStream fis = null;
        InputStreamReader reader = null;
        try {
            //clear old data
            data.clear();
            this.properties_file = properties_file;
            fis = new FileInputStream(properties_file);
            reader = new InputStreamReader(fis, Charset.forName("UTF-8"));
            props.load(reader);
            if (props.size() > 0) { 
                //build new data
                for (Iterator it = props.keySet().iterator(); it.hasNext();) {
                    String[] row = new String[2];
                    String key = (String)it.next();
                    row[0] = key;
                    row[1] = props.getProperty(key);
                    data.add(row);
                }
                //force garbage collect
                System.gc();
            }
        } catch (FileNotFoundException ex) {
            Logger.getLogger(SpellTableModel.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(SpellTableModel.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException ex) {
                    Logger.getLogger(SpellTableModel.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException ex) {
                    Logger.getLogger(SpellTableModel.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            //notify about the update
            fireTableDataChanged();
        }
    }
    
    /**
     * Returns name of column with given index.
     * 
     * @param col Column index.
     * @return Name of column with given index.
     */
    @Override
    public String getColumnName(int col) {
        return getColumnNames()[col];
    }

    /**
     * Returns Class of column with given index.
     * 
     * @param c Column index.
     * @return Class of column with given index.
     */
    @Override
    public Class getColumnClass(int c) {
        return getValueAt(0, c).getClass();
    }

    /**
     * Returns column names.
     * 
     * @return Column names.
     */
    public String[] getColumnNames() {
        return column_names;
    }
    
    public List<String[]> getData() {
        return this.data;
    }

    /**
     * Sets the column names.
     * 
     * @param column_names Column names.
     */
    public void setColumnNames(String[] column_names) {
        this.column_names = column_names;
    }
    
    /**
     * Returns if given cell is editable.
     * 
     * @param row Cell row index.
     * @param col Cell column index.
     * @return True if cell is editable, otherwise False.
     */
    @Override
    public boolean isCellEditable(int row, int col) {
        return true;
    }
    
    /**
     * Sets value for given cell.
     * 
     * @param value Value to set.
     * @param row Cell row index.
     * @param col Cell column index.
     */
    @Override
    public void setValueAt(Object value, int row, int col) {
        //ziskame polozku na ktere byla zmena
        if (this.data != null && row >= 0 && row < getRowCount()
                              && col >= 0 && col < getColumnCount()) {
            String[] row_data = data.get(row);
            row_data[col] = String.valueOf(value);
            fireTableCellUpdated(row, col);
        }
    }
    
    /**
     * Function deletes rows specified by given index array.
     * 
     * @param indexes Array of indexes of rows to delete.
     */
    public void deleteRows(int[] indexes) {
        if (this.data != null) {
            Arrays.sort(indexes);
            for (int i = (indexes.length - 1); i >= 0; --i) {
                int index = indexes[i];
                this.data.remove(index);
            }
            fireTableDataChanged();
        }
    }
    
    /**
     * Deletes all data.
     */
    public void deleteAll() {
        data.clear();
        fireTableDataChanged();
    }
    
    /**
     * Function to add row into data. The row consists of two String in array, first is Spelled Character, second is
     * its spelling.
     * 
     * @param row Row to add.
     */
    public void addRow(String[] row) {
        if (this.data != null) {
            this.data.add(row);
            fireTableRowsInserted(this.data.size(), this.data.size());
        }
    }
    
    /**
     * Function saves model data to properties file with given name. Function uses UTF-8 encoding.
     * 
     * @param file_name Name of the properties file.
     * @return True if successfully saved, otherwise False.
     */
    public boolean savePropertiesFile(String file_name) {
        boolean bRet = false;
        
        Properties props = new Properties();
        FileOutputStream fos = null;
        OutputStreamWriter writer = null;
        try {
            for (String[] row_data : data) {
                if (!row_data[0].trim().isEmpty() && !row_data[1].trim().isEmpty()) {
                    props.setProperty(row_data[0].trim(), row_data[1].trim());
                }
            }
            
            fos = new FileOutputStream(file_name);
            writer = new OutputStreamWriter(fos, Charset.forName("UTF-8"));
            props.store(writer, "");
            bRet = true;
        } catch (FileNotFoundException ex) {
            Logger.getLogger(SpellTableModel.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(SpellTableModel.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException ex) {
                    Logger.getLogger(SpellTableModel.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException ex) {
                    Logger.getLogger(SpellTableModel.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            return bRet;
        }
    }
    
    /**
     * Function saves model data to properties file with name: {@link #getPropertiesFile() }
     * 
     * @return True if successfully saved, otherwise False.
     */
    public boolean savePropertiesFile() {
        return savePropertiesFile(properties_file);      
    }
}
