/*
 * Copyright 2010 ETH Zuerich, CISD
 *
 * Licensed 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.
 */

package eu.basysbio.cisd.dss;

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;

import org.apache.log4j.Logger;

import ch.rinn.restrictions.Private;
import ch.systemsx.cisd.common.Constants;
import ch.systemsx.cisd.common.exceptions.UserFailureException;
import ch.systemsx.cisd.common.logging.LogCategory;
import ch.systemsx.cisd.common.logging.LogFactory;
import ch.systemsx.cisd.common.mail.IMailClient;
import ch.systemsx.cisd.common.mail.MailClient;
import ch.systemsx.cisd.common.utilities.ITimeProvider;
import ch.systemsx.cisd.common.utilities.SystemTimeProvider;
import ch.systemsx.cisd.etlserver.IDataSetHandler;
import ch.systemsx.cisd.openbis.dss.generic.shared.dto.DataSetInformation;
import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetType;

/**
 * Data set handler for time series data sets and time point data sets.
 * 
 * @author Franz-Josef Elmer
 */
public class TimeSeriesAndTimePointDataSetHandler implements IDataSetHandler
{
    private static final String CIFEX_DIR_ENDING = ".dir";

    @Private
    static final String HELPDESK_EMAIL = "helpdesk.openbis.basysbio@bsse.ethz.ch";

    private static enum TypeOfDerivedDataSet
    {
        TIME_POINT("time point"), LCA_MIC_TIME_SERIES("LCA MIC time series");

        private final String name;

        TypeOfDerivedDataSet(String name)
        {
            this.name = name;
        }
    }

    private static final class MessageBuilder
    {

        private final String userEMail;

        private final ITimeProvider timeProvider;

        private String dataSetFileName;

        private int numberOfDerivedDataSets;

        private int count;

        private TypeOfDerivedDataSet type;

        MessageBuilder(String userEMail, ITimeProvider timeProvider)
        {
            this.userEMail = userEMail;
            this.timeProvider = timeProvider;
        }

        void setDataSetFileName(File dataSet)
        {
            dataSetFileName = dataSet.getName();
            if (dataSetFileName.endsWith(CIFEX_DIR_ENDING))
            {
                int until = dataSetFileName.length() - CIFEX_DIR_ENDING.length();
                dataSetFileName = dataSetFileName.substring(0, until);
            }
        }

        void setNumberOfExpectedDerivedDataSets(TypeOfDerivedDataSet type,
                int numberOfDerivedDataSets)
        {
            this.type = type;
            this.numberOfDerivedDataSets = numberOfDerivedDataSets;
        }

        void addDerivedDataSetCode(String code)
        {
            count++;
        }

        void logSendEMailAndHandlerError(Logger logger, IMailClient mailClient, boolean sendEMail)
        {
            if (count < numberOfDerivedDataSets)
            {
                int numberOfFailures = numberOfDerivedDataSets - count;
                operationLog.error(numberOfFailures + " " + type.name
                        + " data sets couldn't be registered.");
                if (sendEMail)
                {
                    String subject =
                            "BaSysBio: Failed uploading of data set '" + dataSetFileName + "'";
                    String timeStamp =
                            Constants.DATE_FORMAT.get().format(
                                    new Date(timeProvider.getTimeInMilliseconds()));
                    String message =
                            "Uploading of data set '" + dataSetFileName + "' failed because "
                                    + numberOfFailures + " of " + numberOfDerivedDataSets + " "
                                    + type.name + " data sets couldn't be registered.\n\n"
                                    + "Please, contact the help desk for support: "
                                    + HELPDESK_EMAIL + "\n(Time stamp of failure: " + timeStamp
                                    + ")";
                    mailClient.sendMessage(subject, message, null, null, userEMail, HELPDESK_EMAIL);
                }
                throw new UserFailureException("Not all data sets could be registered. "
                        + "For more details see error messages in the log.");
            } else
            {
                if (count > 0 && operationLog.isInfoEnabled())
                {
                    operationLog.info(count + " " + type.name + " data sets have been registered.");
                }
                if (sendEMail)
                {
                    String subject =
                            "BaSysBio: Successful uploading of data set '" + dataSetFileName + "'";
                    String message =
                            "The data set '" + dataSetFileName
                                    + "' has been successfully uploaded and registered in openBIS.";

                    mailClient.sendMessage(subject, message, null, null, userEMail);
                }
            }
        }

    }

