/*
 * Created on 12.03.2005
 */
package net.sourceforge.ganttproject.document;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;

import org.xml.sax.Attributes;

import net.sourceforge.ganttproject.GanttProject;
import net.sourceforge.ganttproject.GanttTree2;
import net.sourceforge.ganttproject.IGanttProject;
import net.sourceforge.ganttproject.calendar.BusinessHoursOptions;
import net.sourceforge.ganttproject.calendar.GPCalendar;
import net.sourceforge.ganttproject.calendar.IntervalManager;
import net.sourceforge.ganttproject.calendar.TimeUnitRangeOptions;
import net.sourceforge.ganttproject.gui.TableHeaderUIFacade;
import net.sourceforge.ganttproject.gui.UIFacade;
import net.sourceforge.ganttproject.io.GPSaver;
import net.sourceforge.ganttproject.language.GanttLanguage;
import net.sourceforge.ganttproject.parser.AllocationTagHandler;
import net.sourceforge.ganttproject.parser.BlankLineTagHandler;
import net.sourceforge.ganttproject.parser.CustomPropertiesTagHandler;
import net.sourceforge.ganttproject.parser.DefaultWeekTagHandler;
import net.sourceforge.ganttproject.parser.DependencyTagHandler;
import net.sourceforge.ganttproject.parser.FileFormatException;
import net.sourceforge.ganttproject.parser.GPParser;
import net.sourceforge.ganttproject.parser.GenericOptionTagHandler;
import net.sourceforge.ganttproject.parser.HolidayTagHandler;
import net.sourceforge.ganttproject.parser.IntervalTagHandler;
import net.sourceforge.ganttproject.parser.ParserFactory;
import net.sourceforge.ganttproject.parser.PreviousStateTasksTagHandler;
import net.sourceforge.ganttproject.parser.ResourceTagHandler;
import net.sourceforge.ganttproject.parser.RoleTagHandler;
import net.sourceforge.ganttproject.parser.TagHandler;
import net.sourceforge.ganttproject.parser.TaskCategoryTagHandler;
import net.sourceforge.ganttproject.parser.TaskDisplayColumnsTagHandler;
import net.sourceforge.ganttproject.parser.TaskPropertiesTagHandler;
import net.sourceforge.ganttproject.parser.TaskTagHandler;
import net.sourceforge.ganttproject.parser.VacationTagHandler;
import net.sourceforge.ganttproject.parser.ViewTagHandler;
import net.sourceforge.ganttproject.resource.HumanResourceManager;
import net.sourceforge.ganttproject.resource.ResourceManager;
import net.sourceforge.ganttproject.roles.RoleManager;
import net.sourceforge.ganttproject.task.TaskCategoryManager;
import net.sourceforge.ganttproject.task.TaskManager;
import net.sourceforge.ganttproject.task.TaskManagerImpl;

/**
 * @author bard
 */
class ProxyDocument implements Document {
    private final Document myPhysicalDocument;

    private IGanttProject myProject;

    private UIFacade myUIFacade;

    private final ParserFactory myParserFactory;

    private boolean isLoaded;

    private final DocumentCreator myCreator;

    private PortfolioImpl myPortfolio;

    private final TableHeaderUIFacade myVisibleFields;

    ProxyDocument(DocumentCreator creator, Document physicalDocument, IGanttProject project,
            UIFacade uiFacade, TableHeaderUIFacade visibleFields, ParserFactory parserFactory) {
        myPhysicalDocument = physicalDocument;
        myProject = project;
        myUIFacade = uiFacade;
        myParserFactory = parserFactory;
        myCreator = creator;
        myVisibleFields = visibleFields;
    }

    public String getDescription() {
        return myPhysicalDocument.getDescription();
    }

    public boolean canRead() {
        return myPhysicalDocument.canRead();
    }

    public boolean canWrite() {
        return myPhysicalDocument.canWrite();
    }

    public boolean isValidForMRU() {
        return myPhysicalDocument.isValidForMRU();
    }

    public boolean acquireLock() {
        return myPhysicalDocument.acquireLock();
    }

    public void releaseLock() {
        myPhysicalDocument.releaseLock();
    }

    public InputStream getInputStream() throws IOException {
        return myPhysicalDocument.getInputStream();
    }

    public OutputStream getOutputStream() throws IOException {
        return myPhysicalDocument.getOutputStream();
    }

    public String getPath() {
        return myPhysicalDocument.getPath();
    }

    public String getFilePath() {
        String result = myPhysicalDocument.getFilePath();
        if (result==null) {
            try {
                result = myCreator.createTemporaryFile();
            } catch (IOException e) {
                myUIFacade.showErrorDialog(e);
            }
        }
        return result;
    }

    public String getURLPath() {
        return myPhysicalDocument.getURLPath();
    }

    public String getUsername() {
        return myPhysicalDocument.getUsername();
    }

    public String getPassword() {
        return myPhysicalDocument.getPassword();
    }

    public void setUserInfo(String user, String pass) {
        myPhysicalDocument.setUserInfo(user, pass);
    }

