package net.sourceforge.ganttproject;

import java.math.BigDecimal;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;

import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

import net.sourceforge.ganttproject.language.GanttLanguage;
import net.sourceforge.ganttproject.resource.AssignmentNode;
import net.sourceforge.ganttproject.resource.HumanResource;
import net.sourceforge.ganttproject.resource.HumanResourceManager;
import net.sourceforge.ganttproject.resource.ProjectResource;
import net.sourceforge.ganttproject.resource.ResourceManager;
import net.sourceforge.ganttproject.resource.ResourceNode;
import net.sourceforge.ganttproject.roles.Role;
import net.sourceforge.ganttproject.task.ResourceAssignment;

import org.jdesktop.swingx.treetable.DefaultMutableTreeTableNode;
import org.jdesktop.swingx.treetable.DefaultTreeTableModel;
import org.jdesktop.swingx.treetable.TreeTableNode;


public class ResourceTreeTableModel extends DefaultTreeTableModel {
    public static enum DefaultColumn {
        NAME("tableColResourceName", true, String.class) {
            @Override
            Object getValue(Object node) {
                if (node instanceof ResourceNode) {
                    return ((ResourceNode)node).getName();
                }
                else if (node instanceof AssignmentNode) {
                    return ((AssignmentNode)node).getTask().getName();
                }
                throw new IllegalStateException();
            }
            void setValue(Object node, Object value) {
                ((ResourceNode) node).setName((String)value);                
            }
            boolean isEditable(Object node) {
                return node instanceof ResourceNode;
            }
        },
        ROLE("tableColResourceRole") {
            @Override
            Object getValue(Object node) {
                if (node instanceof ResourceNode) {
                    return ((ResourceNode)node).getDefaultRole();
                }
                return "";
            }            
            void setValue(Object node, Object value) {
                ((ResourceNode) node).setDefaultRole((Role)value);                
            }
            boolean isEditable(Object node) {
                return node instanceof ResourceNode;
            }
        },
        EMAIL("tableColResourceEMail"){
            @Override
            Object getValue(Object node) {
                if (node instanceof ResourceNode) {
                    return ((ResourceNode)node).getEMail();
                }
                return "";
            }            
            void setValue(Object node, Object value) {
                ((ResourceNode) node).setEMail((String)value);                
            }
            boolean isEditable(Object node) {
                return node instanceof ResourceNode;
            }
        },
        PHONE("tableColResourcePhone") {
            @Override
            Object getValue(Object node) {
                if (node instanceof ResourceNode) {
                    return ((ResourceNode)node).getPhone();
                }
                return "";
            }            
            void setValue(Object node, Object value) {
                ((ResourceNode) node).setPhone((String)value);                
            }
            boolean isEditable(Object node) {
                return node instanceof ResourceNode;
            }
        },
        ROLE_IN_TASK("tableColResourceRoleForTask") {
            @Override
            Object getValue(Object node) {
                if (node instanceof AssignmentNode) {
                    return ((AssignmentNode)node).getRoleForAssigment();
                }
                return "";
            }            
            void setValue(Object node, Object value) {
                ((AssignmentNode) node).setRoleForAssigment((Role)value);                
            }
            boolean isEditable(Object node) {
                return node instanceof AssignmentNode;
            }
        },
        ID("ID", false, Integer.class) {
            @Override
            Object getValue(Object node) {
                if (node instanceof ResourceNode) {
                    return ((ResourceNode)node).getId();
                }
                return "";
            }            
            void setValue(Object node, Object value) {
                throw new IllegalStateException();
            }
            Object getID() {
                return "rpd5";
            }
        },
        PRICE("tableColResourcePrice", false, BigDecimal.class) {
            @Override
            Object getValue(Object node) {
                if (node instanceof ResourceNode) {
                    return ((ResourceNode)node).getPrice();
                }
                return "";
            }            
            void setValue(Object node, Object value) {
                ((ResourceNode) node).setPrice(BigDecimal.valueOf(Double.parseDouble((String)value)));                
            }
            boolean isEditable(Object node) {
                return node instanceof ResourceNode;
            }
            Object getID() {
                return "rpd6";
            }
        };
        
        private final String myI18Ntifier;
        private boolean isDefaultVisible;
        private final Class myValueClass;

        DefaultColumn(String i18ntifier) {
            this(i18ntifier, false, String.class);
        }
        
