<?php
/***************************************************\
 *
 *  Mailer (https://github.com/txthinking/Mailer)
 *
 *  A lightweight PHP SMTP mail sender.
 *  Implement RFC0821, RFC0822, RFC1869, RFC2045, RFC2821
 *
 *  Support html body, don't worry that the receiver's
 *  mail client can't support html, because Mailer will
 *  send both text/plain and text/html body, so if the
 *  mail client can't support html, it will display the
 *  text/plain body.
 *
 *  Create Date 2012-07-25.
 *  Under the MIT license.
 *
 \***************************************************/
namespace Tx\Mailer;

class Message
{
    /**
     * from name
     */
    protected $fromName;

    /**
     * from email
     */
    protected $fromEmail;

    /**
     * fake from name
     */
    protected $fakeFromName;

    /**
     * fake from email
     */
    protected $fakeFromEmail;

    /**
     * to email
     */
    protected $to = array();

    /**
     * cc email
     */
    protected $cc = array();

    /**
     * bcc email
     */
    protected $bcc = array();

    /**
     * mail subject
     */
    protected $subject;

    /**
     * mail body
     */
    protected $body;

    /**
     *mail attachment
     */
    protected $attachment = array();

    /**
     * message header
     */
    protected $header = array();

    /**
     * charset
     */
    protected $charset = "UTF-8";

    /**
     * header multipart boundaryMixed
     */
    protected $boundaryMixed;

    /**
     * header multipart alternative
     */
    protected $boundaryAlternative;

    /**
     * $this->CRLF
     * @var string
     */
    protected $CRLF = "\r\n";


    /**
     * Address for the reply-to header
     * @var string
     */
    protected $replyToName;

    /**
     * Address for the reply-to header
     * @var string
     */
    protected $replyToEmail;


    public function setReplyTo($name, $email)
    {
        $this->replyToName = $name;
        $this->replyToEmail = $email;
        return $this;
    }


    /**
     * set mail from
     * @param string $name
     * @param string $email
     * @return $this
     */
    public function setFrom($name, $email)
    {
        $this->fromName = $name;
        $this->fromEmail = $email;
        return $this;
    }


    /**
     * set mail fake from
     * @param string $name
     * @param string $email
     * @return $this
     */
    public function setFakeFrom($name, $email)
    {
        $this->fakeFromName = $name;
        $this->fakeFromEmail = $email;
        return $this;
    }

    /**
     * add mail receiver
     * @param string $name
     * @param string $email
     * @return $this
     */
    public function addTo($name, $email)
    {
        $this->to[$email] = $name;
        return $this;
    }

    /**
     * add cc mail receiver
     * @param string $name
     * @param string $email
     * @return $this
     */
    public function addCc($name, $email)
    {
        $this->cc[$email] = $name;
        return $this;
    }

    /**
     * add bcc mail receiver
     * @param string $name
     * @param string $email
     * @return $this
     */
    public function addBcc($name, $email)
    {
        $this->bcc[$email] = $name;
        return $this;
    }

    /**
     * set mail subject
     * @param string $subject
     * @return $this
     */
    public function setSubject($subject)
    {
        $this->subject = $subject;
        return $this;
    }

    /**
     * set mail body
     * @param string $body
     * @return $this
     */
    public function setBody($body)
    {
        $this->body = $body;
        return $this;
    }

    /**
     * add mail attachment
     * @param $name
     * @param $path
     * @return $this
     */
    public function addAttachment($name, $path)
    {
        $this->attachment[$name] = $path;
        return $this;
    }

    /**
     * @return string
     */
    public function getFromName()
    {
        return $this->fromName;
    }

    /**
     * @return string
     */
    public function getFromEmail()
    {
        return $this->fromEmail;
    }


    /**
     * @return string
     */
    public function getFakeFromName()
    {
        return $this->fakeFromName;
    }

    /**
     * @return string
     */
    public function getFakeFromEmail()
    {
        return $this->fakeFromEmail;
    }

    /**
     * @return mixed
     */
    public function getTo()
    {
        return $this->to;
    }

    /**
     * @return mixed
     */
    public function getCc()
    {
        return $this->cc;
    }

    /**
     * @return mixed
     */
    public function getBcc()
    {
        return $this->bcc;
    }

    /**
     * @return mixed
     */
    public function getSubject()
    {
        return $this->subject;
    }

    /**
     * @return mixed
     */
    public function getBody()
    {
        return $this->body;
    }

    /**
     * @return array
     */
    public function getAttachment()
    {
        return $this->attachment;
    }

