<?php namespace HashOver;

// Copyright (C) 2010-2021 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/>.


class Thread
{
protected $setup;

public $data;
public $commentList = array ();
public $threadCount = array ();
public $primaryCount = 1;
public $totalCount = 1;
public $primaryDeletedCount = 0;
public $collapsedDeletedCount = 0;
public $totalDeletedCount = 0;

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

// Name of data format class to instantiate
$data_class = 'HashOver\\Parse' . strtoupper ($setup->dataFormat);

// Instantiate data format class
$this->data = new $data_class ($setup, $this);
}

// Queries a list of comments
public function queryComments ()
{
// Query a list of comments
$comment_list = $this->data->query ();

// Check if comments could be queried
if (!empty ($comment_list)) {
// If so, set comments as comment list
$this->commentList = $comment_list;

// And organize comments
$this->organizeComments ();
}
}

// Counts a comment
public function countComment ($comment)
{
// Check if comment has replies
if (strpos ($comment, '-') !== false) {
// If so, split comment by dashes
$file_parts = explode ('-', $comment);

// Get parent comment
$thread = basename ($comment, '-' . end ($file_parts));

// Check if parent comment reply count exists
if (isset ($this->threadCount[$thread])) {
// If so, increase parent comment reply count
$this->threadCount[$thread]++;
} else {
// If not, create parent comment reply count
$this->threadCount[$thread] = 1;
}
} else {
// If not, increase primary comment count
$this->primaryCount++;
}

// Check if thread comment count exists
if (isset ($this->threadCount[$comment])) {
// If so, increase thread comment count
$this->threadCount[$comment]++;
} else {
// If not, create thread comment count
$this->threadCount[$comment] = 1;
}

// Increase total comment count
$this->totalCount++;
}

// Explode a string, cast substrings to integers
protected function intExplode ($delimiter, $string)
{
// Initial integers
$ints = array ();

// Split string by delimiter
$parts = explode ($delimiter, $string);

// Cast all parts of string to integers
for ($i = 0, $il = count ($parts); $i < $il; $i++) {
$ints[] = (int)($parts[$i]);
}

return $ints;
}

// Counts a deleted comment
protected function countDeleted ($comment)
{
// Count deleted replies
if (strpos ($comment, '-') === false) {
$this->primaryDeletedCount++;
}

// Get count from comment key
$comment_parts = $this->intExplode ('-', $comment);
$comment_count = array_sum ($comment_parts);

// Count collapsed deleted comments
if ($comment_count > $this->setup->collapseLimit) {
$this->collapsedDeletedCount++;
}

// Count all other deleted comments
$this->totalDeletedCount++;
}

// Check for missing comments
protected function findMissingComments ($key)
{
// Get integers from key
$key_parts = $this->intExplode ('-', $key);

// Initial comment tree
$comment_tree = '';

// Run through each key
foreach ($key_parts as $key => $reply) {
// Check for comments counting backward from the current
for ($i = 1; $i <= $reply; $i++) {
// Current level to check for
if ($key > 0) {
$current = $comment_tree . '-' . $i;
} else {
$current = $i;
}

// Check for the comment in the list
if (!isset ($this->commentList[$current])) {
// If it doesn't exist, mark comment as missing
$this->commentList[$current] = 'missing';

// Count the missing comment
$this->countComment ($current);
$this->countDeleted ($current);
}
}

// Add current reply number to tree
if ($key > 0) {
$comment_tree .= '-' . $reply;
} else {
$comment_tree = $reply;
}
}
}

// Organize comments
protected function organizeComments ()
{
// Run through comment list
foreach ($this->commentList as $key) {
// Check for missing comments
$this->findMissingComments ($key);

// Count comment
$this->countComment ($key);
}

// Sort comments by their keys alphabetically in ascending order
uksort ($this->commentList, 'strnatcasecmp');
}

// Read comments
public function read ($end = -1)
{
// Initial data to return
$comments = array ();

// Number of comments successfully added to return data
$added_count = 0;

// Run through each comment
foreach ($this->commentList as $i => $key) {
// Stop at end point
if ($end !== -1 and $added_count >= $end) {
break;
}

// Skip deleted comments
if ($key === 'missing') {
$comments[$i]['status'] = 'missing';
continue;
}

// Read comment
$comment = $this->data->read ($key);

// See if it read successfully
if ($comment !== false) {
// If so, add the comment to output
$comments[$i] = $comment;

// Count deleted status comments
if (Misc::getArrayItem ($comment, 'status') === 'deleted') {
$this->countDeleted ($key);
}

// And increase added count
$added_count++;
} else {
// If not, set comment status as a read error
$comments[$i]['status'] = 'read-error';
}
}

return $comments;
}

// Queries an array of websites
public function queryWebsites ()
{
return $this->data->queryWebsites ();
}

// Queries an array of comment threads
public function queryThreads ()
{
return $this->data->queryThreads ();
}
}