/*
 * Copyright 2014-2017 Richard Linsdale.
 *
 * 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 uk.theretiredprogrammer.mailer;

import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.logging.Level;
import org.openide.loaders.DataObject;
import uk.theretiredprogrammer.mailer.email.HtmlMessage;
import uk.theretiredprogrammer.mailer.email.HtmlMessageWithAttachments;
import uk.theretiredprogrammer.mailer.email.Email451Exception;
import uk.theretiredprogrammer.mailer.email.EmailException;
import uk.theretiredprogrammer.mailer.email.SmtpSslConnection;
import uk.theretiredprogrammer.mailer.expressionparserandevaluate.ParseAndEvaluate;

/**
 * An Action Helper - supporting action which involve the sending of e-mails.
 *
 * @author Richard Linsdale - richard at theretiredprogrammer.uk
 */
public abstract class MailActionHelper extends ActionHelper {

    /**
     * Constructor
     *
     * @param context the dataobject of the command file
     */
    public MailActionHelper(DataObject context) {
        super(context);
    }

    /**
     * Return value if e-mail was sent correctly.
     */
    protected final static int SENT = 0;

    /**
     * Return value if e-mail was not sent (failed transmission).
     */
    protected final static int SENDFAILED = 1;

    /**
     * Return value if this e-mail was not processes as the selection criteria
     * was not met.
     */
    protected final static int NOTSELECTED = -1;

    /**
     * Wait based on countdown and command file parameters. Wait can be defined
     * to be applied prior to every send and also after a "x" emails (interblock
     * delay).
     *
     * @param countdown the email countdown until interblock delay.
     * @param manifest the command file data model
     * @param goodcount the number of successful sent emails in this batch
     * @param badcount the number of email sends which has failed in this batch
     * @return the revised countdown
     */
    protected int countdownToWait(int countdown, ManifestModel manifest, int goodcount, int badcount) {
        countdown--;
        if (countdown <= 0) {
            // wait and reset counter
            int pause = manifest.getDelayWaitMinutes();
            writeMsg("Pausing for " + pause + " minutes; so far " + goodcount + " emails sent; " + badcount + " emails failed");
            try {
                Thread.sleep(60000 * pause);
            } catch (InterruptedException ex) {
                // ignore the interrupt but just continue processing
            }
            writeMsg("Continueing to send emails");
            countdown = manifest.getDelayCount();
        }
        return countdown;
    }

    /**
     * Process an email based to the current command file and a set of
     * parameters (from a single data model record).
     *
     * @param parseAndEvaluate the parser and evaluator for a command file
     * @param stl the expression language being used
     * @param manifest the command file data model
     * @param parameters the data parameters
     * @return the outcome code
     */
    @SuppressWarnings("SleepWhileInLoop")
    protected int processDataLine(ParseAndEvaluate parseAndEvaluate, SimpleTemplateLanguage stl, ManifestModel manifest, Map<String, String> parameters) {
        stl.setParameters(parameters);
        stl.addParameters(manifest.getPerRecordCalculation());
        if (!parseAndEvaluate.evaluateAsBoolean(parameters)) {
            return NOTSELECTED;
        }
        stl.addParameters(manifest.getPerSelectedRecordCalculation());
        int retrycount = manifest.getRetry451Count();
        while (true) {
            try {
                send(stl, manifest);
                writeMsg("email sent for: " + stl.process(manifest.getDisplay()));
                return SENT;
            } catch (Email451Exception ex) {
                // send failed with 451 code
                if (manifest.getRetry451Count() > 0) {
                    // assume this is "Too many messages sent within time period. Please try again later" from google mail or equivalent
                    if (retrycount <= 0) {
                        writeErr("email failed for: " + stl.process(manifest.getDisplay()) + "; Send Failure 451 - retried " + manifest.getRetry451Count() + " but no success");
                        return SENDFAILED;
                    }
                    int pause = manifest.getRetry451WaitMinutes();
                    writeErr("email problem for: " + stl.process(manifest.getDisplay()) + "; Send Failure 451 - Pausing for " + pause + " minutes");
                    try {
                        Thread.sleep(60000 * pause);
                    } catch (InterruptedException ex1) {
                        // do nothing but just continue processing
                    }
                    writeMsg("Retry to send email");
                    retrycount--;
                } else {
                    writeErr("email failed for: " + stl.process(manifest.getDisplay()) + "; " + ex.getMessage());
                    return SENDFAILED;
                }
            } catch (EmailException ex) {
                writeErr("email failed for: " + stl.process(manifest.getDisplay()) + "; " + ex.getMessage());
                return SENDFAILED;
            } catch (IOException ex) {
                // can't read attachment file
                writeErr("email failed for: " + stl.process(manifest.getDisplay()) + "; Problem with attachment for email - " + ex.getMessage());
                return SENDFAILED;
            }
        }
    }

    private void send(SimpleTemplateLanguage stl, ManifestModel manifest) throws IOException, Email451Exception, EmailException {
        HtmlMessage html;
        SmtpSslConnection connection = new SmtpSslConnection(manifest.getServer(),
                manifest.getPort(), manifest.getUsername(), manifest.getPw(), manifest.isDebug());
        String att = manifest.getAttachment();
        if (att != null) {
            HtmlMessageWithAttachments ahtml = new HtmlMessageWithAttachments(connection, manifest.getFromEmail(), manifest.getFromName());
            ahtml.attachFile(new File(att));
            html = ahtml;
        } else {
            html = new HtmlMessage(connection, manifest.getFromEmail(), manifest.getFromName());
        }
        messageAdditionsHook(html, stl, manifest);
        html.to(stl.process(manifest.getToEmail()), stl.process(manifest.getToName()));
        html.subject(stl.process(manifest.getSubject()));
        String body = stl.process(manifest.getBody());
        html.body(Format.html(body), Format.text(body));
        html.send();
        connection.disconnect();
    }

    /**
     * A hook allowing a concrete implementation to add future email processing
     * prior to the email being sent.
     *
     * @param html the html email message
     * @param stl the expression language being used
     * @param manifest the command file data model
     * @throws EmailException if problems
     */
    abstract protected void messageAdditionsHook(HtmlMessage html, SimpleTemplateLanguage stl, ManifestModel manifest)
            throws EmailException;
}