    /**
     * Create mail header
     * @return $this
     */
    protected function createHeader()
    {
        $this->header['Date'] = date('r');

        $fromName = "";
        $fromEmail = $this->fromEmail;
        if(!empty($this->fromName)){
            $fromName = sprintf("=?utf-8?B?%s?= ", base64_encode($this->fromName));
        }
        if(!empty($this->fakeFromEmail)){
            if(!empty($this->fakeFromName)){
                $fromName = sprintf("=?utf-8?B?%s?= ", base64_encode($this->fakeFromName));
            }
            $fromEmail = $this->fakeFromEmail;
        }
        $this->header['Return-Path'] = $fromEmail;
        $this->header['From'] = $fromName . "<" . $fromEmail .">";

        $this->header['To'] = '';
        foreach ($this->to as $toEmail => $toName) {
            if(!empty($toName)){
                $toName = sprintf("=?utf-8?B?%s?= ", base64_encode($toName));
            }
            $this->header['To'] .= $toName . "<" . $toEmail . ">, ";
        }
        $this->header['To'] = substr($this->header['To'], 0, -2);
        $this->header['Cc'] = '';
        foreach ($this->cc as $toEmail => $toName) {
            if(!empty($toName)){
                $toName = sprintf("=?utf-8?B?%s?= ", base64_encode($toName));
            }
            $this->header['Cc'] .= $toName . "<" . $toEmail . ">, ";
        }
        $this->header['Cc'] = substr($this->header['Cc'], 0, -2);
        $this->header['Bcc'] = '';
        foreach ($this->bcc as $toEmail => $toName) {
            if(!empty($toName)){
                $toName = sprintf("=?utf-8?B?%s?= ", base64_encode($toName));
            }
            $this->header['Bcc'] .= $toName . "<" . $toEmail . ">, ";
        }
        $this->header['Bcc'] = substr($this->header['Bcc'], 0, -2);

        $replyToName = "";
        if(!empty($this->replyToEmail)){
            if(!empty($this->replyToName)){
                $replyToName = sprintf("=?utf-8?B?%s?= ", base64_encode($this->replyToName));
            }
            $this->header['Reply-To'] = $replyToName . "<" . $this->replyToEmail . ">";
        }

        if(empty($this->subject)){
            $subject = '';
        }else{
            $subject = sprintf("=?utf-8?B?%s?= ", base64_encode($this->subject));
        }
        $this->header['Subject'] = $subject;

        $this->header['Message-ID'] = '<' . md5(uniqid()) . $this->fromEmail . '>';
        $this->header['X-Priority'] = '3';
        $this->header['X-Mailer'] = 'Mailer (https://github.com/txthinking/Mailer)';
        $this->header['MIME-Version'] = '1.0';
        if (!empty($this->attachment)){
            $this->boundaryMixed = md5(md5(time().'TxMailer').uniqid());
            $this->header['Content-Type'] = "multipart/mixed; \r\n\tboundary=\"" . $this->boundaryMixed . "\"";
        }
        $this->boundaryAlternative = md5(md5(time().'TXMailer').uniqid());
        return $this;
    }

    /**
     * @brief createBody create body
     *
     * @return string
     */
    protected function createBody()
    {
        $in = "";
        $in .= "Content-Type: multipart/alternative; boundary=\"$this->boundaryAlternative\"" . $this->CRLF;
        $in .= $this->CRLF;
        $in .= "--" . $this->boundaryAlternative . $this->CRLF;
        $in .= "Content-Type: text/plain; charset=\"" . $this->charset . "\"" . $this->CRLF;
        $in .= "Content-Transfer-Encoding: base64" . $this->CRLF;
        $in .= $this->CRLF;
        $in .= chunk_split(base64_encode($this->body)) . $this->CRLF;
        $in .= $this->CRLF;
        $in .= "--" . $this->boundaryAlternative . $this->CRLF;
        $in .= "Content-Type: text/html; charset=\"" . $this->charset ."\"" . $this->CRLF;
        $in .= "Content-Transfer-Encoding: base64" . $this->CRLF;
        $in .= $this->CRLF;
        $in .= chunk_split(base64_encode($this->body)) . $this->CRLF;
        $in .= $this->CRLF;
        $in .= "--" . $this->boundaryAlternative . "--" . $this->CRLF;
        return $in;
    }

    /**
     * @brief createBodyWithAttachment create body with attachment
     *
     * @return string
     */
    protected function createBodyWithAttachment()
    {
        $in = "";
        $in .= $this->CRLF;
        $in .= $this->CRLF;
        $in .= '--' . $this->boundaryMixed . $this->CRLF;
        $in .= "Content-Type: multipart/alternative; boundary=\"$this->boundaryAlternative\"" . $this->CRLF;
        $in .= $this->CRLF;
        $in .= "--" . $this->boundaryAlternative . $this->CRLF;
        $in .= "Content-Type: text/plain; charset=\"" . $this->charset . "\"" . $this->CRLF;
        $in .= "Content-Transfer-Encoding: base64" . $this->CRLF;
        $in .= $this->CRLF;
        $in .= chunk_split(base64_encode($this->body)) . $this->CRLF;
        $in .= $this->CRLF;
        $in .= "--" . $this->boundaryAlternative . $this->CRLF;
        $in .= "Content-Type: text/html; charset=\"" . $this->charset ."\"" . $this->CRLF;
        $in .= "Content-Transfer-Encoding: base64" . $this->CRLF;
        $in .= $this->CRLF;
        $in .= chunk_split(base64_encode($this->body)) . $this->CRLF;
        $in .= $this->CRLF;
        $in .= "--" . $this->boundaryAlternative . "--" . $this->CRLF;
        foreach ($this->attachment as $name => $path){
            $in .= $this->CRLF;
            $in .= '--' . $this->boundaryMixed . $this->CRLF;
            $in .= "Content-Type: application/octet-stream; name=\"". $name ."\"" . $this->CRLF;
            $in .= "Content-Transfer-Encoding: base64" . $this->CRLF;
            $in .= "Content-Disposition: attachment; filename=\"" . $name . "\"" . $this->CRLF;
            $in .= $this->CRLF;
            $in .= chunk_split(base64_encode(file_get_contents($path))) . $this->CRLF;
        }
        $in .= $this->CRLF;
        $in .= $this->CRLF;
        $in .= '--' . $this->boundaryMixed . '--' . $this->CRLF;
        return $in;
    }

    public function toString()
    {
        $in = '';
        $this->createHeader();
        foreach ($this->header as $key => $value) {
            $in .= $key . ': ' . $value . $this->CRLF;
        }
        if (empty($this->attachment)) {
            $in .= $this->createBody();
        } else {
            $in .= $this->createBodyWithAttachment();
        }
        $in .= $this->CRLF . $this->CRLF . "." . $this->CRLF;
        return $in;
    }

}