        DefaultColumn(String i18ntifier, boolean isDefaultVisible, Class valueClass) {
            myI18Ntifier = i18ntifier;
            this.isDefaultVisible = isDefaultVisible;
            myValueClass = valueClass;
        }
        
        String getName() {
            return i18n(myI18Ntifier);
        }
        
        static String i18n(String key) {
            return GanttLanguage.getInstance().getText(key);
        }
        
        public boolean isDefaultVisible() {
            return isDefaultVisible;
        }
        public Class getValueClass() {
            return myValueClass;
        }

        Object getID() {
            return String.valueOf(ordinal());
        }
        
        abstract Object getValue(Object node);
        abstract void setValue(Object node, Object value);
        boolean isEditable(Object node) {
            return false;
        }
    }

    private DefaultMutableTreeTableNode root = null;

    private final HumanResourceManager myResourceManager;

    public ResourceTreeTableModel(ResourceManager resMgr) {
        super();
        myResourceManager = (HumanResourceManager) resMgr;
        root = buildTree();
        this.setRoot(root);
    }

    public ResourceNode getNodeForResource(ProjectResource resource) {
        ResourceNode res = null;
        Enumeration childs = root.children();
        while (childs.hasMoreElements() && res == null) {
            ResourceNode rn = (ResourceNode) childs.nextElement();
            if (resource.equals(rn.getUserObject()))
                res = rn;
        }
        return res;
    }

    public AssignmentNode getNodeForAssigment(ResourceAssignment assignement) {
        AssignmentNode res = null;
        Enumeration childs = getNodeForResource(assignement.getResource())
                .children();
        while (childs.hasMoreElements() && res == null) {
            AssignmentNode an = (AssignmentNode) childs.nextElement();
            if (assignement.equals(an.getUserObject()))
                res = an;
        }
        return res;
    }

    private ResourceNode buildTree() {

        ResourceNode root = new ResourceNode(null);
        List listResources = myResourceManager.getResources();
        Iterator itRes = listResources.iterator();

        while (itRes.hasNext()) {
            ProjectResource pr = (ProjectResource) itRes.next();

            ResourceAssignment[] tra = pr.getAssignments();
            ResourceNode rnRes = new ResourceNode(pr); // the first for the
            // resource
            root.add(rnRes);
        }

        return root;
    }

    public void updateResources() {
        ProjectResource[] listResources = myResourceManager.getResourcesArray();

        for (int idxResource=0; idxResource<listResources.length; idxResource++) {
            ProjectResource pr = listResources[idxResource];

            ResourceNode rnRes = exists(pr);
            if (rnRes == null) {
                rnRes = new ResourceNode(pr);
            }
            buildAssignmentsSubtree(rnRes);
            if (exists(pr) == null)
                root.add(rnRes);
            modelSupport.fireTreeStructureChanged(new TreePath(rnRes.getPath()));
        }
        // this.setRoot(root);

    }

    ResourceNode exists(ProjectResource pr) {
        ResourceNode res = null;
        Enumeration en = root.children();
        while (res == null && en.hasMoreElements()) {
            ResourceNode rn = (ResourceNode) en.nextElement();
            if (rn.getUserObject().equals(pr))
                res = rn;
        }
        return res;
    }

    private AssignmentNode exists(ResourceNode rn, ResourceAssignment ra) {
        AssignmentNode res = null;
        Enumeration en = rn.children();
        while (res == null && en.hasMoreElements()) {
            AssignmentNode an = (AssignmentNode) en.nextElement();
            if (an.getUserObject().equals(ra))
                res = an;
        }
        return res;
    }

    public DefaultMutableTreeTableNode addResource(ProjectResource people) {
        DefaultMutableTreeTableNode result = new ResourceNode(people); 
        insertNodeInto(result, root, root.getChildCount());
        myResourceManager.toString();
        return result;
    }

    public void deleteResources(ProjectResource[] peoples) {
        for (int i = 0; i < peoples.length; i++) {
            deleteResource(peoples[i]);
        }
    }

    public void deleteResource(ProjectResource people) {
        removeNodeFromParent(getNodeForResource(people));
    }

    public void reset() {
        myResourceManager.clear();
    }

    public List getAllResouces() {
        return myResourceManager.getResources();
    }

    /**
     * {@inheritDoc}
     */
    public int getColumnCount() {
        return DefaultColumn.values().length+myResourceManager.getDefinitions().size();
    }
    
