/***************************************************************************
 * GanttXMLSaver.java  -  description
 * -------------------
 * begin                : feb 2003
 * copyright            : (C) 2002 by Thomas Alexandre
 * email                : alexthomas(at)ganttproject.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

package net.sourceforge.ganttproject.io;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;

import net.sourceforge.ganttproject.GPLogger;
import net.sourceforge.ganttproject.GanttCalendar;
import net.sourceforge.ganttproject.GanttGraphicArea;
import net.sourceforge.ganttproject.GanttProject;
import net.sourceforge.ganttproject.GanttResourcePanel;
import net.sourceforge.ganttproject.GanttTree2;
import net.sourceforge.ganttproject.IGanttProject;
import net.sourceforge.ganttproject.Mediator;
import net.sourceforge.ganttproject.calendar.Interval;
import net.sourceforge.ganttproject.calendar.IntervalManager;
import net.sourceforge.ganttproject.calendar.IntervalType;
import net.sourceforge.ganttproject.gui.UIFacade;
import net.sourceforge.ganttproject.roles.Role;
import net.sourceforge.ganttproject.task.TaskCategory;
import net.sourceforge.ganttproject.util.ColorConvertion;
import net.sourceforge.ganttproject.roles.RoleManager;
import net.sourceforge.ganttproject.roles.RoleSet;
import net.sourceforge.ganttproject.task.TaskCategory;
import net.sourceforge.ganttproject.util.ColorConvertion;

import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

/**
 * Classe for save the project in a XML file
 */
public class GanttXMLSaver extends SaverBase implements GPSaver {
    
    private static final String VERSION = "2.0";
    
    private final IGanttProject myProject;
    
    private final UIFacade myUIFacade;
    
    private GanttTree2 tree;
    
    private GanttGraphicArea area;
    
    private final List<GPSaver> mySavers;
    /** The constructor 
     * @param savers */
    public GanttXMLSaver(IGanttProject project, GanttTree2 tree,
            GanttResourcePanel peop, GanttGraphicArea area, UIFacade uiFacade, List<GPSaver> savers) {
        this.tree = tree;
        this.area = area;
        myProject = project;
        myUIFacade = uiFacade;
        mySavers = savers;
    }
    
    public void save(OutputStream stream) throws IOException {
        try {
            StreamResult result = new StreamResult(stream);
            SAXTransformerFactory factory = (SAXTransformerFactory) SAXTransformerFactory
                    .newInstance();
            TransformerHandler handler = factory.newTransformerHandler();
            Transformer serializer = handler.getTransformer();
            serializer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
            serializer.setOutputProperty(OutputKeys.INDENT, "yes");
            serializer.setOutputProperty(OutputKeys.METHOD, "xml");
            serializer.setOutputProperty(
                    "{http://xml.apache.org/xslt}indent-amount", "4");
            handler.setResult(result);
            save(handler);
            stream.close();
        } catch (Throwable e) {
            e.printStackTrace();
            IOException propagatedException  = new IOException("Failed to save the project file");
            propagatedException.initCause(e);
            throw propagatedException;
        }
    }
    
    public void save(TransformerHandler handler) throws IOException {
        try {
            AttributesImpl attrs = new AttributesImpl();
            handler.startDocument();
            addAttribute("name", getProject().getProjectName(), attrs);
            addAttribute("company", getProject().getOrganization(), attrs);
            addAttribute("webLink", getProject().getWebLink(), attrs);
            addAttribute("view-date", new GanttCalendar(myUIFacade.getGanttChart().getStartDate()).toXMLString(), attrs);
            addAttribute("view-index", "" + myUIFacade.getViewIndex(), attrs);
            //TODO for GP 2.0: move view configurations into <view> tag (see ViewSaver)
            addAttribute("gantt-divider-location", ""
                    + myUIFacade.getGanttDividerLocation(), attrs);
            addAttribute("resource-divider-location", ""
                    + myUIFacade.getResourceDividerLocation(), attrs);
            addAttribute("version", VERSION, attrs);
            startElement("project", attrs, handler);
            //
            cdataElement("description", getProject().getDescription(), attrs, handler);
            
            saveViews(handler);
            emptyComment(handler);
            saveCalendar(handler);
            saveIntervals(handler);
            saveCategories(handler);
            saveTasks(handler);
            saveResources(handler);
            saveAssignments(handler);
            saveVacations(handler);
            saveGanttChartView(handler);
            saveHistory(handler);
            saveRoles(handler);
            for (GPSaver saver : mySavers) {
                saver.save(handler);
            }
            endElement("project", handler);
            handler.endDocument();
        } catch (Throwable e) {
            if (!GPLogger.log(e)) {
                e.printStackTrace(System.err);
            }
            IOException propagatedException  = new IOException("Failed to save the project file");
            propagatedException.initCause(e);
            throw propagatedException;
        }
    }
    
    public void addSaver(GPSaver saver) {
        mySavers.add(saver);
    }
    
    private void saveHistory(TransformerHandler handler) throws SAXException, ParserConfigurationException, IOException {
        List history = ((GanttProject) myProject).getPreviouStates();
        new HistorySaver().save(history, handler);
    }
    
    private void saveGanttChartView(TransformerHandler handler) throws SAXException {
        new GanttChartViewSaver().save(tree.getVisibleFields(), handler);
    }
    
    private void saveVacations(TransformerHandler handler) throws SAXException {
        new VacationSaver().save(getProject(), handler);
    }
    
    private void saveResources(TransformerHandler handler) throws SAXException {
        new ResourceSaver().save(getProject(), handler);
    }
    
    private void saveViews(TransformerHandler handler) throws SAXException, IOException {
        new ViewSaver().save(getUIFacade(), handler);
    }
    
