package net.sourceforge.ganttproject.io;

import java.awt.Color;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.GregorianCalendar;
import java.util.Iterator;

import javax.xml.transform.sax.TransformerHandler;

import net.sourceforge.ganttproject.CustomPropertyManager;
import net.sourceforge.ganttproject.GanttCalendar;
import net.sourceforge.ganttproject.GanttTask;
import net.sourceforge.ganttproject.IGanttProject;
import net.sourceforge.ganttproject.task.CustomColumn;
import net.sourceforge.ganttproject.task.CustomColumnsStorage;
import net.sourceforge.ganttproject.task.CustomColumnsValues;
import net.sourceforge.ganttproject.task.Task;
import net.sourceforge.ganttproject.task.dependency.TaskDependency;
import net.sourceforge.ganttproject.util.ColorConvertion;

import org.w3c.util.DateParser;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

class TaskSaver extends SaverBase {
    void save(IGanttProject project, TransformerHandler handler, Color defaultColor, CustomColumnsStorage customColumnsStorage) throws SAXException, IOException {
        AttributesImpl attrs = new AttributesImpl();
        if (defaultColor!=null) {
        	addAttribute("color", ColorConvertion.getColor(defaultColor), attrs);
        }
        startElement("tasks", attrs, handler);

        startElement("taskproperties", handler);
        writeTaskProperties(handler, customColumnsStorage);
        endElement("taskproperties", handler);
        Task rootTask = project.getTaskManager().getTaskHierarchy().getRootTask();
        Task[] tasks = project.getTaskManager().getTaskHierarchy().getNestedTasks(rootTask);
        for (int i=0; i<tasks.length; i++) {
            writeTask(handler, (GanttTask) tasks[i], customColumnsStorage);
        }
        endElement("tasks", handler);
    }

    private void writeTask(TransformerHandler handler, GanttTask task, CustomColumnsStorage customColumns) throws SAXException, IOException {
        if (task.getTaskID() == -1) {
            throw new IllegalArgumentException("Is it a fake root task? Task="+task);
        }
        AttributesImpl attrs = new AttributesImpl();
        addAttribute("id", String.valueOf(task.getTaskID()), attrs);
        addAttribute("name", task.getName(), attrs);
        if (task.colorDefined()) {
            addAttribute("color", ColorConvertion.getColor(task.getColor()), attrs);
        }
        if (task.shapeDefined()) {
            addAttribute("shape", task.getShape().getArray(), attrs);
        }
        addAttribute("meeting", Boolean.valueOf(task.isMilestone()).toString(), attrs);
        if (task.isProjectTask()) {
            addAttribute("project", Boolean.TRUE.toString(), attrs);
        }
        addAttribute("start", task.getStart().toXMLString(), attrs);
        addAttribute("duration", String.valueOf(task.getLength()), attrs);
        addAttribute("complete", String.valueOf(task.getCompletionPercentage()), attrs);
        if (task.getThird() != null) {
            addAttribute("thirdDate", task.getThird().toXMLString(), attrs);
            addAttribute("thirdDate-constraint", String.valueOf(task.getThirdDateConstraint()), attrs);
        }
        addAttribute("priority", String.valueOf(task.getPriority()), attrs);
        final String sWebLink = task.getWebLink();
        if (sWebLink != null && !sWebLink.equals("")
                && !sWebLink.equals("http://")) {
            addAttribute("webLink", URLEncoder.encode(sWebLink, "ISO-8859-1"), attrs);
        }
        addAttribute("expand", String.valueOf(task.getExpand()), attrs);
        
        startElement("task", attrs, handler);
        
        if (task.getNotes() != null && task.getNotes().length() > 0) {
            cdataElement("notes", task.getNotes(), attrs, handler);
//            fout.write(space2 + "<notes>");
//            fout.write("\n"
//                    + space2
//                    + s
//                    + correct(replaceAll(task.getNotes(), "\n", "\n"
//                            + space2 + s)));
//            fout.write("\n" + space2 + "</notes>\n");
        }
        // use successors to write depends information
        final TaskDependency[] depsAsDependee = task.getDependenciesAsDependee().toArray();
        for (int i = 0; i < depsAsDependee.length; i++) {
            TaskDependency next = depsAsDependee[i];
            addAttribute("id", String.valueOf(next.getDependant().getTaskID()), attrs);
            addAttribute("type", String.valueOf(next.getConstraint().getID()), attrs);
            addAttribute("difference", String.valueOf(next.getDifference()), attrs);
            addAttribute("hardness", next.getHardness().getIdentifier(), attrs);
            emptyElement("depend", attrs, handler);
        }

        CustomColumnsValues ccv = task.getCustomValues();
        for (Iterator/*<CustomColumn>*/ it = customColumns.getCustomColums().iterator(); it.hasNext();) {
            CustomColumn nextColumn = (CustomColumn) it.next();
            final String name = nextColumn.getName();
            final String idc = nextColumn.getId();
            Object value = ccv.getValue(name);
            if (GregorianCalendar.class.isAssignableFrom(nextColumn.getType())) {
                value = DateParser.getIsoDate(((GanttCalendar)value).getTime());
            }
            addAttribute("taskproperty-id", idc, attrs);
            addAttribute("value", String.valueOf(value), attrs);
            emptyElement("customproperty", attrs, handler);
        }

        // Write the child of the task
        if (task.getManager().getTaskHierarchy().hasNestedTasks(task)) {
            Task[] nestedTasks = task.getManager().getTaskHierarchy().getNestedTasks(task);
            for (int i = 0; i < nestedTasks.length; i++) {
                writeTask(handler, (GanttTask) nestedTasks[i], customColumns);
            }

        }

        // end of task section
        endElement("task", handler);
    }

