<?php
/**
 * PHPWord
 *
 * @link        https://github.com/PHPOffice/PHPWord
 * @copyright   2014 PHPWord
 * @license     http://www.gnu.org/licenses/lgpl.txt LGPL
 */

namespace PhpOffice\PhpWord\Writer;

use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Style;
use PhpOffice\PhpWord\Element\AbstractElement;
use PhpOffice\PhpWord\Exception\Exception;
use PhpOffice\PhpWord\Style\Font;
use PhpOffice\PhpWord\Style\Paragraph;
use PhpOffice\PhpWord\Writer\HTML\Element\Element as ElementWriter;
use PhpOffice\PhpWord\Writer\HTML\Element\TextRun as TextRunWriter;
use PhpOffice\PhpWord\Writer\HTML\Style\Generic as GenericStyleWriter;
use PhpOffice\PhpWord\Writer\HTML\Style\Font as FontStyleWriter;
use PhpOffice\PhpWord\Writer\HTML\Style\Paragraph as ParagraphStyleWriter;

/**
 * HTML writer
 *
 * Not supported: PreserveText, PageBreak, Object
 * @since 0.10.0
 */
class HTML extends AbstractWriter implements WriterInterface
{
    /**
     * Is the current writer creating PDF?
     *
     * @var boolean
     */
    protected $isPdf = false;

    /**
     * Footnotes and endnotes collection
     *
     * @var array
     */
    protected $notes = array();

    /**
     * Create new instance
     */
    public function __construct(PhpWord $phpWord = null)
    {
        $this->setPhpWord($phpWord);
    }

    /**
     * Save PhpWord to file
     *
     * @param string $filename
     * @throws Exception
     */
    public function save($filename = null)
    {
        if (!is_null($this->getPhpWord())) {
            $this->setTempDir(sys_get_temp_dir() . '/PHPWordWriter/');
            $hFile = fopen($filename, 'w');
            if ($hFile !== false) {
                fwrite($hFile, $this->writeDocument());
                fclose($hFile);
            } else {
                throw new Exception("Can't open file");
            }
            $this->clearTempDir();
        } else {
            throw new Exception("No PHPWord assigned.");
        }
    }

    /**
     * Get phpWord data
     *
     * @return string
     */
    public function writeDocument()
    {
        $html = '';
        $html .= '<!DOCTYPE html>' . PHP_EOL;
        $html .= '<!-- Generated by PHPWord -->' . PHP_EOL;
        $html .= '<html>' . PHP_EOL;
        $html .= '<head>' . PHP_EOL;
        $html .= $this->writeHTMLHead();
        $html .= '</head>' . PHP_EOL;
        $html .= '<body>' . PHP_EOL;
        $html .= $this->writeHTMLBody();
        $html .= $this->writeNotes();
        $html .= '</body>' . PHP_EOL;
        $html .= '</html>' . PHP_EOL;

        return $html;
    }

    /**
     * Generate HTML header
     *
     * @return string
     */
    private function writeHTMLHead()
    {
        $properties = $this->getPhpWord()->getDocumentProperties();
        $propertiesMapping = array(
            'creator' => 'author',
            'title' => '',
            'description' => '',
            'subject' => '',
            'keywords' => '',
            'category' => '',
            'company' => '',
            'manager' => ''
        );
        $title = $properties->getTitle();
        $title = ($title != '') ? $title : 'PHPWord';

        $html = '';
        $html .= '<meta charset="UTF-8" />' . PHP_EOL;
        $html .= '<title>' . htmlspecialchars($title) . '</title>' . PHP_EOL;
        foreach ($propertiesMapping as $key => $value) {
            $value = ($value == '') ? $key : $value;
            $method = "get" . $key;
            if ($properties->$method() != '') {
                $html .= '<meta name="' . $value . '" content="' .
                    htmlspecialchars($properties->$method()) . '" />' . PHP_EOL;
            }
        }
        $html .= $this->writeStyles();

        return $html;
    }

    /**
     * Get content
     *
     * @return string
     */
    private function writeHTMLBody()
    {
        $phpWord = $this->getPhpWord();
        $html = '';

        $sections = $phpWord->getSections();
        $countSections = count($sections);

        if ($countSections > 0) {
            foreach ($sections as $section) {
                $elements = $section->getElements();
                foreach ($elements as $element) {
                    if ($element instanceof AbstractElement) {
                        $elementWriter = new ElementWriter($this, $element, false);
                        $html .= $elementWriter->write();
                    }
                }
            }
        }

        return $html;
    }

    /**
     * Write footnote/endnote contents as textruns
     */
    private function writeNotes()
    {
        $html = '';
        if (!empty($this->notes)) {
            $html .= "<hr />";
            foreach ($this->notes as $noteId => $noteMark) {
                $noteAnchor = "note-{$noteId}";
                list($noteType, $noteTypeId) = explode('-', $noteMark);
                $collectionObject = 'PhpOffice\\PhpWord\\' . ($noteType == 'endnote' ? 'Endnotes' : 'Footnotes');
                $collection = $collectionObject::getElements();
                if (array_key_exists($noteTypeId, $collection)) {
                    $element = $collection[$noteTypeId];
                    $elmWriter = new TextRunWriter($this, $element, true);
                    $content = "<a href=\"#{$noteMark}\" class=\"NoteRef\"><sup>{$noteId}</sup></a>" . $elmWriter->write();
                    $html .= "<p><a name=\"{$noteAnchor}\" />{$content}</p>" . PHP_EOL;
                }
            }
        }

        return $html;
    }

    /**
     * Get styles
     *
     * @return string
     */
    private function writeStyles()
    {
        $css = '<style>' . PHP_EOL;

        // Default styles
        $defaultStyles = array(
            '*' => array(
                'font-family' => $this->getPhpWord()->getDefaultFontName(),
                'font-size' => $this->getPhpWord()->getDefaultFontSize() . 'pt',
            ),
            'a.NoteRef' => array(
                'text-decoration' => 'none',
            ),
            'hr' => array(
                'height' => '1px',
                'padding' => '0',
                'margin' => '1em 0',
                'border' => '0',
                'border-top' => '1px solid #CCC',
            ),
        );
        foreach ($defaultStyles as $selector => $style) {
            $styleWriter = new GenericStyleWriter($style);
            $css .= $selector . ' {' . $styleWriter->write() . '}' . PHP_EOL;
        }

        // Custom styles
        $customStyles = Style::getStyles();
        if (is_array($customStyles)) {
            foreach ($customStyles as $name => $style) {
                if ($style instanceof Font) {
                    $styleWriter = new FontStyleWriter($style);
                    if ($style->getStyleType() == 'title') {
                        $name = str_replace('Heading_', 'h', $name);
                    } else {
                        $name = '.' . $name;
                    }
                    $css .= "{$name} {" . $styleWriter->write() . '}'  . PHP_EOL;
                } elseif ($style instanceof Paragraph) {
                    $styleWriter = new ParagraphStyleWriter($style);
                    $name = '.' . $name;
                    $css .= "{$name} {" . $styleWriter->write() . '}'  . PHP_EOL;
                }
            }
        }
        $css .= '</style>' . PHP_EOL;

        return $css;
    }

    /**
     * Get is PDF
     *
     * @return bool
     */
    public function isPdf()
    {
        return $this->isPdf;
    }

    /**
     * Get notes
     *
     * @return array
     */
    public function getNotes()
    {
        return $this->notes;
    }

    /**
     * Add note
     *
     * @param int $noteId
     * @param string $noteMark
     */
    public function addNote($noteId, $noteMark)
    {
        $this->notes[$noteId] = $noteMark;
    }
}