    private void saveCalendar(TransformerHandler handler) throws SAXException {
        new CalendarSaver().save(getProject(), handler);
    }
    
    private void saveTasks(TransformerHandler handler) throws SAXException, IOException {
        new TaskSaver().save(getProject(), handler, area.getTaskColor());
    }
    
    private void saveAssignments(TransformerHandler handler) throws SAXException {
        new AssignmentSaver().save(getProject(), handler);
    }
    private void saveRoles(TransformerHandler handler) throws SAXException {
        AttributesImpl attrs = new AttributesImpl();
        RoleManager roleManager = getProject().getRoleManager();
        RoleSet[] roleSets = roleManager.getRoleSets();
        for (int i = 0; i < roleSets.length; i++) {
            RoleSet next = roleSets[i];
            if (next.isEnabled()) {
                addAttribute("roleset-name", next.getName(), attrs);
                emptyElement("roles", attrs, handler);
            }
        }
        //
        RoleSet projectRoleSet = roleManager.getProjectRoleSet();
        if (!projectRoleSet.isEmpty()) {
            startElement("roles", attrs, handler);
            Role[] projectRoles = projectRoleSet.getRoles();
            for (int i = 0; i < projectRoles.length; i++) {
                Role next = projectRoles[i];
                addAttribute("id", next.getPersistentID(), attrs);
                addAttribute("name", next.getName(), attrs);
                emptyElement("role", attrs, handler);
            }
            endElement("roles", handler);
        }
    }
    
    private void saveIntervals(TransformerHandler handler) throws SAXException{
                
        Set<IntervalType> types = this.myProject.getIntervalManager().getUnmodifiableIntervalTypes();
        
        if(!types.isEmpty()){
            
            AttributesImpl attrs = new AttributesImpl();
            
            startElement("intervals", attrs, handler);
            AttributesImpl intervalTypeAttrs = null;
            AttributesImpl intervalAttrs = null;
            
            for (IntervalType type : types) {        
                intervalTypeAttrs = new AttributesImpl();
                addAttribute("description", type.getDescription(), intervalTypeAttrs);
                addAttribute("color", ColorConvertion.getColor(type.getColor()), intervalTypeAttrs);
                addAttribute("locked", Boolean.toString(type.isLocked()), intervalTypeAttrs);
                startElement("intervalType", intervalTypeAttrs, handler);
                for (Interval interval : type.getIntervals()) {
                    intervalAttrs = new AttributesImpl();
                    addAttribute("startdate", IntervalManager.SIMPLE_DATE_FORMAT.format(interval.getStartDate()), intervalAttrs);
                    addAttribute("enddate", IntervalManager.SIMPLE_DATE_FORMAT.format(interval.getEndDate()), intervalAttrs);
                    addAttribute("description", type.getDescription(), intervalAttrs);
                    emptyElement("interval", intervalAttrs, handler);
                }
                endElement("intervalType", handler);
            }
            
            endElement("intervals", handler);
        }
    }
    
    private void saveCategories(TransformerHandler handler) throws SAXException{
        AttributesImpl attrs = new AttributesImpl();
        Collection<TaskCategory> categories = myProject.getTaskCategoryManager().getCategories();
        if(!categories.isEmpty()){
            startElement("taskCategories", attrs, handler);
            for (TaskCategory category : categories) {
                addAttribute("id", Long.toString(category.getId()), attrs);
                addAttribute("color", ColorConvertion.getColor(category.getColor()), attrs);
                addAttribute("description", category.getDescription(), attrs);
                emptyElement("taskCategory", attrs, handler);
            }
            endElement("taskCategories", handler);
        }
    }
    
    IGanttProject getProject() {
        return myProject;
    }
    
    UIFacade getUIFacade() {
        return myUIFacade;
    }

    /**
     * Writes the properties defined for a resource.
     *
     * @param fout Writer
     */
    /*
    public void writeResourceProperties(Writer fout) {
        try {
            fout.write(s+s+s+"<resproperty name=\"name\" type=\"String\" />\n");
            fout.write(s+s+s+"<resproperty name=\"role\" type=\"String\" />\n");
            fout.write(s+s+s+"<resproperty name=\"mail\" type=\"String\" />\n");
            fout.write(s+s+s+"<resproperty name=\"phone\" type=\"String\" />\n");
            fout.write(s+s+s+"<resproperty name=\"assignedrole\" type=\"String\" />\n");
            fout.write(s+s+s+"<resproperty name=\"id\" type=\"Integer\" />\n");
     
            List resources = peop.getPeople();
            if (resources.size() > 0) {
                HumanResourceManager rm = ((HumanResource)resources.get(0)).getResourceManager();
     
                Hashtable fields = rm.getCustomFields();
                Enumeration keys = fields.keys();
                String out = new String();
                while (keys.hasMoreElements()) {
                    String el = (String)keys.nextElement();
                    String type = fields.get(el).getClass().toString().split(" ")[1];
                    if (type.equals("java.lang.String"))
                        type = "text";
                    else if (type.equals("java.lang.Integer"))
                        type = "int";
                    else if (type.equals("java.lang.Double"))
                        type = "double";
                    else if (type.equals("java.lang.Boolean"))
                        type = "boolean";
                    else if (type.equals("net.sourceforge.ganttproject.GanttCalendar"))
                        type = "date";
                    Object value = fields.get(el);
                    if (value instanceof GregorianCalendar)
                        value = DateParser.getIsoDate(((GregorianCalendar)value).getTime());
     
                    fout.write(s+s+s+"<resproperty name=\""+el+"\" type=\"custom\" valueType=\""
                    + type +"\" defaultValue=\"" + value + "\"/>\n");
     
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
     
    }
     */
    
}
