package demo;

import java.util.ArrayList;

import javax.swing.table.AbstractTableModel;

import gnu.java.zrtp.ZrtpConfigure;
import gnu.java.zrtp.ZrtpConstants;

public class ZrtpConfigureTableModel<T extends Enum<T>> extends AbstractTableModel {

    private final ZrtpConfigure active;
    private final ZrtpConfigure inActive;
    
    // used to identify the Enum class when calling ZrtpConfigure methods.
    private final T algorithm;  
    private final Class<T> clazz;
    private final String savedConfigureKey;
    
    boolean onOff[];

    public ZrtpConfigureTableModel(T algo, ZrtpConfigure act,
            ZrtpConfigure inAct, String savedConf) {
        active = act;
        inActive = inAct;
        algorithm = algo;
        
        clazz = algorithm.getDeclaringClass();
        savedConfigureKey = clazz.getName().replace('$', '_');
        
        initialize(savedConf);
    }

    private void initialize(String savedConf) {

        // Get all enums constants of this Enum to process
        T enumValues[] = clazz.getEnumConstants();

        // first build a list of all available algorithms of this type
        ArrayList<String> fullList = new ArrayList<String>(enumValues.length);
        for (T sh : enumValues) {
            fullList.add(sh.name());
        }

        String savedAlgos[] = savedConf.split(";");
        // Configure saved algorithms as active, remove them from full list of
        // algos
        for (String str : savedAlgos) {
            T algoEnum = Enum.valueOf(clazz, str);
            if (algoEnum != null) {
                active.addAlgo(algoEnum);
                fullList.remove(str);
            }
        }
        // rest of algorithms are inactive
        for (String str : fullList) {
            T algoEnum = Enum.valueOf(clazz, str);
            if (algoEnum != null) {
                inActive.addAlgo(algoEnum);
            }
        }
    }

    public int getColumnCount() { 
        return 2;
    }
    
    public int getRowCount() { 
        return active.getNumConfiguredAlgos(algorithm) + inActive.getNumConfiguredAlgos(algorithm);
    }
    
    public Object getValueAt(int row, int col) {
        switch (col)
        {
        case 0:
            if (row >= active.getNumConfiguredAlgos(algorithm)) {
                return (new Boolean(false));
            }
            return (new Boolean(true));
        case 1:
            if (row >= active.getNumConfiguredAlgos(algorithm)) {
                row -= active.getNumConfiguredAlgos(algorithm);
                return (inActive.getAlgoAt(row, algorithm).name());
            }
            else 
                return (active.getAlgoAt(row, algorithm).name());
        default:
            return null;
        }

    }
    public boolean isCellEditable(int rowIndex, int columnIndex)
    {
        return (columnIndex == 0);
    }
    public Class<?> getColumnClass(int columnIndex)
    {
        return (columnIndex == 0) ? Boolean.class : super
            .getColumnClass(columnIndex);
    }
    
    public void setValueAt(Object value, int row, int column)
    {
        if ((column == 0) && (value instanceof Boolean))
        {
            if (row >= active.getNumConfiguredAlgos(algorithm)) {
                row -= active.getNumConfiguredAlgos(algorithm);
                active.addAlgo(inActive.getAlgoAt(row, algorithm));
                inActive.removeAlgo(inActive.getAlgoAt(row, algorithm));
            }
            else {
                inActive.addAlgo(active.getAlgoAt(row, algorithm));
                active.removeAlgo(active.getAlgoAt(row, algorithm));
            }
            fireTableRowsUpdated(0, getRowCount());
        }
    }

    /**
     * Move a Configuration entry up or down one position.
     * 
     * The "move up" is Converted to a "move down" with modified row index
     * and flags.
     * 
     * @param row
     *        Which row to move
     * @param up
     *        If true move up, else move down
     * @param upSave
     *        Because the functions converts a move up into a move down
     *        this flag shows what the caller intented. Needed to adjust
     *        an index return value.
     * @return
     */
    public int move(int row, boolean up, boolean upSave) {
        if (up) {
            if (row <= 0)
                throw new IllegalArgumentException("rowIndex");

            return move(row - 1, false, upSave) - 1;
        }
        T swap;
        if (row >= (getRowCount() - 1))
            throw new IllegalArgumentException("rowIndex");

        // Can't move down last last entry of active list.
//        if (row == active.getNumConfiguredPubKeys() - 1) {
//            // this was a "move up" of the first inActive member adjust index
//            return upSave ? row + 2 : row;
//        }

//        if (row >= active.getNumConfiguredPubKeys()) {
//            if (inActive.getNumConfiguredPubKeys() <= 1) {
//                return row + 2;
//            }
//            row -= active.getNumConfiguredPubKeys();
//            swap = inActive.getPubKeyAlgoAt(row);
//            inActive.removePubKeyAlgo(swap);
//            inActive.addPubKeyAlgoAt(row + 1, swap);
//            row++;                           // take active rows into account
//        }
//        else {
            if (active.getNumConfiguredAlgos(algorithm) <= 1) {
                return row;
            }
            swap = active.getAlgoAt(row, algorithm);
            active.removeAlgo(swap);
            active.addAlgoAt(row + 1, swap);
//        }

        fireTableRowsUpdated(0, getRowCount());
        return row + 1;
    }
    
    public boolean checkEnableUp(int row) {
        return (row < active.getNumConfiguredAlgos(algorithm));
    }

    public boolean checkEnableDown(int row) {
        return (row < active.getNumConfiguredAlgos(algorithm) - 1);
    }
    
    /**
     * Saves the ZrtpConfigure data for this algorithm to configure file
     */
    public void saveConfig() {
        StringBuffer algoStr = new StringBuffer();
        for (T sh: active.algos(algorithm)) {
            algoStr.append(sh.name());
            algoStr.append(';');
        }
        // save in configuration data using the appropriate key
    }
    
    /**
     * Sets the ZrtpConfigure data for this algorithm to a predefined set.
     * 
     * The caller prepared active ZrtpConfigureto contain a standard set of
     * algorithms. Get the names and construct a string, then call initialize
     * to setup the inActive ZrtpConfigure data.
     */
    public void setStandardConfig() {
        StringBuffer algoStr = new StringBuffer();
        for (T sh: active.algos(algorithm)) {
            algoStr.append(sh.name());
            algoStr.append(';');
        }
        initialize(algoStr.toString());
        fireTableRowsUpdated(0, getRowCount());
    }
}