    public String getLastError() {
        return myPhysicalDocument.getLastError();
    }

    public void read() throws IOException {
        FailureState failure = new FailureState();
        SuccessState success = new SuccessState();
        ParsingState parsing = new ParsingState(success, failure);
//        OpenCopyConfirmationState confirmation = new OpenCopyConfirmationState(
//                parsing, failure);
//        AcquireLockState lock = new AcquireLockState(parsing, confirmation);
        try {
            getTaskManager().setEventsEnabled(false);
            parsing.enter();
        }
        finally {
            getTaskManager().setEventsEnabled(true);
        }
        //lock.enter();
    }

    public void write() throws IOException {
        GPSaver saver = myParserFactory.newSaver();
        byte[] buffer;
        try {
            ByteArrayOutputStream bufferStream = new ByteArrayOutputStream();
            saver.save(bufferStream);
            bufferStream.flush();
            buffer = bufferStream.toByteArray();
        }
        catch (IOException e) {
            getUIFacade().showErrorDialog(e);
            return;
        }
        OutputStream output = getOutputStream();
        try {
            output.write(buffer);
            output.flush();
        }
        finally {
            output.close();
        }
    }
    
    private TaskManagerImpl getTaskManager() {
        return (TaskManagerImpl) myProject.getTaskManager();
    }

    private RoleManager getRoleManager() {
        return myProject.getRoleManager();
    }

    private HumanResourceManager getHumanResourceManager() {
        return (HumanResourceManager) myProject.getHumanResourceManager();
    }

    private IntervalManager getIntervalManager(){
        return this.myProject.getIntervalManager();
    }

    private TaskCategoryManager getTaskCategoryManager(){
        return myProject.getTaskCategoryManager();
    }

    private GPCalendar getActiveCalendar() {
        return myProject.getActiveCalendar();
    }

    private UIFacade getUIFacade() {
        return myUIFacade;
    }

    class AcquireLockState {
        OpenCopyConfirmationState myConfirmationState;

        ParsingState myParsingState;

        /**
         * @param parsing
         * @param confirmation
         */
        public AcquireLockState(ParsingState parsing,
                OpenCopyConfirmationState confirmation) {
            myParsingState = parsing;
            myConfirmationState = confirmation;
        }

        void enter() throws IOException {
            boolean locked = acquireLock();
            if (!locked) {
                myConfirmationState.enter();
            } else {
                myParsingState.enter();
            }
        }
    }

    
    class OpenCopyConfirmationState {
        ParsingState myParsingState;

        FailureState myExitState;

        /**
         * @param parsing
         * @param failure
         */
        public OpenCopyConfirmationState(ParsingState parsing,
                FailureState failure) {
            myParsingState = parsing;
            myExitState = failure;
        }

        void enter() throws IOException {
            String message = GanttLanguage.getInstance().getText("msg13");
            String title = GanttLanguage.getInstance().getText("warning");
            if (UIFacade.Choice.YES==getUIFacade().showConfirmationDialog(message, title)) {
                myParsingState.enter();
            } else {
                myExitState.enter();
            }
        }
    }

    class ParsingState {
        FailureState myFailureState;

        SuccessState mySuccessState;

        /**
         * @param success
         * @param failure
         */
        public ParsingState(SuccessState success, FailureState failure) {
            mySuccessState = success;
            myFailureState = failure;
        }

