package ex1;

import javax.swing.table.AbstractTableModel;
import java.util.*;

/**
 * This is the parent class for both CropTableModle and OprnTableModel.
 * It will be used mostly for polymorphism.
 * @author maxerdwien
 */
public abstract class WepsTableModel extends AbstractTableModel {

    public enum TableEnum {

        Operation, Crop, UPGM
    }

    private static final long serialVersionUID = 1L;

    protected DefnFileParser parms;   // this is the meta information about the parameters
    protected DataFileParser pData;   // xml parser for data files
    protected boolean altUnits;   // true if we are in english units
    protected boolean allowEdits;  // true if changes allowed
    protected ArrayList<WepsDBFile> xmlFiles;  // list of crop file loaded
    protected final String dbDir;   // directory where crop files are loaded from
    protected final String mcrewCfgDir;  // directory of config xml files

    /**
     * whether to check the data against bounds for each column
     */
    public boolean checkData = true;
    
    boolean redExists = false;
    boolean yellowExists = false;

    /**
     * the first row to have the status represented in ts
     */
    private ArrayList<Integer> redRow = new ArrayList<>();

    private ArrayList<Integer> redColumn = new ArrayList<>();
    
    private ArrayList<Integer> yellowRow = new ArrayList<>();
    
    private ArrayList<Integer> yellowColumn = new ArrayList<>();
    
    /**
     * We need to know if the table has passed it's first viewing, as indexes
     * are shifted three to the left if so.
     */
    private boolean firstView = true;

    public WepsTableModel(String cfgDir, String dbMainDir) {
        allowEdits = false;
        altUnits = false;

        mcrewCfgDir = cfgDir;
        xmlFiles = new ArrayList<WepsDBFile>();
        dbDir = dbMainDir;

       
    }

    /**
     * setAltUnits()
     *
     * True for English, False for Metric
     *
     * @param val True for English (alternate units) false for metric (default units)
     */
    public void setAltUnits(boolean val) {
        altUnits = val;
    }

    /**
     *
     * Returns a list of the modified files.
     *
     * @return A list of all the files that have been modified seperated by a '\n'
     */
    public String getChangedFiles() {
        StringBuilder sb = new StringBuilder();
        for (WepsDBFile wf : xmlFiles) {
            if (wf != null) {
                if (wf.isModified()) {
                    sb.append(wf.getFileName() + "\n");
                }
            }
        }
        return sb.toString();
    }