    /**
     * {@inheritDoc}
     */
    public Class getColumnClass(int colIndex) {
        if (colIndex < DefaultColumn.values().length) {
            return DefaultColumn.values()[colIndex].getValueClass();
        }
        return myResourceManager.getDefinitions().get(colIndex-DefaultColumn.values().length).getType();
    }

    public String getColumnName(int column) {
        if (column >= DefaultColumn.values().length) {
            return myResourceManager.getDefinitions().get(column-DefaultColumn.values().length).getName();
        }
        return DefaultColumn.values()[column].getName();
    }

    /**
     * @inheritDoc
     */
    public boolean isCellEditable(Object node, int column) {
        if (column < DefaultColumn.values().length) {
            return DefaultColumn.values()[column].isEditable(node);
        }
        return node instanceof ResourceNode;
    }

    
    /**
     * @inheritDoc
     */
    public Object getValueAt(Object node, int column) {
        if (column < DefaultColumn.values().length) {
            return DefaultColumn.values()[column].getValue(node);
        }
        if (node instanceof ResourceNode) {
            return ((ResourceNode)node).getCustomField(
                    myResourceManager.getDefinitions().get(column-DefaultColumn.values().length).getID());
        }
        return "";
    }

    /**
     * @inheritDoc
     */
    public void setValueAt(Object value, Object node, int column) {
        if (isCellEditable(node, column)) {
            if (column < DefaultColumn.values().length) {
                DefaultColumn.values()[column].setValue(node, value);
            }
            else {
                ((ResourceNode)node).setCustomField(
                        myResourceManager.getDefinitions().get(column-DefaultColumn.values().length).getID(), 
                        value);
            }
        }
    }

    public void addCustomColumn(CustomPropertyDefinition def) {
        
    }

    /** deletes a custom column from the datamodel */
    public void deleteCustomColumn(CustomPropertyDefinition def) {
        //myResourceManager.deleteDefinition(def);
    }

    /** checks if the given column is removable */
    public boolean checkRemovableCol(String name) {
        /* only custom columns are removable */
        return ((HumanResourceManager) myResourceManager)
                .checkCustomField(name);
    }

    public void resourceChanged(ProjectResource resource) {
        ResourceNode node = getNodeForResource(resource);
        if (node == null) {
            return;
        }
        TreeTableNode parent = node.getParent();
        int index = parent.getIndex(node);
        assert index >= 0;
        modelSupport.fireTreeStructureChanged(new TreePath(node.getPath()));
    }
    
    public void resourceAssignmentsChanged(ProjectResource[] resources) {
        for (int i=0; i<resources.length; i++) {
            ResourceNode nextNode = exists(resources[i]);
            buildAssignmentsSubtree(nextNode);
        }
    }
    
    /** 
     * It updates the line number of 
     * the resources and their assignments.
     */
    public void updateLineNumber() {
        List res = myResourceManager.getResources();	
        
        int lineCounter = 1;
        for (Iterator i=res.iterator();i.hasNext();){
            HumanResource resource = (HumanResource) i.next();
            ResourceAssignment[] tra = resource.getAssignments();
            ResourceNode resNode = exists(resource);
            
            String lineNumber = lineCounter + "";
            resource.setLineNumber(lineNumber);
            lineCounter++;
            
            if ( resNode == null){
                resNode = new ResourceNode(resource);
            }
            for(int k=0; k<tra.length; k++){
                AssignmentNode an = exists(resNode, tra[k]);
                lineNumber = lineCounter + "";
                an.setLineNumber(lineNumber);
                lineCounter++;
            }
        }	
    }
    
    private void buildAssignmentsSubtree(ResourceNode resourceNode) {
        ProjectResource resource = resourceNode.getResource();
        resourceNode.removeAllChildren();
        ResourceAssignment[] assignments = resource.getAssignments();
        int[] indices = new int[assignments.length];
        TreeNode[] children = new TreeNode[assignments.length];
        if (assignments.length>0) {
            for (int i = 0; i < assignments.length; i++) {
                indices[i] = i;
                AssignmentNode an = new AssignmentNode(assignments[i]);
                children[i] = an;
                resourceNode.add(an);
            }
        }
        modelSupport.fireTreeStructureChanged(new TreePath(resourceNode.getPath()));
        
    }   
}
