/*
 * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
 *
 * The contents of this file are subject to the CCM Public
 * License (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the
 * License at http://www.redhat.com/licenses/ccmpl.html.
 *
 * Software distributed under the License is distributed on an
 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
 * or implied. See the License for the specific language
 * governing rights and limitations under the License.
 *
 */
package com.arsdigita.cms.contenttypes;

import com.arsdigita.cms.ContentSection;
import com.arsdigita.cms.ContentSectionCollection;
import com.arsdigita.cms.ContentType;
import com.arsdigita.cms.ContentTypeLifecycleDefinition;
import com.arsdigita.cms.ContentTypeWorkflowTemplate;
import com.arsdigita.cms.Template;
import com.arsdigita.cms.TemplateManager;
import com.arsdigita.cms.TemplateManagerFactory;
import com.arsdigita.cms.installer.xml.XMLContentTypeHandler;
import com.arsdigita.cms.lifecycle.LifecycleDefinition;
import com.arsdigita.cms.lifecycle.LifecycleDefinitionCollection;
import com.arsdigita.domain.DataObjectNotFoundException;
import com.arsdigita.domain.DomainObjectFactory;
import com.arsdigita.kernel.Kernel;
import com.arsdigita.kernel.KernelExcursion;
import com.arsdigita.loader.PackageLoader;
import com.arsdigita.persistence.DataCollection;
import com.arsdigita.persistence.Session;
import com.arsdigita.runtime.ScriptContext;
import com.arsdigita.util.Assert;
import com.arsdigita.util.UncheckedWrapperException;
import com.arsdigita.util.parameter.Parameter;
import com.arsdigita.workflow.simple.TaskCollection;
import com.arsdigita.workflow.simple.WorkflowTemplate;
import com.arsdigita.xml.XML;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Date;
import java.net.URL;

import java.util.Iterator;
import java.util.List;

/**
 * This is the base loader that can be used by individual content types.
 * Specifically, it provides type loading functionality in the "run" method
 * that can be used by content types to reduce code duplication.
 *
 * @author Rafael H. Schloming &lt;rhs@mit.edu&gt;
 * @version $Revision: #11 $ $Date: 2004/04/07 $
 **/

public abstract class AbstractContentTypeLoader extends PackageLoader {

    public final static String versionId = "$Id: //cms/dev/src/com/arsdigita/cms/contenttypes/AbstractContentTypeLoader.java#11 $ by $Author: dennis $, $DateTime: 2004/04/07 16:07:11 $";

    public void run(final ScriptContext ctx) {
        new KernelExcursion() {
            protected void excurse() {
                setEffectiveParty(Kernel.getSystemParty());
                createTypes(ctx);
            }
        }.run();
    }

    private void createTypes(ScriptContext ctx) {
        XMLContentTypeHandler handler = new XMLContentTypeHandler();
        String[] contentTypes = getTypes();
        for (int i = 0; i < contentTypes.length; i++) {
            XML.parseResource(contentTypes[i], handler);
        }

        List types = handler.getContentTypes();
        Session ssn = ctx.getSession();
        DataCollection sections = ssn.retrieve(ContentSection.BASE_DATA_OBJECT_TYPE);

        while (sections.next()) {
            ContentSection section = (ContentSection)
                    DomainObjectFactory.newInstance(sections.getDataObject());
            if ( !isLoadableInto(section) ) { continue; }

            LifecycleDefinitionCollection ldc =
                section.getLifecycleDefinitions();
            LifecycleDefinition ld = null;
            if (ldc.next()) {
                ld = ldc.getLifecycleDefinition();
                ldc.close();
            }

            TaskCollection tc = section.getWorkflowTemplates();
            WorkflowTemplate wf = null;
            if (tc.next()) {
                wf = (WorkflowTemplate) tc.getTask();
                tc.close();
            }

            for (Iterator it = types.iterator(); it.hasNext(); ) {
                final ContentType type = (ContentType) it.next();

                section.addContentType(type);

                prepareSection(section, type, ld, wf);
            }
        }
    }

    protected void prepareSection(final ContentSection section,
                                  final ContentType type,
                                  final LifecycleDefinition ld,
                                  final WorkflowTemplate wf) {
        ContentTypeLifecycleDefinition.updateLifecycleDefinition
            (section, type, ld);

        ContentTypeWorkflowTemplate.updateWorkflowTemplate
            (section, type, wf);
    }

    protected abstract String[] getTypes();

    private boolean isLoadableInto(ContentSection section) {
        if ( section == null ) { throw new NullPointerException("section"); }

        if ( getContentSections().size() > 0 ) {
            return getContentSections().contains(section.getName());
        } else {
            return ContentSection.getConfig().getDefaultContentSection().
                equals(section.getName());
        }
    }

    /**
     * Returns a list of content sections into which the content type should be
     * installed.
     *
     * <p>If this returns an empty list, then the content type will be loaded
     * into the section specified by {@link
     * com.arsdigita.cms.ContentSectionConfig#getDefaultContentSection()}.</p>
     *
     * <p>The default implementation returns an empty list.</p>
     *
     * @post return != null
     **/
    protected List getContentSections() {
        return java.util.Collections.EMPTY_LIST;
    }


    /**
     *  This provides an easy way to subtypes to register default
     *  templates during the loading.  When this is used, it should
     *  be called by the loader class by overriding prepareSection
     */
    protected Template setDefaultTemplate(final String name,
                                          final String label,
                                          final URL templateURL,
                                          final ContentSection section,
                                          final ContentType type,
                                          final LifecycleDefinition ld,
                                          final WorkflowTemplate wf) {
        final Template template = new Template();
        template.setName(name);
        template.setLabel(label);
        template.setContentSection(section);
        template.setParent(section.getTemplatesFolder());

        InputStream stream;
        try {
            stream = templateURL.openStream();
        } catch (IOException ex) {
            throw new UncheckedWrapperException("Cannot read stream", ex);
        }

        Assert.truth(stream != null, "Template not found");
        
        final BufferedReader input = new BufferedReader
            (new InputStreamReader(stream));
        
        final StringBuffer body = new StringBuffer();
        
        try {
            String line;

            while ((line = input.readLine()) != null) {
                body.append(line);
                body.append("\n");
            }
        } catch (IOException ioe) {
            throw new UncheckedWrapperException
                ("Template cannot be read", ioe);
        }

        template.setText(body.toString());

        TemplateManagerFactory.getInstance().addTemplate
            (section, type, template, TemplateManager.PUBLIC_CONTEXT);

        template.publish(ld, new Date());
        return template;
    }
}
