/****************************************************************************
**
** Copyright (C) 2012 Róbert Márki
**
** This file is part of Web Feeds.
**
** Web Feeds is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** Web Feeds 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 General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Web Feeds.  If not, see <http://www.gnu.org/licenses/>.
****************************************************************************/

#include "globals.h"
#include "feedclassifiers.h"

namespace NewsBlur
{
/*!
  \class NewsBlurEnum
  \brief The NewsBlurEnum class encapsulates commonly used static functions
  and enumerations.

  This class acts as a container for globally used enumerations, that should also be
  accessible from QML, since only the enumerations that are members of some
  class can be exposed to QML.
*/
/*!
  \enum NewsBlurEnum::ServiceRequestError
  Specifies the type of error that can occur during NewsBlur related operations.

  \var NewsBlurEnum::ServiceRequestError NewsBlurEnum::SreNoError
  No error has occured.
  \var NewsBlurEnum::ServiceRequestError NewsBlurEnum::SreUndefinedError
  An unknown error.
  \var NewsBlurEnum::ServiceRequestError NewsBlurEnum::SreAborted
  The operation has been aborted.
  \var NewsBlurEnum::ServiceRequestError NewsBlurEnum::SreNetworkError
  Some kind of network error occurred.
  \var NewsBlurEnum::ServiceRequestError NewsBlurEnum::SreInvalidReplyContents
  Failed parse the contetns of a network reply.
  \var NewsBlurEnum::ServiceRequestError NewsBlurEnum::SreResponseResultFalse
  The network operaion failed on the server side.
  \var NewsBlurEnum::ServiceRequestError NewsBlurEnum::SreInvalidAccount
  Authentication data is invalid.
*/
/*!
  \enum NewsBlurEnum::ClassificationFilter
  Specifies the type of item filtering.

  \var NewsBlurEnum::ClassificationFilter NewsBlurEnum::CfNoFilter
  Every item should be accepted by the filter.
  \var NewsBlurEnum::ClassificationFilter NewsBlurEnum::CfNonNegative
  Only the items with a non negative classification are accepted.
  \var NewsBlurEnum::ClassificationFilter NewsBlurEnum::CfOnlyPositive
  Only the items with a positive classification are accepted.
*/
/*!
  \enum NewsBlurEnum::ClassifierValue
  Specifies the value of a feed classifier.

  \var NewsBlurEnum::ClassifierValue NewsBlurEnum::CvUndefinedClassifierValue
  Uninitialized classifier.
  \var NewsBlurEnum::ClassifierValue NewsBlurEnum::CvNegativeClassifier
  Negative classification.
  \var NewsBlurEnum::ClassifierValue NewsBlurEnum::CvNeutralClassifier
  Neutral classification.
  \var NewsBlurEnum::ClassifierValue NewsBlurEnum::CvPositiveClassifier
  Positive classification.
*/
/*!
  \var NewsBlurEnum::s_highlightColor
  The highlight color of neutral classification.
*/
/*!
  \var NewsBlurEnum::s_negativeHighlightColor
  The highlight color of negative classification.
*/
/*!
  \var NewsBlurEnum::s_positiveHighlightColor
  The highlight color of positive classification.
*/

const char* NewsBlurEnum::s_highlightColor          = "#e3a65b";
const char* NewsBlurEnum::s_negativeHighlightColor  = "#e39a9a";
const char* NewsBlurEnum::s_positiveHighlightColor  = "#82ba82";

/*!
  Returns true if the \a classification is acceptable by the \a filter.
*/
bool NewsBlurEnum::filterAcceptsClassification(NewsBlurEnum::ClassificationFilter filter,
                                        NewsBlurEnum::ClassifierValue classification)
{
    bool ret = true;
    if(filter == NewsBlurEnum::CfNonNegative)
    {
        ret = ((classification == NewsBlurEnum::CvNeutralClassifier)
                || (classification == NewsBlurEnum::CvPositiveClassifier));
    }
    else if(filter == NewsBlurEnum::CfOnlyPositive)
    {
        ret = (classification == NewsBlurEnum::CvPositiveClassifier);
    }
    return ret;
}

/*!
  Converts classifier values from their textual representation to NewsBlurEnum::ClassifierValue identifiers.
*/
NewsBlurEnum::ClassifierValue NewsBlurEnum::classifierFromString(const QString &classifier)
{
    return NewsBlurEnum::ClassifierValue(classifier.toInt());
}

/*!
  Returns the final classification of a story based on it's \a tagClassifier, \a authorClassifier,
  \a titleClassifier and \a feedClassifier value.
*/
NewsBlurEnum::ClassifierValue NewsBlurEnum::storyClassification(NewsBlurEnum::ClassifierValue tagClassifier,
                                                                NewsBlurEnum::ClassifierValue authorClassifier,
                                                                NewsBlurEnum::ClassifierValue titleClassifier,
                                                                NewsBlurEnum::ClassifierValue feedClassifier)
{
    NewsBlurEnum::ClassifierValue ret = NewsBlurEnum::CvUndefinedClassifierValue;
    bool posClassifierExists = false;
    bool negClassifierExists = false;

    //stop when a positive classification is found, otherwise continue
    if(tagClassifier == NewsBlurEnum::CvPositiveClassifier)
        posClassifierExists = true;
    else if(tagClassifier == NewsBlurEnum::CvNegativeClassifier)
        negClassifierExists = true;
    if(!posClassifierExists)
    {
        if(authorClassifier == NewsBlurEnum::CvPositiveClassifier)
            posClassifierExists = true;
        else if(authorClassifier == NewsBlurEnum::CvNegativeClassifier)
            negClassifierExists = true;
        if(!posClassifierExists)
        {
            if(titleClassifier == NewsBlurEnum::CvPositiveClassifier)
                posClassifierExists = true;
            else if(titleClassifier == NewsBlurEnum::CvNegativeClassifier)
                negClassifierExists = true;
        }
    }

    //if a positive classification exists the final result is positive
    if(posClassifierExists)
        ret = NewsBlurEnum::CvPositiveClassifier;
    //otherwise check if negative classification exists
    else if(negClassifierExists)
        ret = NewsBlurEnum::CvNegativeClassifier;
    //if no non neutral classification has been found, just use the feed's classification
    else
        ret = feedClassifier;
    return ret;
}

/*!
  Returns the final classification of a story based on the feeds \a classifierSet,
  and on the story's \a storyTitle, \a storyAuthor and \a storyTags.
*/
NewsBlurEnum::ClassifierValue NewsBlurEnum::storyClassification(const FeedClassifiers &classifierSet,
                                                                const QString &storyTitle, const QString &storyAuthor,
                                                                const QStringList &storyTags)
{
    NewsBlurEnum::ClassifierValue ret = NewsBlurEnum::CvUndefinedClassifierValue;
    bool posClassifierExists = false;
    bool negClassifierExists = false;
    ClassifierHash tagClassifiers = classifierSet.tagClassifiers();
    ClassifierHash authorClassifiers = classifierSet.authorClassifiers();
    ClassifierHash titleClassifiers = classifierSet.titleClassifiers();

    //stop when a positive classification is found, otherwise continue
    if(authorClassifiers.contains(storyAuthor))
    {
        if(authorClassifiers.value(storyAuthor) == NewsBlurEnum::CvPositiveClassifier)
            posClassifierExists = true;
        else if(authorClassifiers.value(storyAuthor) == NewsBlurEnum::CvNegativeClassifier)
            negClassifierExists = true;
    }
    //if there is no positive classification yet check the title classifiers
    if(!posClassifierExists)
    {
        //loop through the feeds title classifiers
        foreach(const QString& title, titleClassifiers.keys())
        {
            //if the given stories title contains the text of the title classifier
            if(storyTitle.contains(title, Qt::CaseInsensitive))
            {
                if(titleClassifiers.value(title) == NewsBlurEnum::CvPositiveClassifier)
                {
                    posClassifierExists = true;
                    break;
                }
                else if(titleClassifiers.value(title) == NewsBlurEnum::CvNegativeClassifier)
                {
                    negClassifierExists = true;
                }
            }
        }
        //if there is no positive classification yet check the tag classifiers
        if(!posClassifierExists)
        {
            //loop through the list of tags of the story
            foreach(const QString& storyTag, storyTags)
            {
                //if the feeds tag classifiers contain the tag
                if(tagClassifiers.contains(storyTag))
                {
                    if(tagClassifiers.value(storyTag) == NewsBlurEnum::CvPositiveClassifier)
                    {
                        posClassifierExists = true;
                        break;
                    }
                    else if(tagClassifiers.value(storyTag) == NewsBlurEnum::CvNegativeClassifier)
                    {
                        negClassifierExists = true;
                    }
                }
            }
        }
    }

    //if a positive classification exists the final result is positive
    if(posClassifierExists)
        ret = NewsBlurEnum::CvPositiveClassifier;
    //otherwise check if negative classification exists
    else if(negClassifierExists)
        ret = NewsBlurEnum::CvNegativeClassifier;
    //if no non neutral classification has been found, just use the feed's classification
    else
        ret = classifierSet.feedClassifier();
    return ret;
}
} // namespace NewsBlur
