/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  The ASF licenses this file to You
 * under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.  For additional information regarding
 * copyright in this work, please see the NOTICE file in the top level
 * directory of this distribution.
 */
package org.apache.roller.weblogger.ui.struts2.editor;

import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.roller.weblogger.WebloggerException;
import org.apache.roller.weblogger.business.MediaFileManager;
import org.apache.roller.weblogger.business.WebloggerFactory;
import org.apache.roller.weblogger.config.WebloggerRuntimeConfig;
import org.apache.roller.weblogger.pojos.MediaFile;
import org.apache.roller.weblogger.pojos.MediaFileDirectory;
import org.apache.roller.weblogger.util.RollerMessages;
import org.apache.roller.weblogger.util.RollerMessages.RollerMessage;
import org.apache.roller.weblogger.util.Utilities;
import org.apache.struts2.interceptor.validation.SkipValidation;

/**
 * Adds a new media file.
 */
@SuppressWarnings("serial")
// TODO: make this work @AllowedMethods({"execute","save"})
public class MediaFileAdd extends MediaFileBase {

    private static final Log log = LogFactory.getLog(MediaFileAdd.class);
    private MediaFileBean bean = new MediaFileBean();
    private MediaFileDirectory directory;

    // an array of files uploaded by the user, if applicable
    private File[] uploadedFiles = null;

    // an array of content types for upload files
    private String[] uploadedFilesContentType = null;

    // an array of filenames for uploaded files
    private String[] uploadedFilesFileName = null;

    private List<MediaFile> newImages = new ArrayList<>();

    private List<MediaFile> newFiles = new ArrayList<>();

    private String directoryName = null;

    public MediaFileAdd() {
        this.actionName = "mediaFileAdd";
        this.desiredMenu = "editor";
        this.pageTitle = "mediaFileAdd.title";
    }

    /**
     * Prepares action class
     */
    @Override
    public void myPrepare() {
        log.debug("Into myprepare");
        refreshAllDirectories();
        try {
            MediaFileManager mgr = WebloggerFactory.getWeblogger()
                    .getMediaFileManager();
            if (!StringUtils.isEmpty(bean.getDirectoryId())) {
                setDirectory(mgr.getMediaFileDirectory(bean.getDirectoryId()));

            } else if (StringUtils.isNotEmpty(directoryName)) {
                setDirectory(mgr.getMediaFileDirectoryByName(getActionWeblog(),
                        directoryName));

            } else {
                MediaFileDirectory root = mgr
                        .getDefaultMediaFileDirectory(getActionWeblog());
                if (root == null) {
                    root = mgr.createDefaultMediaFileDirectory(getActionWeblog());
                }
                setDirectory(root);
            }
            directoryName = getDirectory().getName();
            bean.setDirectoryId(getDirectory().getId());

        } catch (WebloggerException ex) {
            log.error("Error looking up media file directory", ex);
        } finally {
            // flush
            try {
                WebloggerFactory.getWeblogger().flush();
            } catch (WebloggerException e) {
                // ignored
            }
        }
    }

    /**
     * Show form for adding a new media file.
     * 
     * @return String The result of the action.
     */
    @SkipValidation
    @Override
    public String execute() {
        return INPUT;
    }

