/*
 * Copyright 2009- ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation
 * nor does it submit to any jurisdiction.
 */

#include "TextFilterHandler.hpp"

#include <algorithm>
#include <sstream>

#include "SessionHandler.hpp"
#include "VSettings.hpp"

TextFilterHandler* TextFilterHandler::instance_ = nullptr;

//==============================================
//
// TextFilterItem
//
//==============================================

void TextFilterItem::save(VSettings* vs) const {
    vs->put("filter", filter_);
    vs->putAsBool("matched", matched_);
    vs->putAsBool("caseSensitive", caseSensitive_);
    vs->putAsBool("contextMenu", contextMenu_);
}

TextFilterItem TextFilterItem::make(VSettings* vs) {
    std::string emptyDefault = "";
    std::string filter       = vs->get("filter", emptyDefault);
    bool matched             = vs->getAsBool("matched", true);
    bool caseSensitive       = vs->getAsBool("filter", false);
    bool contextMenu         = vs->getAsBool("contextMenu", false);
    return TextFilterItem(filter, matched, caseSensitive, contextMenu);
}

bool TextFilterItem::operator==(const TextFilterItem& o) const {
    return filter_ == o.filter_ && matched_ == o.matched_ && caseSensitive_ == o.caseSensitive_;
}

//==============================================
//
// TextFilterHandler
//
//==============================================

TextFilterHandler* TextFilterHandler::Instance() {
    if (!instance_)
        instance_ = new TextFilterHandler();

    return instance_;
}

TextFilterHandler::TextFilterHandler()

{
    readSettings();
}

int TextFilterHandler::indexOf(const std::string& filter, bool matched, bool caseSensitive) const {
    if (filter.empty())
        return -1;

    TextFilterItem item(filter, matched, caseSensitive);
    for (size_t i = 0; i < items_.size(); i++) {
        if (items_[i] == item)
            return i;
    }

    return -1;
}

bool TextFilterHandler::contains(const std::string& filter, bool matched, bool caseSensitive) const {
    return indexOf(filter, matched, caseSensitive) != -1;
}

bool TextFilterHandler::containsExceptOne(int index,
                                          const std::string& filter,
                                          bool matched,
                                          bool caseSensitive) const {
    if (filter.empty())
        return false;

    TextFilterItem item(filter, matched, caseSensitive);
    for (int i = 0; i < static_cast<int>(items_.size()); i++) {
        if (i != index && items_[i] == item)
            return true;
    }
    return false;
}

bool TextFilterHandler::add(const TextFilterItem& item) {
    if (item.filter().empty())
        return false;

    items_.push_back(item);
    writeSettings();
    return true;
}

bool TextFilterHandler::add(const std::string& filter, bool matched, bool caseSensitive, bool contextMenu) {
    TextFilterItem item(filter, matched, caseSensitive, contextMenu);
    return add(item);
}

void TextFilterHandler::addLatest(const TextFilterItem& item) {
    if (item.filter().empty())
        return;

    // Remove if exists
    auto it = std::find(latest_.begin(), latest_.end(), item);
    if (it != latest_.end())
        latest_.erase(it);

    // trim size
    while (static_cast<int>(latest_.size()) >= maxLatestNum_) {
        latest_.pop_back();
    }

    // add item to front
    latest_.insert(latest_.begin(), item);

    writeSettings();
}

void TextFilterHandler::addLatest(const std::string& filter, bool matched, bool caseSensitive, bool contextMenu) {
    TextFilterItem item(filter, matched, caseSensitive, contextMenu);
    addLatest(item);
}

void TextFilterHandler::remove(int index) {
    if (index < 0 || index >= static_cast<int>(items_.size()))
        return;

    items_.erase(items_.begin() + index);
    writeSettings();
}

void TextFilterHandler::update(int index, const TextFilterItem& item) {
    if (index < 0 || index >= static_cast<int>(items_.size()))
        return;

    items_[index] = item;
    writeSettings();
}

void TextFilterHandler::allFilters(std::set<std::string>& v) {
    v.clear();
    for (auto& item : items_) {
        v.insert(item.filter());
    }
    for (auto& i : latest_) {
        v.insert(i.filter());
    }
}

std::string TextFilterHandler::settingsFile() {
    SessionItem* cs = SessionHandler::instance()->current();
    return cs->textFilterFile();
}

void TextFilterHandler::writeSettings() {
    std::string dummyFileName    = "dummy";
    std::string settingsFilePath = settingsFile();
    VSettings vs(settingsFilePath);

    std::vector<VSettings> vsItems;
    for (auto& item : items_) {
        VSettings vsThisItem(dummyFileName);
        item.save(&vsThisItem);
        vsItems.push_back(vsThisItem);
    }
    vs.put("saved", vsItems);

    vsItems.clear();
    for (auto& i : latest_) {
        VSettings vsThisItem(dummyFileName);
        i.save(&vsThisItem);
        vsItems.push_back(vsThisItem);
    }
    vs.put("latest", vsItems);

    vs.write();
}

void TextFilterHandler::readSettings() {
    std::string settingsFilePath = settingsFile();
    VSettings vs(settingsFilePath);

    bool ok = vs.read(false); // false means we don't abort if the file is not there

    if (ok) {
        std::vector<VSettings> vsItems;
        vs.get("saved", vsItems);
        for (auto& vsItem : vsItems) {
            add(TextFilterItem::make(&vsItem));
        }

        vsItems.clear();
        vs.get("latest", vsItems);
        for (auto& vsItem : vsItems) {
            addLatest(TextFilterItem::make(&vsItem));
        }
    }
    // If there is no settings file at all we automatically add this filter
    else if (!vs.fileExists()) {
        add(TextFilterItem("^\\+\\s", false, false));
    }
}
