<?php namespace HashOver;

// Copyright (C) 2010-2019 Jacob Barkdull
// This file is part of HashOver.
//
// HashOver is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// HashOver is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with HashOver. If not, see <http://www.gnu.org/licenses/>.


// Parse comments and create deleted comment note
class CommentParser
{
protected $setup;
protected $login;
protected $cookies;
protected $locale;
protected $avatars;

protected $timeModify;
protected $currentDate;
protected $shortDateLocales;
protected $todayLocale;
protected $date;
protected $time;

public function __construct (Setup $setup)
{
// Store parameters as properties
$this->setup = $setup;

// Instantiate various classes
$this->login = new Login ($setup);
$this->cookies = new Cookies ($setup, $this->login);
$this->locale = new Locale ($setup);
$this->avatars = new Avatars ($setup);

// Get current time
$current_time = new \DateTime ();

// Get 24-hour time from client
$client_time = new \DateTime ($setup->getRequest ('time'));

// Server-side hours and minutes as integers
$current_hours = (int)($current_time->format ('H'));
$current_mins = (int)($current_time->format ('i'));

// Client hours and minutes as integers
$client_hours = (int)($client_time->format ('H'));
$client_mins = (int)($client_time->format ('i'));

// Hours and minutes to adjust posting time by
$this->timeModify = sprintf (
'%+d hours %+d minutes',
$client_hours - $current_hours,
$client_mins - $current_mins
);

// Get current date without time
$this->currentDate = new \DateTime (date ('Y-m-d'));

// Known short date interval locales
$this->shortDateLocales = array (
'y' => 'date-year',
'm' => 'date-month',
'd' => 'date-day'
);

// Short date when a comment was posted today
$this->todayLocale = $this->locale->text['date-today'];

// Get date formatter for comment post date
$this->date = $this->getDateFormatter ($setup->datePattern);

// Get date formatter for comment post time
$this->time = $this->getDateFormatter ($setup->timePattern);
}

// Get date and time formatter
protected function getDateFormatter ($pattern)
{
// Instantiate International Date Formatter
return \IntlDateFormatter::create (
// Set language as configured
$this->setup->language,

// We're setting our own format
\IntlDateFormatter::NONE,
\IntlDateFormatter::NONE,

// Set timezone as configured
$this->setup->serverTimezone,

// Use Gregorian calender
\IntlDateFormatter::GREGORIAN,

// Set date format
$pattern
);
}

// Get short form date and time
protected function getDateTime (\DateTime $dt, $time)
{
// Remove time from datetime
$datetime = new \DateTime ($dt->format ('Y-m-d'));

// Get difference between today's date and timeless date
$interval = $datetime->diff ($this->currentDate);

// Attempt to get a day, month, or year interval
foreach ($this->shortDateLocales as $i => $key) {
// Skip intervals that are zero
if ($interval->$i <= 0) {
continue;
}

// Otherwise, check if interval is more than one
if ($interval->$i !== 1) {
// If so, use plural locale string
$locale = $this->locale->text[$key . 's'];
} else {
// If not, use singlur locale string
$locale = $this->locale->text[$key];
}

// Inject interval into locale string
$date = sprintf ($locale, $interval->$i);

// And return short date
return $date;
}

// Otherwise, inject time into today locale string
$date = sprintf ($this->todayLocale, $time);

return $date;
}

// Parse comment files
public function parse (array $comment, $key, $key_parts, $popular = false)
{
// Initial parsed comment data output
$output = array ();

// Get post date as-is
$date = Misc::getArrayItem ($comment, 'date');

// Get comment post datetime
$post_date = new \DateTime ($date ?: date ('Y-m-d', 0));

// Adjust post date to client timezone if enabled
if ($this->setup->usesUserTimezone === true) {
$post_date->modify ($this->timeModify);
}

// Generate permalink
if (count ($key_parts) > 1) {
$output['permalink'] = 'c' . str_replace ('-', 'r', $key);
} else {
$output['permalink'] = 'c' . $key;
}

// Append "-pop" to end of permalink if popular comment
if ($popular === true) {
$output['permalink'] .= '-pop';
}

// Add name to output
if (!empty ($comment['name'])) {
$output['name'] = $comment['name'];
}

// Check if icons are enabled
if ($this->setup->iconMode !== 'none') {
// If so, check if icons are images
if ($this->setup->iconMode === 'image') {
// If so, get MD5 hash for Gravatar from comment
$hash = Misc::getArrayItem ($comment, 'email_hash') ?: '';

// Add Gravatar URL to output
$output['avatar'] = $this->avatars->getGravatar ($hash);
} else {
// If not, use comment permalink number
$output['avatar'] = end ($key_parts);
}
}

// Add website URL to output
if (!empty ($comment['website'])) {
$output['website'] = $comment['website'];
}

// Output whether commenter receives notifications
if (!empty ($comment['email']) and !empty ($comment['notifications'])) {
if ($comment['notifications'] === 'yes') {
$output['subscribed'] = true;
}
}

// Add number of likes to output
if (!empty ($comment['likes'])) {
$output['likes'] = (int)($comment['likes']);
}

// If enabled, add number of dislikes to output
if ($this->setup->allowsDislikes === true) {
if (!empty ($comment['dislikes'])) {
$output['dislikes'] = (int)($comment['dislikes']);
}
}

// Check if the user is logged in
if ($this->login->userIsLoggedIn === true and !empty ($comment['login_id'])) {
// If so, check if this comment belongs to logged in user
if ($this->login->loginHash === $comment['login_id']) {
// If so, set user comment indictor to true
$output['user-owned'] = true;

// And set editable indictor if comment has a password
if (!empty ($comment['password'])) {
$output['editable'] = true;
}
}
}

// Admin is allowed to edit every comment
if ($this->login->userIsAdmin === true) {
$output['editable'] = true;
}

// Create like cookie hash
$like_hash = md5 ($this->setup->domain . $this->setup->threadName . '/' . $key);

// Get like cookie
$like_cookie = $this->cookies->getValue ($like_hash);

// Set liked/disliked indictor
switch ($like_cookie) {
// Comment was liked
case 'liked': {
$output['liked'] = true;
break;
}

// Comment was disliked
case 'disliked': {
// Only set indictor if disliked are enabled
if ($this->setup->allowsDislikes === true) {
$output['disliked'] = true;
}

break;
}
}

// Get micro time of comment post date
$timestamp = $post_date->getTimestamp ();

// Get localized full comment post date
$full_date = $this->date->format ($timestamp);

// Get localized full comment post time
$post_time = $this->time->format ($timestamp);

// Check if short dates are enabled
if ($this->setup->usesShortDates === true) {
// If so, get localized short date
$comment_date = $this->getDateTime ($post_date, $post_time);
} else {
// If not, use full localized date and time
$comment_date = $full_date;
}

// Check if we have a status
if (!empty ($comment['status'])) {
// If so, get comment status
$status = $comment['status'];

// Check if comment has a status other than approved
if ($status !== 'approved') {
// If so, add comment status to output
$output['status'] = (string)($status);

// Get status locale
$status_locale = $this->locale->text[$status . '-name'];

// And add status text to output
$output['status-text'] = mb_strtolower ($status_locale);
}
}

// Add comment date to output
$output['date'] = $comment_date;

// Set full date and time
$output['date-time'] = $full_date . ' - ' . $post_time;

// Add comment date as Unix timestamp to output
$output['timestamp'] = $timestamp;

// Add comment body to output
$output['body'] = $comment['body'];

// And return parsed comment data
return $output;
}

// Function for adding notices to output
public function notice ($type, $key, &$last_date)
{
// Initial notice data output
$output = array ();

// Set notice name as comment title
$output['title'] = $this->locale->text[$type . '-name'];

// Increase fallback last comment date
$last_date++;

// Add notice icon if icons are enabled
if ($this->setup->iconMode !== 'none') {
$output['avatar'] = $this->setup->getImagePath ($type . '-icon');
}

// Add notice permalink to notice data
$output['permalink'] = 'c' . str_replace ('-', 'r', $key);

// Add notice text to notice data
$output['notice'] = $this->locale->text[$type . '-note'];

// Add notice class to notice data
$output['notice-class'] = 'hashover-' . $type;

// Add last comment date as notice timestamp
$output['timestamp'] = (int)($last_date);

// And return notice data
return $output;
}
}