    /**
     * Save a media file.
     * 
     * @return String The result of the action.
     */
    public String save() {

        myValidate();

        if (!hasActionErrors()) {

            MediaFileManager manager = WebloggerFactory.getWeblogger().getMediaFileManager();

            RollerMessages errors = new RollerMessages();
            List<MediaFile> uploaded = new ArrayList<>();
            File[] uploads = getUploadedFiles();

            if (uploads != null && uploads.length > 0) {

                // loop over uploaded files and try saving them
                for (int i = 0; i < uploads.length; i++) {

                    // skip null files
                    if (uploads[i] == null || !uploads[i].exists()) {
                        continue;
                    }

                    try {
                        MediaFile mediaFile = new MediaFile();
                        bean.copyTo(mediaFile);

                        String fileName = getUploadedFilesFileName()[i];
                        int terminated = fileName.indexOf('\000');
                        if (terminated != -1) {
                            // disallow sneaky null terminated strings
                            fileName = fileName.substring(0, terminated).trim();
                        }

                        // make sure fileName is valid
                        if (fileName.indexOf('/') != -1
                                || fileName.indexOf('\\') != -1
                                || fileName.contains("..")) {
                            addError("uploadFiles.error.badPath", fileName);
                            continue;
                        }

                        mediaFile.setName(fileName);
                        mediaFile.setDirectory(getDirectory());
                        mediaFile.setWeblog(getActionWeblog());
                        mediaFile.setLength(this.uploadedFiles[i].length());
                        mediaFile.setInputStream(new FileInputStream(
                                this.uploadedFiles[i]));
                        mediaFile
                                .setContentType(this.uploadedFilesContentType[i]);

                        // in some cases Struts2 is not able to guess the content
                        // type correctly and assigns the default, which is
                        // octet-stream. So in cases where we see octet-stream
                        // we double check and see if we can guess the content
                        // type via the Java MIME type facilities.
                        mediaFile.setContentType(this.uploadedFilesContentType[i]);
                        if (mediaFile.getContentType() == null
                                || mediaFile.getContentType().endsWith("/octet-stream")) {

                            String ctype = Utilities.getContentTypeFromFileName(mediaFile.getName());
                            if (null != ctype) {
                                mediaFile.setContentType(ctype);
                            }
                        }

                        manager.createMediaFile(getActionWeblog(), mediaFile, errors);
                        WebloggerFactory.getWeblogger().flush();

                        if (mediaFile.isImageFile()) {
                            newImages.add(mediaFile);
                        } else {
                            newFiles.add(mediaFile);
                        }

                        uploaded.add(mediaFile);

                    } catch (Exception e) {
                        log.error("Error uploading media file", e);
                        addError("mediaFileAdd.errorUploading", bean.getName());
                    }
                }

                for (Iterator<RollerMessage> it = errors.getErrors(); it.hasNext();) {
                    RollerMessage msg = it.next();
                    addError(msg.getKey(), Arrays.asList(msg.getArgs()));
                }

                if (!uploaded.isEmpty() && !this.errorsExist()) {
                    addMessage("uploadFiles.uploadedFiles");
                    for (MediaFile upload : uploaded) {
                        addMessage("uploadFiles.uploadedFile",
                                upload.getPermalink());
                    }

                } else {
                    return INPUT;
                }

                this.pageTitle = "mediaFileAddSuccess.title";
                return SUCCESS;
            }
        }
        return INPUT;
    }

    /**
     * Validates media file to be added.
     */
    public void myValidate() {

        //
        // TODO: don't allow upload if user is over quota
        //

        // make sure uploads are enabled
        if (!WebloggerRuntimeConfig.getBooleanProperty("uploads.enabled")) {
            addError("error.upload.disabled");
        }
    }
    
    public MediaFileBean getBean() {
        return bean;
    }

    public void setBean(MediaFileBean b) {
        this.bean = b;
    }

    public MediaFileDirectory getDirectory() {
        return directory;
    }

    public void setDirectory(MediaFileDirectory directory) {
        this.directory = directory;
    }

    public File[] getUploadedFiles() {
        return uploadedFiles;
    }

    public void setUploadedFiles(File[] uploadedFiles) {
        this.uploadedFiles = uploadedFiles;
    }

    public String[] getUploadedFilesContentType() {
        return uploadedFilesContentType;
    }

    public void setUploadedFilesContentType(String[] uploadedFilesContentType) {
        this.uploadedFilesContentType = uploadedFilesContentType;
    }

    public String[] getUploadedFilesFileName() {
        return uploadedFilesFileName;
    }

    public void setUploadedFilesFileName(String[] uploadedFilesFileName) {
        this.uploadedFilesFileName = uploadedFilesFileName;
    }

    /**
     * @return the newImages
     */
    public List<MediaFile> getNewImages() {
        return newImages;
    }

    /**
     * @param newImages
     *            the newImages to set
     */
    public void setNewImages(List<MediaFile> newImages) {
        this.newImages = newImages;
    }

    /**
     * @return the newFiles
     */
    public List<MediaFile> getNewFiles() {
        return newFiles;
    }

    /**
     * @param newFiles
     *            the newFiles to set
     */
    public void setNewFiles(List<MediaFile> newFiles) {
        this.newFiles = newFiles;
    }

    /**
     * @return the directoryName
     */
    public String getDirectoryName() {
        return directoryName;
    }

    /**
     * @param directoryName
     *            the directoryName to set
     */
    public void setDirectoryName(String directoryName) {
        this.directoryName = directoryName;
    }
}
