1 // =================================
  2 // Copyright (c) 2021 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 #include <soulng/util/Log.hpp>
  7 #include <soulng/util/TextUtils.hpp>
  8 #include <soulng/util/Unicode.hpp>
  9 #include <iostream>
 10 #include <mutex>
 11 #include <list>
 12 #include <condition_variable>
 13 #include <thread>
 14 #include <chrono>
 15 #include <atomic>
 16 
 17 namespace soulng { namespace util {
 18 
 19 using namespace soulng::unicode;
 20 
 21 std::mutex logMutex;
 22 LogMode logMode = LogMode::console;
 23 bool endLog = false;
 24 std::list<std::string> log;
 25 std::condition_variable messageEnqueuedOrEndLog;
 26 
 27 void SetLogMode(LogMode mode)
 28 {
 29     logMode = mode;
 30 }
 31 
 32 void StartLog()
 33 {
 34     endLog = false;
 35 }
 36 
 37 void EndLog()
 38 {
 39     for (int i = 0; i < 10; ++i)
 40     {
 41         if (!log.empty())
 42         {
 43             messageEnqueuedOrEndLog.notify_one();
 44             std::this_thread::sleep_for(std::chrono::milliseconds({500 }));
 45         }
 46         else
 47         {
 48             break;
 49         }
 50     }
 51     endLog = true;
 52     messageEnqueuedOrEndLog.notify_one();
 53 }
 54 
 55 void LogMessage(int logStreamIdconst std::string& message)
 56 {
 57     std::lock_guard<std::mutex> lock(logMutex);
 58     if (logMode == LogMode::console)
 59     {
 60         if (logStreamId == -1)
 61         {
 62             std::cout << message << std::endl;
 63         }
 64         else
 65         {
 66             std::cout << Format(std::to_string(logStreamId)2FormatWidth::minFormatJustify::right'0') << ">" << message << std::endl;
 67         }
 68     }
 69     else if (logMode == LogMode::queue)
 70     {
 71         if (logStreamId == -1)
 72         {
 73             log.push_back(message);
 74         }
 75         else
 76         {
 77             log.push_back(Format(std::to_string(logStreamId)2FormatWidth::minFormatJustify::right'0') + ">" + message);
 78         }
 79         messageEnqueuedOrEndLog.notify_one();
 80     }
 81 }
 82 
 83 void LogMessage(int logStreamIdconst std::string& messageint indent)
 84 {
 85     LogMessage(logStreamIdstd::string(indent' ') + message);
 86 }
 87 
 88 std::string logMessage;
 89 
 90 int WaitForLogMessage()
 91 {
 92     std::unique_lock<std::mutex> lock(logMutex);
 93     messageEnqueuedOrEndLog.wait(lock[]{ return !log.empty() || endLog; });
 94     if (!log.empty())
 95     {
 96         logMessage = log.front();
 97         log.pop_front();
 98         return logMessage.length();
 99     }
100     else
101     {
102         return -1;
103     }
104 }
105 
106 int FetchLogMessage(char16_t* bufint size)
107 {
108     std::u16string utf16LogMessage = ToUtf16(logMessage);
109     if (size <= utf16LogMessage.length())
110     {
111         return -1;
112     }
113     else
114     {
115         int n = utf16LogMessage.length();
116         for (int i = 0; i < n; ++i)
117         {
118             char16_t c = utf16LogMessage[i];
119             buf[i] = c;
120         }
121         buf[n] = u'\0';
122         return n;
123     }
124 }
125 
126 std::string FetchLogMessage(bool& endOfLogint timeoutMsbool& timeout)
127 {
128     endOfLog = false;
129     std::unique_lock<std::mutex> lock(logMutex);
130     if (timeoutMs)
131     {
132         if (!messageEnqueuedOrEndLog.wait_for(lockstd::chrono::milliseconds({timeoutMs }) [] { return !log.empty() || endLog; }))
133         {
134             timeout = true;
135             return std::string();
136         }
137     }
138     else
139     {
140         messageEnqueuedOrEndLog.wait(lock[] { return !log.empty() || endLog; });
141     }
142     if (!log.empty())
143     {
144         logMessage = log.front();
145         log.pop_front();
146         return logMessage;
147     }
148     endOfLog = true;
149     return std::string();
150 }
151 
152 } } // namespace soulng::util