    /**
     *
     * Returns true if any file has been modified.
     *
     * @return True if any file has been changed
     */
    public boolean isModified() {
        for (WepsDBFile wf : xmlFiles) {
            if (wf != null) {
                if (wf.isModified()) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     *
     * True if changes are allowed.
     *
     * @param val True to allow edits, false to make crop data readonly
     *
     */
    public void setEdit(boolean val) {
        allowEdits = val;
    }

    /**
     *
     * For a row get the short file name (no path).
     *
     * @param row Specific row to get filename for
     * @return Filename as a String
     *
     */
    public String getFileName(int row) {
        if (row < xmlFiles.size()) {
            WepsDBFile wf = xmlFiles.get(row);
            if (wf != null) {
                return wf.getFileName();
            } else {
                return "???";
            }
        } else {
            return "???";
        }
    }

    /**
     * Get the full path name of the file for a specific row.
     *
     * @param row row to get filename from
     * @return full filename (directory + base + extension)
     */
    public String getFilePath(int row) {
        if (row < xmlFiles.size()) {
            WepsDBFile wf = xmlFiles.get(row);
            if (wf != null) {
                return wf.getPathName();
            } else {
                return "???";
            }
        } else {
            return "???";
        }
    }

    /**
     * Saves the file at the specified row
     *
     * @param row row to save file from
     * @return true if file saved ok
     */
    public boolean saveFile(SavePanelSingle parent, int row) {
        if (row < xmlFiles.size()) {
            WepsDBFile wf = xmlFiles.get(row);
            return wf.saveFile(parent, mcrewCfgDir);
        } else {
            return false;
        }
    }
    
    /**
     * Saves the file at the specified row
     *
     * @param row row to save file from
     * @return true if file saved ok
     */
    public boolean saveFile(int row) {
        if (row < xmlFiles.size()) {
            WepsDBFile wf = xmlFiles.get(row);
            return wf.saveFile(mcrewCfgDir);
        } else {
            return false;
        }
    }

    /**
     * sort()
     *
     * Sort the column data in ascending or descending order. This will reorder the xmlFiles list so
     * that when the table is refreshed the data will be in the requested order.
     *
     * @param col Column to sort on
     * @param status ASCENDING or DESCENDING
     */
    public void sort(int col, int status) {
        TreeMap<String, ArrayList<Integer>> tree = new TreeMap<>(new ParamCompare<String>());
        for (int i = 0; i < xmlFiles.size(); i++) {
            String str = (String) getValueAt(i, col);
            str = str.toUpperCase();
            ArrayList<Integer> v = tree.get(str);
            if (v != null) {
                v.add(i);
            } else {
                ArrayList<Integer> al = new ArrayList<>();
                al.add(i);
                tree.put(str, al);
            }
        }
        ArrayList<WepsDBFile> xmlFiles2 = new ArrayList<>();
        Collection<ArrayList<Integer>> c = tree.values();
        if (status == TableSorter2.ASCENDING) {
            for (ArrayList<Integer> o : c) {
                for (Integer o1 : o) {
                    int inx = o1;
                    xmlFiles2.add(xmlFiles.get(inx));
                }
            }
            xmlFiles = xmlFiles2;
        } else if (status == TableSorter2.DESCENDING) {
            ArrayList<ArrayList<Integer>> temp = new ArrayList<>();
            for (ArrayList<Integer> a : c) {
                temp.add(a);
            }
            Collections.reverse(temp);
            for (ArrayList<Integer> o : temp) {
                for (Integer o1 : o) {
                    int inx = o1;
                    xmlFiles2.add(xmlFiles.get(inx));
                }
            }
            xmlFiles = xmlFiles2;
        } else {
        }
        clearRowStatus();
    }

    /**
     * This returns the structure holding all information about the file on a specific row.
     *
     * @param row row of operations table to get info about
     * @return the structure holding all info about the file
     */
    public WepsDBFile getXmlFile(int row) {
        if (row < xmlFiles.size() && (row >= 0)) {
            return xmlFiles.get(row);
        }
        return null;
    }

    /**
     * This checks if the file associated with a specific row is read-only
     *
     * @param row row to check for read-only properties
     * @return true if row is read-only, false if changes allowed.
     */
    public boolean isReadOnly(int row) {
        if (allowEdits == false) {
            return true;
        }

        if (row < xmlFiles.size()) {
            WepsDBFile wf = xmlFiles.get(row);
            return wf.isReadOnly();
        }else if(row >= xmlFiles.size()){
            //the case that the row is greater due to duplicate processes, return false
            return false;
        }
        return true;
    }

    /**
     * Move one or more rows to the top of the list.
     *
     * @param rows
     */
    public void promote(int rows[]) {
        WepsDBFile wf2;
        ArrayList<WepsDBFile> xmlfiles2 = new ArrayList<WepsDBFile>();
        for (int i = 0; i < rows.length; i++) {
            wf2 = xmlFiles.get(rows[i]);
            xmlfiles2.add(wf2);
        }
        for (int i = 0; i < xmlFiles.size(); i++) {
            boolean included = false;
            for (int j = 0; j < rows.length; j++) {
                if (rows[j] == i) {
                    included = true;
                    break;
                }
            }
            if (included == false) {
                xmlfiles2.add(xmlFiles.get(i));
            }
        }

        xmlFiles = xmlfiles2;
    }

    /**
     * Get structure holding all the parameters loaded
     *
     * @return the DefnFileParser structure which holds all parameters
     */
    public DefnFileParser getParamDef() {
        return parms;
    }

    /**
     * will only increase the severity of the table status, never decrease
     * @param newStatus 
     */
    public void setTableStatus(InputLimits.TableStatus newStatus, int row, int column) {
        // if this is the first noSave spot
        cleanup();
        
        switch(newStatus)
        {
            case OKAY:
                if(redRow.contains(row))
                {
                    if(redColumn.contains(column))
                    {
                        for(int index = 0; index < redRow.size(); index ++)
                        {
                            if(redRow.get(index) == row)
                            {
                                if(redColumn.get(index) == column)
                                {
                                    redRow.remove(index);
                                    redColumn.remove(index);
                                }
                            }
                        }
                    }
                }
                if(yellowRow.contains(row))
                {
                    if(yellowColumn.contains(column))
                    {
                        for(int index = 0; index < yellowRow.size(); index ++)
                        {
                            if(yellowRow.get(index) == row)
                            {
                                if(yellowColumn.get(index) == column)
                                {
                                    yellowRow.remove(index);
                                    yellowColumn.remove(index);
                                }
                            }
                        }
                    }
                }
            break;
            case WARNSAVE:
                if(redRow.contains(row))
                {
                    if(redColumn.contains(column))
                    {
                        for(int index = 0; index < redRow.size(); index ++)
                        {
                            if(redRow.get(index) == row)
                            {
                                if(redColumn.get(index) == column)
                                {
                                    redRow.remove(index);
                                    redColumn.remove(index);
                                }
                            }
                        }
                    }
                }
                yellowRow.add(row);
                yellowColumn.add(column);
            break;
            case NOSAVE:
                if(yellowRow.contains(row))
                {
                    if(yellowColumn.contains(column))
                    {
                        for(int index = 0; index < yellowRow.size(); index ++)
                        {
                            if(yellowRow.get(index) == row)
                            {
                                if(yellowColumn.get(index) == column)
                                {
                                    yellowRow.remove(index);
                                    yellowColumn.remove(index);
                                }
                            }
                        }
                    }
                }
                redRow.add(row);
                redColumn.add(column);
            break;
        }
    }

    /**
     *
     */
    public void checkNames() {
        for (WepsDBFile wf : xmlFiles) {
            if (wf != null) {
                wf.checkName();
            }
        }
    }

    /**
     *
     * @param row
     * @return
     */
    public String getName(int row) {
        WepsDBFile wf = xmlFiles.get(row);
        return wf.getBaseName();
    }

    /**
     *
     * Give a new name to the crop on this row.
     *
     * @param name new name for crop file
     * @param row row that is being renamed
     *
     */
    public void rename(String name, int row) {
        WepsDBFile wf = xmlFiles.get(row);
        if (wf != null) {
            wf.changeName(mcrewCfgDir, name);
        }
    }

    /**
     * Delete the operation on the specified row.
     *
     * @param row row to be deleted
     * @return true if the operation was deleted successfully
     */
    public boolean delete(int row) {
        boolean rc = false;

        WepsDBFile wf = xmlFiles.get(row);
        if (wf != null) {
            rc = wf.deleteFile();
            if (rc) {
                // delete was ok, remove from xmlfiles list
                xmlFiles.remove(row);
            }
        }
        return rc;
    }

    /**
     * Checks the process order for all files and marks any that are in error.
     *
     * @param display true if error messages are to be displayed
     */
    public void checkProcesses(boolean display) {
        for (WepsDBFile wf : xmlFiles) {
            if (wf != null) {
                wf.checkProcesses(display);
            }
        }
    }
    
    private void cleanup()
    {
        for(int item = 0; item < redRow.size(); item ++)
        {
            for(int place = item + 1; place < redRow.size(); place ++)
            {
                if(redRow.get(item).equals(redRow.get(place)))
                {
                    if(redColumn.get(item).equals(redColumn.get(place)))
                    {
                        redRow.remove(place);
                        redColumn.remove(place);
                    }
                }
            }
        }
        for(int item = 0; item < yellowRow.size(); item ++)
        {
            for(int place = item + 1; place < yellowRow.size(); place ++)
            {
                if(yellowRow.get(item).equals(yellowRow.get(place)))
                {
                    if(yellowColumn.get(item).equals(yellowColumn.get(place)))
                    {
                        yellowRow.remove(place);
                        yellowColumn.remove(place);
                    }
                }
            }
        }
    }
    
    public InputLimits.TableStatus getTableStatus() {
        cleanup();
        redExists = !redRow.isEmpty();
        yellowExists = !yellowRow.isEmpty();
        
        if (redExists) {
            return InputLimits.TableStatus.NOSAVE;
        } else if (yellowExists) {
            return InputLimits.TableStatus.WARNSAVE;
        } else {
            return InputLimits.TableStatus.OKAY;
        }
    }
    
    public InputLimits.TableStatus getTableStatus(int row)
    {
        if(redRow.contains(row))
        {
            return InputLimits.TableStatus.NOSAVE;
        }
        if(yellowRow.contains(row))
        {
            return InputLimits.TableStatus.WARNSAVE;
        }
        else
        {
            return InputLimits.TableStatus.OKAY;
        }
    }

    public int getFirstRedRow() {
        return redRow.get(0);
    }

    public int getFirstRedColumn() {
        return redColumn.get(0);
    }
    
    public int getFirstRedColumn(int row)
    {
        return redColumn.get(redRow.indexOf(row));
    }
    
    public int getFirstYellowRow() {
        return yellowRow.get(0);
    }
    
    public int getFirstYellowColumn() {
        return yellowColumn.get(0);
    }
    
    public int getFirstYellowColumn(int row)
    {
        return yellowColumn.get(yellowRow.indexOf(row));
    }
    
    public boolean getFirstView()
    {
        return firstView;
    }
    public void setFirstView(boolean input)
    {
        firstView = input;
    }
    
    void clearRowStatus()
    {
        redRow.clear();
        redColumn.clear();
        yellowRow.clear();
        yellowColumn.clear();
    }
    
    public ArrayList<String> dive()
    {
        ArrayList<String> values = new ArrayList<String>();
        for(WepsDBFile curr : xmlFiles)
        {
            ArrayList<String> temp = curr.dive();
            for(String item : temp)
            {
                if(!values.contains(item))
                {
                    values.add(item);
                }
            }
        }
        return values;
    }

    @Override
    public abstract int getColumnCount();

    @Override
    public abstract int getRowCount();

    @Override
    public abstract String getColumnName(int col);

    @Override
    public abstract Object getValueAt(int row, int col);

    public abstract ParamDef.ColumnType getColumnType(int col);

    @Override
    public abstract Class<?> getColumnClass(int c);

    @Override
    public abstract boolean isCellEditable(int row, int col);

    public abstract void addXmlFile(String file);

    public abstract String saveAll();

    public abstract boolean isModified(int row);

    public abstract boolean nameMismatched(int row);
    
    public abstract boolean outOfOrder(int row);

    public abstract int getLogicalRow(int tab, int row);

    public abstract TableEnum getType();

    public abstract String getTypeString();

    public abstract String getTypeFileExtension();

    public abstract void copy(String name, int row);

}
