package net.sourceforge.ganttproject;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Vector;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;

import net.sourceforge.ganttproject.delay.Delay;
import net.sourceforge.ganttproject.language.GanttLanguage;
import net.sourceforge.ganttproject.language.GanttLanguage.Event;
import net.sourceforge.ganttproject.task.CustomColumnsException;
import net.sourceforge.ganttproject.task.ResourceAssignment;
import net.sourceforge.ganttproject.task.Task;
import net.sourceforge.ganttproject.task.TaskContainmentHierarchyFacade;
import net.sourceforge.ganttproject.task.TaskInfo;
import net.sourceforge.ganttproject.task.TaskLength;
import net.sourceforge.ganttproject.task.TaskNode;
import net.sourceforge.ganttproject.task.dependency.TaskDependency;

import org.jdesktop.swing.treetable.DefaultTreeTableModel;

/**
 * This class is the model for GanttTreeTable to display tasks.
 * 
 * @author bbaranne (Benoit Baranne)
 */
public class GanttTreeTableModel extends DefaultTreeTableModel implements
		TableColumnModelListener, TaskContainmentHierarchyFacade, GanttLanguage.Listener {

	private static GanttLanguage language = GanttLanguage.getInstance();

	public static String strColType = null;

	public static String strColPriority = null;

	public static String strColInfo = null;

	public static String strColName = null;

	public static String strColBegDate = null;

	public static String strColEndDate = null;

	public static String strColDuration = null;

	public static String strColCompletion = null;

	public static String strColCoordinator = null;

	public static String strColPredecessors = null;

	public static String strColID = null;

	/** The colums titles */
	public List titles = null;

	/**
	 * Custom columns list.
	 */
	private Vector customColumns = null;

	/**
	 * Number of columns (presently in the model)
	 */
	private int nbCol = 11;

	/**
	 * Number of columns (at all, even hiden)
	 */
	private int nbColTot = nbCol;

	/**
	 * Creates an instance of GanttTreeTableModel with a root.
	 * 
	 * @param root
	 *            The root.
	 */
	public GanttTreeTableModel(TreeNode root) {
		super(root);
		titles = new ArrayList();
		customColumns = new Vector();
		changeLanguage(language);
	}

	/**
	 * Changes the language.
	 * 
	 * @param ganttLanguage
	 *            New language to use.
	 */
	public void changeLanguage(GanttLanguage ganttLanguage) {
		strColType = language.getText("tableColType");
		strColPriority = language.getText("tableColPriority");
		strColInfo = language.getText("tableColInfo");
		strColName = language.getText("tableColName");
		strColBegDate = language.getText("tableColBegDate");
		strColEndDate = language.getText("tableColEndDate");
		strColDuration = language.getText("tableColDuration");
		strColCompletion = language.getText("tableColCompletion");
		strColCoordinator = language.getText("tableColCoordinator");
		strColPredecessors = language.getText("tableColPredecessors");
		strColID = language.getText("tableColID");

		titles.clear();
		String[] cols = new String[] { strColType, strColPriority, strColInfo,
				strColName, strColBegDate, strColEndDate, strColDuration,
				strColCompletion, strColCoordinator, strColPredecessors,
				strColID };
		for (int i = 0; i < cols.length; i++)
			titles.add(new String(cols[i]));
	}

	/**
	 * Invoked this to insert newChild at location index in parents children.
	 * This will then message nodesWereInserted to create the appropriate event.
	 * This is the preferred way to add children as it will create the
	 * appropriate event.
	 */
	public void insertNodeInto(MutableTreeNode newChild,
			MutableTreeNode parent, int index) {
		parent.insert(newChild, index);

		int[] newIndexs = new int[1];

		newIndexs[0] = index;
		nodesWereInserted(parent, newIndexs);
	}

	/**
	 * Message this to remove node from its parent. This will message
	 * nodesWereRemoved to create the appropriate event. This is the preferred
	 * way to remove a node as it handles the event creation for you.
	 */
	public void removeNodeFromParent(MutableTreeNode node) {
		MutableTreeNode parent = (MutableTreeNode) node.getParent();

		if (parent == null)
			throw new IllegalArgumentException("node does not have a parent.");

		int[] childIndex = new int[1];
		Object[] removedArray = new Object[1];

		childIndex[0] = parent.getIndex(node);
		parent.remove(childIndex[0]);
		removedArray[0] = node;
		nodesWereRemoved(parent, childIndex, removedArray);
	}

	/**
	 * Add a custom column to the model.
	 * 
	 * @param title
	 */
	public void addCustomColumn(String title) {
		customColumns.add(title);
		nbColTot++;
	}

	/**
	 * Delete a custom column.
	 * 
	 * @param title
	 */
	public void deleteCustomColumn(String title) {
		customColumns.remove(title);
		this.columnRemoved(null);
		nbColTot--;
	}

	public void renameCustomColumn(String oldName, String newName) {
		customColumns.set(customColumns.indexOf(oldName), newName);
	}

	// /**
	// * Returns the number of custom columns.
	// * @return
	// */
	// public int getCustomColumnCount()
	// {
	// return customColumns.size();
	// }

	public int getColumnCount() {
		return nbCol;
	}

	public int getColumnCountTotal() {
		return nbColTot;
	}

	/**
	 * {@inheritDoc}
	 */
	public Class getColumnClass(int column) {
		switch (column) {
		case 0:
		case 1:
		case 2:
			return Icon.class;
		case 3:
			return hierarchicalColumnClass;
		case 4:
		case 5:
			return GregorianCalendar.class;
		case 6:
		case 7:
			return Integer.class;
		case 8:
			return String.class;
		case 9:
			return String.class;
		case 10:
			return Integer.class;
		default: {
			TaskNode tn = (TaskNode) this.getRoot();
			// tn = (TaskNode)tn.children().nextElement();
			Object o = this.getValueAt(tn, column);
			if (o == null) {
				o = "erreur";
				System.err.println("!!!!!!!!!!!!!!!!!");
			}
			return o.getClass();
		}
		}
	}

	public String getColumnName(int column) {
		if (column < titles.size())
			return (String) titles.get(column);

		try {
			return (String) customColumns.get(column - titles.size());
		} catch (IndexOutOfBoundsException e) {
			return (String) customColumns.get(column - titles.size() - 1);
		}

	}

	/**
	 * @inheritDoc
	 */
	public boolean isCellEditable(Object node, int column) {
		if (node instanceof TaskNode)
			return column != 8 && column != 9 && column != 10 && column !=2;
		return false;
	}

	// public Object getChild(Object parent, int index)
	// {
	//	 	
	// }
	//	 
	// public int getChildCount(Object parent)
	// {
	//     	
	// }

	/**
	 * @inheritDoc
	 */
	public Object getValueAt(Object node, int column) {
		Object res = null;
		if (!(node instanceof TaskNode))
			return null;
		TaskNode tn = (TaskNode) node;
		Task t = (Task) tn.getUserObject();
		// if(tn.getParent()!=null){
		switch (column) {
		case 0:
			if (((Task) tn.getUserObject()).isProjectTask()) {
				res = new ImageIcon(getClass().getResource(
						"/icons/mproject.gif"));
			} else if (!tn.isLeaf())
				res = new ImageIcon(getClass().getResource("/icons/mtask.gif"));
			else if (t.isMilestone())
				res = new ImageIcon(getClass()
						.getResource("/icons/meeting.gif"));
			else
				res = new ImageIcon(getClass().getResource("/icons/tasks2.png"));
			break;
		case 1:
			Task task = (Task) tn.getUserObject();
			String path = (task.getPriority() == 0 ? "/icons/task1.gif" : task
					.getPriority() == 1 ? "/icons/task.gif"
					: "/icons/task2.gif");
			res = new ImageIcon(getClass().getResource(path));
			break;
		case 2: // info
			TaskInfo info = t.getTaskInfo();
			if (info != null) {
				if (info instanceof Delay) {
					int type = ((Delay) info).getType();
					if (type == Delay.NORMAL)
						res = new ImageIcon(getClass().getResource(
								"/icons/alert1_16.gif"));
					else if (type == Delay.CRITICAL)
						res = new ImageIcon(getClass().getResource(
								"/icons/alert2_16.gif"));
				}
			}
			break;
		case 3:
			res = tn.getName();
			break;
		case 4:
			res = tn.getStart();
			break;
		case 5:
			res = tn.getEnd();
			break;
		case 6:
			res = new Integer(tn.getDuration());
			break;
		case 7:
			res = new Integer(tn.getCompletionPercentage());
			break;
		case 8: {
			ResourceAssignment[] tAssign = t.getAssignments();
			StringBuffer sb = new StringBuffer();
			int nb = 0;
			for (int i = 0; i < tAssign.length; i++) {
				ResourceAssignment resAss = tAssign[i];
				if (resAss.isCoordinator())
					sb.append(nb++ == 0 ? "" : ", ").append(
							resAss.getResource().getName());
			}
			// res = "Not implemented";
			res = sb.toString();
			break;
		}
		case 9: {
			String resStr = "";
			TaskDependency[] dep = t.getDependenciesAsDependant().toArray();
			int i = 0;
			if (dep != null && dep.length > 0) {
				for (i = 0; i < dep.length - 1; i++)
					resStr += dep[i].getDependee().getTaskID() + ", ";
				resStr += dep[i].getDependee().getTaskID() + "";
			}
			res = resStr;
			break;
		}
		case 10:
			res = new Integer(t.getTaskID());
			break;
		default:
			String colName = this.getColumnName(column);
			// System.out.println(" -> "+colName);
			// System.out.println(t+" : "+t.getCustomValues());
			res = t.getCustomValues().getValue(colName);
			break;
		}
		// }
		// else
		// res ="";
		return res;
	}

	/**
	 * @inheritDoc
	 */
	public void setValueAt(final Object value, final Object node,
			final int column) {
		if (isCellEditable(node, column)) {
//			System.out.println("undoable colonne: " + column);
			Mediator.getGanttProjectSingleton().getUndoManager().undoableEdit(
					"Change properties column", new Runnable() {
						public void run() {
							setValue(value, node, column);
						}
					});
		} else {
//			System.out.println("NOT undoable colonne: " + column);
			setValue(value, node, column);
		}
		// System.out.println("node : " + node);
		// System.out.println("valeur : " + value);
		Mediator.getGanttProjectSingleton().repaint();
		Mediator.getGanttProjectSingleton().setAskForSave(true);
	}

	/**
	 * Set value in left pane cell
	 * @param value
	 * @param node
	 * @param column
	 */
	private void setValue(final Object value, final Object node,
			final int column) {
		switch (column) {
		case 0:
		case 1:
		case 2: // info
			((TaskNode) node).setTaskInfo((TaskInfo) value);
		case 8:
			break;
		case 3:
			((TaskNode) node).setName(value.toString());
			break;
		case 4:
			((TaskNode) node).setStart((GanttCalendar) value);
			((TaskNode) node).applyThirdDateConstraint();
			break;
		case 5:
			((TaskNode) node).setEnd((GanttCalendar) value);
			break;
		case 6:
            Task task = (Task) ((TaskNode) node).getUserObject();
			TaskLength tl = task.getDuration();
			((TaskNode) node).setDuration(task.getManager().createLength(tl.getTimeUnit(),
					((Integer) value).intValue()));
			break;
		case 7:
			((TaskNode) node).setCompletionPercentage(((Integer) value)
					.intValue());
			break;
		default: // custom colums
			try {
				((Task) ((TaskNode) node).getUserObject()).getCustomValues()
						.setValue(this.getColumnName(column), value);
			} catch (CustomColumnsException e) {
				e.printStackTrace();
			}
		}

	}

	/**
	 * @inheritDoc
	 */
	public void columnAdded(TableColumnModelEvent arg0) {
		nbCol++;
	}

	/**
	 * @inheritDoc
	 */
	public void columnRemoved(TableColumnModelEvent arg0) {
		nbCol--;
	}

	/**
	 * @inheritDoc
	 */
	public void columnMoved(TableColumnModelEvent arg0) {
		// TODO Auto-generated method stub

	}

	/**
	 * @inheritDoc
	 */
	public void columnMarginChanged(ChangeEvent arg0) {
		// TODO Auto-generated method stub

	}

	/**
	 * @inheritDoc
	 */
	public void columnSelectionChanged(ListSelectionEvent arg0) {
		// TODO Auto-generated method stub
	}

	/**
	 * @inheritDoc
	 */
	public Task[] getNestedTasks(Task container) {
		TaskNode r = (TaskNode) root;
		Enumeration e = r.children();

		Vector v = new Vector();
		while (e.hasMoreElements())
			v.add((TaskNode) e.nextElement());
		Task[] res = new Task[v.size()];
		v.toArray(res);
		return res;

	}

	/**
	 * Purpose: Should return true if this Tasks has any nested subtasks.
	 */
	public boolean hasNestedTasks(Task container) {
		TaskNode r = (TaskNode) root;
		if (r.getChildCount() > 0) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * @inheritDoc
	 */
	public Task getRootTask() {
		return (Task) ((TaskNode) this.getRoot()).getUserObject();
	}

	/**
	 * Returns the corresponding task node according to the given task.
	 * 
	 * @param task
	 *            The task whom tasknode we want to get.
	 * @return The corresponding task node according to the given task.
	 */
	public TaskNode getTaskNodeForTask(Task task) {
		Enumeration enumeration = ((TaskNode) getRoot()).preorderEnumeration();
		while (enumeration.hasMoreElements()) {
			Object next = enumeration.nextElement();
			if (!(next instanceof TaskNode))
				continue;
			TaskNode tn = (TaskNode) next;
			Task t = (Task) tn.getUserObject();
			if (t.equals(task))
				return tn;
		}
		return null;
	}

	/**
	 * @inheritDoc
	 */
	public Task getContainer(Task nestedTask) {
		// TODO Auto-generated method stub
		return null;
	}

	/**
	 * @inheritDoc
	 */
	public void move(Task whatMove, Task whereMove) {
		// TODO Auto-generated method stub

	}

	/**
	 * @inheritDoc
	 */
	public boolean areUnrelated(Task dependant, Task dependee) {
		// TODO Auto-generated method stub
		return false;
	}

	public int getDepth(Task task) {
		// TODO Auto-generated method stub
		return 0;
	}

    public void languageChanged(Event event) {
        changeLanguage(event.getLanguage());
    }

    public int compareDocumentOrder(Task next, Task dependeeTask) {
        throw new UnsupportedOperationException();
    }

    public boolean contains(Task task) {
        throw new UnsupportedOperationException();
    }
    
}