<?php

/*
 * This file is part of MailSo.
 *
 * (c) 2014 Usenko Timur
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace MailSo\Mime;

/**
 * @category MailSo
 * @package Mime
 */
class Part
{
	/**
	 * @var string
	 */
	public static $DefaultCharset = \MailSo\Base\Enumerations\Charset::ISO_8859_1;

	/**
	 * @var HeaderCollection
	 */
	public $Headers;

	/**
	 * @var resource
	 */
	public $Body = null;

	/**
	 * @var resource
	 */
	public $Raw = null;

	/**
	 * @var PartCollection
	 */
	public $SubParts;

	function __construct()
	{
		$this->Headers = new HeaderCollection;
		$this->SubParts = new PartCollection;
	}

	public function HeaderCharset() : string
	{
		return \trim(\strtolower($this->Headers->ParameterValue(Enumerations\Header::CONTENT_TYPE, Enumerations\Parameter::CHARSET)));
	}

	public function HeaderBoundary() : string
	{
		return \trim($this->Headers->ParameterValue(Enumerations\Header::CONTENT_TYPE, Enumerations\Parameter::BOUNDARY));
	}

	public function ContentType() : string
	{
		return \trim(\strtolower($this->Headers->ValueByName(Enumerations\Header::CONTENT_TYPE)));
	}

	public function ContentID() : string
	{
		return \trim($this->Headers->ValueByName(Enumerations\Header::CONTENT_ID));
	}

	public function ContentLocation() : string
	{
		return \trim($this->Headers->ValueByName(Enumerations\Header::CONTENT_LOCATION));
	}

	public function IsFlowedFormat() : bool
	{
		$bResult = false;
		if ($this->Headers)
		{
			$bResult = 'flowed' === \trim(\strtolower($this->Headers->ParameterValue(
				Enumerations\Header::CONTENT_TYPE,
				Enumerations\Parameter::FORMAT)));

			if ($bResult && \in_array($this->MailEncodingName(), array('base64', 'quoted-printable')))
			{
				$bResult = false;
			}
		}

		return $bResult;
	}

	public function FileName() : string
	{
		$sResult = '';
		if ($this->Headers)
		{
			$sResult = \trim($this->Headers->ParameterValue(
				Enumerations\Header::CONTENT_DISPOSITION,
				Enumerations\Parameter::FILENAME));

			if (!\strlen($sResult))
			{
				$sResult = \trim($this->Headers->ParameterValue(
					Enumerations\Header::CONTENT_TYPE,
					Enumerations\Parameter::NAME));
			}
		}

		return $sResult;
	}

	public function IsPgpSigned() : bool
	{
		// https://datatracker.ietf.org/doc/html/rfc3156#section-5
		$header = $this->Headers->GetByName(Enumerations\Header::CONTENT_TYPE);
		return $header
		 && \preg_match('#multipart/signed.+protocol=["\']?application/pgp-signature#si', $header->FullValue())
		 // The multipart/signed body MUST consist of exactly two parts.
		 && 2 === \count($this->SubParts)
		 && $this->SubParts[1]->IsPgpSignature();
	}

	public function IsPgpSignature() : bool
	{
		return \in_array($this->ContentType(), array('application/pgp-signature', 'application/pkcs7-signature'));
	}

	public static function FromFile(string $sFileName) : ?self
	{
		$rStreamHandle = \file_exists($sFileName) ? \fopen($sFileName, 'rb') : false;
		if ($rStreamHandle) {
			try {
				return Parser::parseStream($rStreamHandle);
			} finally {
				\fclose($rStreamHandle);
			}
		}
		return null;
	}

	public static function FromString(string $sRawMessage) : ?self
	{
		$rStreamHandle = \strlen($sRawMessage) ?
			\MailSo\Base\ResourceRegistry::CreateMemoryResource() : false;
		if ($rStreamHandle) {
			\fwrite($rStreamHandle, $sRawMessage);
			unset($sRawMessage);
			\fseek($rStreamHandle, 0);

			try {
				return Parser::parseStream($rStreamHandle);
			} finally {
				\MailSo\Base\ResourceRegistry::CloseMemoryResource($rStreamHandle);
			}
		}
		return null;
	}

	/**
	 * @param resource $rStreamHandle
	 */
	public static function FromStream($rStreamHandle) : ?Part
	{
		return Parser::parseStream($rStreamHandle);
	}

	/**
	 * @return resource|bool
	 */
	public function ToStream()
	{
		if ($this->Raw) {
			$aSubStreams = array(
				$this->Raw
			);
		} else {
			if ($this->SubParts->count()) {
				$sBoundary = $this->HeaderBoundary();
				if (!\strlen($sBoundary)) {
					$this->Headers->GetByName(Enumerations\Header::CONTENT_TYPE)->setParameter(
						Enumerations\Parameter::BOUNDARY,
						$this->SubParts->Boundary()
					);
				} else {
					$this->SubParts->SetBoundary($sBoundary);
				}
			}

			$aSubStreams = array(
				$this->Headers . "\r\n"
			);

			if ($this->Body) {
				$aSubStreams[0] .= "\r\n";
				if (\is_resource($this->Body)) {
					$aMeta = \stream_get_meta_data($this->Body);
					if (!empty($aMeta['seekable'])) {
						\rewind($this->Body);
					}
				}
				$aSubStreams[] = $this->Body;
			}

			if ($this->SubParts->count()) {
				$rSubPartsStream = $this->SubParts->ToStream();
				if (\is_resource($rSubPartsStream)) {
					$aSubStreams[] = $rSubPartsStream;
				}
			}
		}

		return \MailSo\Base\StreamWrappers\SubStreams::CreateStream($aSubStreams);
	}
}