    private void writeTaskProperty(TransformerHandler handler, String id, String name, String type, String valueType) throws SAXException {
        writeTaskProperty(handler, id, name, type, valueType, null);
    }    
    private void writeTaskProperty(TransformerHandler handler, String id, String name, String type, String valueType, String defaultValue) throws SAXException {
        AttributesImpl attrs = new AttributesImpl();
        addAttribute("id", id, attrs);
        addAttribute("name", name, attrs);
        addAttribute("type", type, attrs);
        addAttribute("valuetype", valueType, attrs);
        if (defaultValue!=null) {
            addAttribute("defaultvalue", defaultValue, attrs);
        }
        emptyElement("taskproperty", attrs, handler);        
    }
    private void writeTaskProperties(TransformerHandler handler, CustomColumnsStorage customCol) throws SAXException {
        writeTaskProperty(handler, "tpd0", "type", "default", "icon");
        writeTaskProperty(handler, "tpd1", "priority", "default", "icon");
        writeTaskProperty(handler, "tpd2", "info", "default", "icon");
        writeTaskProperty(handler, "tpd3", "name", "default", "text");
        writeTaskProperty(handler, "tpd4", "begindate", "default", "date");
        writeTaskProperty(handler, "tpd5", "enddate", "default", "date");
        writeTaskProperty(handler, "tpd6", "duration", "default", "int");
        writeTaskProperty(handler, "tpd7", "completion", "default", "int");
        writeTaskProperty(handler, "tpd8", "coordinator", "default", "text");
        writeTaskProperty(handler, "tpd9", "predecessorsr", "default", "text");
        Iterator/*<CustomColumn>*/ it = customCol.getCustomColums().iterator();
        while (it.hasNext()) {
            final CustomColumn cc = (CustomColumn) it.next();
            Object defVal = cc.getDefaultValue();
            final Class cla = cc.getType();
            final String valueType = encodeFieldType(cla);
            if (valueType==null) {
            	continue;
            }
            if ("date".equals(valueType)){
            	assert defVal instanceof GanttCalendar;
            	defVal = DateParser.getIsoDate(((GanttCalendar)defVal).getTime());
            }
            String idcStr = cc.getId();
            writeTaskProperty(handler, idcStr, cc.getName(), "custom", valueType, String.valueOf(defVal));
        }
    }
    
    static String encodeFieldType(Class fieldType) {
    	return CustomPropertyManager.PropertyTypeEncoder.encodeFieldType(fieldType);
    }
    
}
