<?php
/**
 * This file is part of PHPWord - A pure PHP library for reading and writing
 * word processing documents.
 *
 * PHPWord is free software distributed under the terms of the GNU Lesser
 * General Public License version 3 as published by the Free Software Foundation.
 *
 * For the full copyright and license information, please read the LICENSE
 * file that was distributed with this source code. For the full list of
 * contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
 *
 * @link        https://github.com/PHPOffice/PHPWord
 * @copyright   2010-2014 PHPWord contributors
 * @license     http://www.gnu.org/licenses/lgpl.txt LGPL version 3
 */

namespace PhpOffice\PhpWord\Writer;

use PhpOffice\PhpWord\Exception\Exception;
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Style\Font;
use PhpOffice\PhpWord\Style\Paragraph;
use PhpOffice\PhpWord\Style;
use PhpOffice\PhpWord\Writer\HTML\Element\Container;
use PhpOffice\PhpWord\Writer\HTML\Element\TextRun as TextRunWriter;
use PhpOffice\PhpWord\Writer\HTML\Style\Font as FontStyleWriter;
use PhpOffice\PhpWord\Writer\HTML\Style\Generic as GenericStyleWriter;
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->phpWord)) {
            throw new Exception('PhpWord object unassigned.');
        }

        $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();
    }

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

        return $content;
    }

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

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

        return $content;
    }

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

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

        if ($countSections > 0) {
            foreach ($sections as $section) {
                $writer = new Container($this, $section);
                $content .= $writer->write();
            }
        }

        return $content;
    }

    /**
     * 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;
    }

    /**
     * Write footnote/endnote contents as textruns
     */
    private function writeNotes()
    {
        $phpWord = $this->getPhpWord();
        $content = PHP_EOL;

        if (!empty($this->notes)) {
            $content .= "<hr />" . PHP_EOL;
            foreach ($this->notes as $noteId => $noteMark) {
                list($noteType, $noteTypeId) = explode('-', $noteMark);
                $method = 'get' . ($noteType == 'endnote' ? 'Endnotes' : 'Footnotes');
                $collection = $phpWord->$method()->getItems();

                if (array_key_exists($noteTypeId, $collection)) {
                    $element = $collection[$noteTypeId];
                    $noteAnchor = "<a name=\"note-{$noteId}\" />";
                    $noteAnchor .= "<a href=\"#{$noteMark}\" class=\"NoteRef\"><sup>{$noteId}</sup></a>";

                    $writer = new TextRunWriter($this, $element);
                    $writer->setOpeningText($noteAnchor);
                    $content .= $writer->write();
                }
            }
        }

        return $content;
    }

    /**
     * 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;
    }
}
