/*
 * Copyright © 2022 UBports foundation.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 3,
 * as published by the Free Software Foundation.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authored by: Alexander Martinz <amartinz@shiftphones.com>
 */

#include "fs_double_tap_to_wake.h"

namespace
{

char const* const log_tag = "FsDoubleTapToWake";

std::vector<std::string> split_value_by(std::string const& to_split, std::string const& delimiter)
{
    std::vector<std::string> parts;
    size_t last = 0;
    size_t next = 0;

    while ((next = to_split.find(delimiter, last)) != std::string::npos)
    {
        auto part = to_split.substr(last, next - last);
        parts.push_back(part);
        last = next + 1;
    }
    parts.push_back(to_split.substr(last));

    return parts;
}

}

repowerd::FsDoubleTapToWake::FsDoubleTapToWake(
        std::shared_ptr<Log> const& log,
        std::shared_ptr<Filesystem> const& filesystem)
        : log{log},
          filesystem{filesystem}
{
    DeviceInfo device_info;
    auto configvalue = device_info.get("DoubleTapToWake", "");
    parse_config(configvalue);
}

repowerd::FsDoubleTapToWake::~FsDoubleTapToWake() = default;

bool repowerd::FsDoubleTapToWake::is_supported()
{
    return !configs.empty();
}

void repowerd::FsDoubleTapToWake::enable()
{
    set_enabled(true);
}

void repowerd::FsDoubleTapToWake::disable()
{
    set_enabled(false);
}

void repowerd::FsDoubleTapToWake::set_enabled(bool enable)
{
    log->logDebug(log_tag, "%s DoubleTapToWake", enable ? "Enabling" : "Disabling");

    for (auto const& config : configs)
    {
        if (!filesystem->is_regular_file(config.path))
        {
            log->logWarning(log_tag, "Invalid path (%s)), skipping", config.path.c_str());
            continue;
        }

        auto ostream = filesystem->ostream(config.path);
        *ostream << (enable ? config.enable_value : config.disable_value);
        ostream->flush();
    }
}

bool repowerd::FsDoubleTapToWake::is_enabled()
{
    bool enabled = false;

    for (auto const& config : configs)
    {
        if (!filesystem->is_regular_file(config.path))
        {
            log->logWarning(log_tag, "Invalid path (%s)), skipping", config.path.c_str());
            continue;
        }

        auto istream = filesystem->istream(config.path);
        std::string value;
        *istream >> value;

        if (value == config.enable_value)
            enabled = true;
        else
            return false;
    }

    log->logDebug(log_tag, "Enabled -> %s", enabled ? "true" : "false");
    return enabled;
}

void repowerd::FsDoubleTapToWake::parse_config(std::string const& configvalue)
{
    if (configvalue.empty())
    {
        log->logDebug(log_tag, "No DoubleTapToWake config set");
        return;
    }
    log->logDebug(log_tag, "Parsing config: %s", configvalue.c_str());

    /*
     * Config format: /path/to/node|ON|OFF,/path/to/another/node|ON|OFF,...
     * Example: /proc/touchpanel/double_tap_enable|1|0,/sys/devices/platform/soc/a84000.i2c/i2c-2/2-0020/input/input1/wake_gesture|on|off
     */

    auto const entries = split_value_by(configvalue, ",");

    for (auto const& entry : entries)
    {
        log->logDebug(log_tag, "Parsing entry: %s", entry.c_str());

        auto const parts = split_value_by(entry, "|");
        if (parts.size() != 3)
        {
            log->logWarning(log_tag, "Invalid config entry, expected (%d) but got (%zu), skipping",
                    3, parts.size());
            continue;
        }

        if (!filesystem->is_regular_file(parts[0]))
        {
            log->logWarning(log_tag, "Invalid config path (%s)), skipping", parts[0].c_str());
            continue;
        }

        FsDoubleTapToWakeConfig config;
        config.path = parts[0];
        config.enable_value = parts[1];
        config.disable_value = parts[2];

        configs.push_back(config);
    }
}