        void enter() throws IOException {
            GPParser opener = myParserFactory.newParser();
            ResourceManager hrManager = getHumanResourceManager();
            RoleManager roleManager = getRoleManager();
            TaskManager taskManager = getTaskManager();
            ResourceTagHandler resourceHandler = new ResourceTagHandler(
                    hrManager, roleManager, myProject.getResourceCustomPropertyManager(), opener.getContext());
            DependencyTagHandler dependencyHandler = new DependencyTagHandler(
                    opener.getContext(), taskManager, getUIFacade());
            AllocationTagHandler allocationHandler = new AllocationTagHandler(
                    hrManager, getTaskManager(), getRoleManager());
            VacationTagHandler vacationHandler = new VacationTagHandler(
                    hrManager);
            PreviousStateTasksTagHandler previousStateHandler = 
                new PreviousStateTasksTagHandler(myProject.getBaselines());
            RoleTagHandler rolesHandler = new RoleTagHandler(roleManager);
            TaskTagHandler taskHandler = new TaskTagHandler(taskManager, opener.getContext(), getTaskCategoryManager());
            DefaultWeekTagHandler weekHandler = new DefaultWeekTagHandler(
                    getActiveCalendar());
            ViewTagHandler viewHandler = new ViewTagHandler(getUIFacade());

            TaskPropertiesTagHandler taskPropHandler = new TaskPropertiesTagHandler(myProject.getCustomColumnsStorage());
            opener.addTagHandler(taskPropHandler);
            CustomPropertiesTagHandler customPropHandler = new CustomPropertiesTagHandler(
                    opener.getContext(), getTaskManager(), myProject.getCustomColumnsStorage());
            opener.addTagHandler(customPropHandler);
            TaskDisplayColumnsTagHandler taskDisplayHandler = 
                new TaskDisplayColumnsTagHandler(myVisibleFields);
            opener.addTagHandler(taskDisplayHandler);

            TaskDisplayColumnsTagHandler resourceViewHandler = new TaskDisplayColumnsTagHandler(
                    getUIFacade().getResourceTree().getVisibleFields(), "field", "id", "order", "width");
            opener.addTagHandler(resourceViewHandler);
            opener.addParsingListener(resourceViewHandler);
            
            opener.addTagHandler(taskHandler);

            opener.addParsingListener(taskPropHandler);
            opener.addParsingListener(taskDisplayHandler);
            opener.addParsingListener(customPropHandler);

            opener.addTagHandler(opener.getDefaultTagHandler());
            opener.addTagHandler(resourceHandler);
            opener.addTagHandler(dependencyHandler);
            opener.addTagHandler(allocationHandler);
            opener.addParsingListener(allocationHandler);
            opener.addTagHandler(vacationHandler);
            opener.addTagHandler(previousStateHandler);
            opener.addTagHandler(rolesHandler);
            opener.addTagHandler(weekHandler);
            opener.addTagHandler(viewHandler);
            opener.addParsingListener(dependencyHandler);
            opener.addParsingListener(resourceHandler);
            
            HolidayTagHandler holidayHandler = new HolidayTagHandler(myProject);
            opener.addTagHandler(holidayHandler);
            opener.addParsingListener(holidayHandler);
            
            TimeUnitRangeOptions timeUnitOptions = new TimeUnitRangeOptions(myProject.getTimeUnitStack()); 
            GenericOptionTagHandler timeUnitRangeHandler = 
                new GenericOptionTagHandler(timeUnitOptions.getOptions());
            opener.addTagHandler(timeUnitRangeHandler);
            opener.addParsingListener(timeUnitOptions);
            //
            GenericOptionTagHandler businessHoursHandler = 
                new GenericOptionTagHandler(new BusinessHoursOptions(myProject).getOptions());
            opener.addTagHandler(businessHoursHandler);
            //
            PortfolioTagHandler portfolioHandler = new PortfolioTagHandler();
            opener.addTagHandler(portfolioHandler);
            //
            
            IntervalTagHandler intervalTagHandler = new IntervalTagHandler(getIntervalManager());
            getIntervalManager().reset();
            opener.addTagHandler(intervalTagHandler);
            
            TaskCategoryTagHandler taskCategroyTagHandler = new TaskCategoryTagHandler(getTaskCategoryManager());
            getTaskCategoryManager().reset();
            opener.addTagHandler(taskCategroyTagHandler);
            
            if (opener.load(getInputStream())) {
                mySuccessState.enter();
            } else {
                myFailureState.enter();
            }
        }
    }

    class SuccessState {
        void enter() {
            ProxyDocument.this.setLoaded(true);
        }
    }

    class FailureState {
        void enter() {

        }
    }

    private void setLoaded(boolean b) {
        isLoaded = b;
    }

    public URI getURI() {
        return myPhysicalDocument.getURI();
    }

    public boolean isLocal() {
        return myPhysicalDocument.isLocal();
    }

    /* (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     * @author arun_ram
     * Added on Feb 26, 2006	 
     */
    public boolean equals(Object doc) {
        if (false==doc instanceof ProxyDocument) {
            return false;
        }
        return getPath().equals(((Document)doc).getPath());
    }

    public Portfolio getPortfolio() {
        return myPortfolio;
    }

    private PortfolioImpl getPortfolioImpl() {
        if (myPortfolio==null) {
            myPortfolio = new PortfolioImpl();
        }
        return myPortfolio;
    }

    private class PortfolioImpl implements Portfolio {
        private Document myDefaultDocument;

        public Document getDefaultDocument() {
            return myDefaultDocument;
        }

        void setDefaultDocument(Document document) {
            if (myDefaultDocument !=null) {
                throw new IllegalStateException("Don't set default document twice");
            }
            myDefaultDocument = document;
        }
    }
    private class PortfolioTagHandler implements TagHandler {
        private static final String PORTFOLIO_TAG = "portfolio";
        private static final String PROJECT_TAG = "project";
        private static final String LOCATION_ATTR = "location";
        private boolean isReadingPortfolio = false;
        public void startElement(String namespaceURI, String sName, String qName,
                Attributes attrs) throws FileFormatException {
            if (PORTFOLIO_TAG.equals(qName)) {
                isReadingPortfolio = true;
                return;
            }
            if (PROJECT_TAG.equals(qName) && isReadingPortfolio) {
                String locationAsString = attrs.getValue(LOCATION_ATTR);
                if (locationAsString!=null) {
                    Document document = myCreator.getDocument(locationAsString);
                    getPortfolioImpl().setDefaultDocument(document);
                }
                
            }
        }

        public void endElement(String namespaceURI, String sName, String qName) {
            if (PORTFOLIO_TAG.equals(qName)) {
                isReadingPortfolio = false;
            }        
        }
        
    }
}