    private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION,
            TimeSeriesAndTimePointDataSetHandler.class);

    private static final FilenameFilter LCA_MIC_TIME_SERIES_FILE_FILTER = new FilenameFilter()
        {

            public boolean accept(File dir, String name)
            {
                return name.startsWith(DataSetHandler.LCA_MIC_TIME_SERIES);
            }
        };

    private final IDataSetHandler delegator;

    private final IMailClient mailClient;

    private final ITimeProvider timeProvider;

    public TimeSeriesAndTimePointDataSetHandler(Properties parentProperties,
            IDataSetHandler delegator)
    {
        this.delegator = delegator;
        this.mailClient = new MailClient(parentProperties);
        timeProvider = SystemTimeProvider.SYSTEM_TIME_PROVIDER;
    }

    @Private
    TimeSeriesAndTimePointDataSetHandler(IDataSetHandler delegator, IMailClient mailClient,
            ITimeProvider timeProvider)
    {
        this.delegator = delegator;
        this.mailClient = mailClient;
        this.timeProvider = timeProvider;
    }

    public List<DataSetInformation> handleDataSet(File dataSet)
    {
        List<DataSetInformation> dataSetInfos = new ArrayList<DataSetInformation>();
        List<DataSetInformation> result = delegator.handleDataSet(dataSet);
        dataSetInfos.addAll(result);
        boolean successful = result.isEmpty() == false;
        if (successful)
        {
            try
            {
                DataSetInformation dataSetInformation = result.get(0);
                MessageBuilder builder =
                        new MessageBuilder(dataSetInformation.tryGetUploadingUserEmail(),
                                timeProvider);
                builder.setDataSetFileName(dataSet);
                File[] files = dataSet.getParentFile().listFiles(LCA_MIC_TIME_SERIES_FILE_FILTER);
                handleDerivedDataSets(files, TypeOfDerivedDataSet.LCA_MIC_TIME_SERIES, delegator,
                        dataSetInfos, builder);
                DataSetType dataSetType = dataSetInformation.getDataSetType();
                boolean lcaMicTimeSeries =
                        dataSetType.getCode().equals(DataSetHandler.LCA_MIC_TIME_SERIES);
                builder.logSendEMailAndHandlerError(operationLog, mailClient,
                        lcaMicTimeSeries == false);
            } catch (RuntimeException ex)
            {
                // NOTE: the code to delete data sets is not supported any more
                throw new RuntimeException("Exception can't be handled properly any more.", ex);
            }
        }
        return dataSetInfos;
    }

    private void handleDerivedDataSets(File[] files, TypeOfDerivedDataSet type,
            IDataSetHandler handler, List<DataSetInformation> dataSetInfos, MessageBuilder builder)
    {
        if (files != null && files.length > 0)
        {
            if (operationLog.isInfoEnabled())
            {
                operationLog.info("Starting registration of " + files.length + " " + type.name
                        + " data sets.");
            }
            builder.setNumberOfExpectedDerivedDataSets(type, files.length);
            for (File file : files)
            {
                List<DataSetInformation> result = handler.handleDataSet(file);
                dataSetInfos.addAll(result);
                if (result.isEmpty() == false)
                {
                    builder.addDerivedDataSetCode(getDataSetCode(result));
                }
            }
        }
    }

    private String getDataSetCode(List<DataSetInformation> result)
    {
        return result.get(0).getDataSetCode();
    }

}