/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "log/logger_output.h"

#include <chrono>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <string_view>

#include <base/containers/string_view.h>
#include <base/namespace.h>
#include <core/namespace.h>

#include "log/logger.h"

namespace LoggerUtils {
namespace {
constexpr int MS_WIDTH = 3;
}  // namespace

BASE_NS::string_view GetFilename(BASE_NS::string_view path)
{
    if (auto const pos = path.find_last_of("\\/"); pos != BASE_NS::string_view::npos) {
        return path.substr(pos + 1);
    }
    return path;
}

void PrintTimeStamp(std::ostream& outputStream)
{
    const auto now = std::chrono::system_clock::now();
    const auto time = std::chrono::system_clock::to_time_t(now);
    const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) -
                    std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch());

    std::tm localTime{};
#if defined(_WIN32)
    localtime_s(&localTime, &time);
#else
    localtime_r(&time, &localTime);
#endif
    outputStream << std::put_time(&localTime, "%D %H:%M:%S.");
    outputStream << std::right << std::setfill('0') << std::setw(MS_WIDTH) << ms.count() << std::setfill(' ');
}
}  // namespace LoggerUtils

CORE_BEGIN_NAMESPACE()
using BASE_NS::string_view;

class FileOutput final : public ILogger::IOutput {
public:
    explicit FileOutput(const string_view filePath) : IOutput(), outputStream_(filePath.data(), std::ios::app)
    {}

    void Write(
        ILogger::LogLevel logLevel, const string_view filename, int linenumber, const string_view message) override
    {
        if (outputStream_.is_open()) {
            auto& outputStream = outputStream_;

            LoggerUtils::PrintTimeStamp(outputStream);
            const auto levelString = Logger::GetLogLevelName(logLevel, true);
            outputStream << ' ' << std::string_view(levelString.data(), levelString.size());

            if (!filename.empty()) {
                outputStream << " (" << std::string_view(filename.data(), filename.size()) << ':' << linenumber
                             << "): ";
            } else {
                outputStream << ": ";
            }

            outputStream << std::string_view(message.data(), message.size()) << std::endl;
        }
    }

protected:
    void Destroy() override
    {
        delete this;
    }

private:
    std::ofstream outputStream_;
};

ILogger::IOutput::Ptr CreateLoggerFileOutput(const string_view filename)
{
    return ILogger::IOutput::Ptr{new FileOutput(filename)};
}
CORE_END_NAMESPACE()