//+------------------------------------------------------------------+
//|                                                      Message.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
#property strict    // Нужно для mql4
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//| Класс сообщения                                                  |
//+------------------------------------------------------------------+
class CMessage
  {
private:
   static int        m_global_error;
   static uchar      m_lang_num;
   static string     m_subject;
   static string     m_text;
   static bool       m_log;
   static bool       m_push;
   static bool       m_mail;
//--- Получает сообщение из нужного массива по идентификатору сообщения
   static void       GetTextByID(const int msg_id);
public:
//--- (1) Выводит текстовое сообщение в журнал, отправляет Push и e-mail,
//--- (2) Выводит сообщение в журнал по идентификатору, отправляет Push и e-mail,
//--- (3) воспроизводит звуковой файл
//--- (4) Останавливает воспроизведение любого звука
   static bool       Out(const string text,const bool push=false,const bool mail=false,const string subject=NULL);
   static bool       OutByID(const int msg_id,const bool code=true);
   static bool       PlaySound(const string file_name);
   static bool       StopPlaySound(void);
   //--- Возвращает (1) сообщение, (2) код в формате "(код)"
   static string     Text(const int msg_id);
   static string     Retcode(const int msg_id)     { return "("+(string)msg_id+")";    }
//--- Устанавливает (1) номер языка текстовых сообщений (0 - язык страны пользователя, 1 - английский, 2 ... N - добавляемые пользователем),
//--- (2) заголовок письма,
//--- флаг отправки сообщений (3) в журнал, (4) на мобильное устройство, (5) на почту
   static void       SetLangNum(const uchar num=0) { CMessage::m_lang_num=num;         }
   static void       SetSubject(const string subj) { CMessage::m_subject=subj;         }
   static void       SetLog(const bool flag)       { CMessage::m_log=flag;             }
   static void       SetPush(const bool flag)      { CMessage::m_push=flag;            }
   static void       SetMail(const bool flag)      { CMessage::m_mail=flag;            }
//--- (1) выводит сообщение в журнал по идентификатору, (2) на e-mail, (3) на мобильное устройство
   static void       ToLog(const int msg_id,const bool code=false);
   static bool       ToMail(const string message,const string subject=NULL);
   static bool       Push(const string message);
//--- (1) отправляет файл на FTP, (2) возвращает код ошибки
   static bool       ToFTP(const string filename,const string ftp_path=NULL);
   static int        GetError(void)                { return CMessage::m_global_error;  }
  };
//+------------------------------------------------------------------+
//| Инициализация статических переменных                             |
//+------------------------------------------------------------------+
uchar  CMessage::m_lang_num=(::TerminalInfoString(TERMINAL_LANGUAGE)==COUNTRY_LANG ? 0 : 1);
int    CMessage::m_global_error=ERR_SUCCESS;
string CMessage::m_subject=::MQLInfoString(MQL_PROGRAM_NAME);
string CMessage::m_text=NULL;
bool   CMessage::m_log=true;
bool   CMessage::m_push=true;
bool   CMessage::m_mail=false;
//+------------------------------------------------------------------+
//| Выводит текстовое сообщение в журнал, отправляет Push и e-mail   |
//+------------------------------------------------------------------+
bool CMessage::Out(const string text,const bool push=false,const bool mail=false,const string subject=NULL)
  {
   bool res=true;
   if(CMessage::m_log)
      ::Print(text);
   if(push)
      res &=CMessage::Push(text);
   if(mail)
      res &=CMessage::ToMail(text,subject);
   return res;
  }
//+------------------------------------------------------------------+
//| Выводит сообщение в журнал по идентификатору,                    |
//| отправляет Push и e-mail                                         |
//+------------------------------------------------------------------+
bool CMessage::OutByID(const int msg_id,const bool code=true)
  {
   bool res=true;
   if(CMessage::m_log)
      CMessage::ToLog(msg_id,code);
   else
      CMessage::GetTextByID(msg_id);
   if(CMessage::m_push)
      res &=CMessage::Push(CMessage::m_text);
   if(CMessage::m_mail)
      res &=CMessage::ToMail(CMessage::m_text,CMessage::m_subject);
   return res;
  }
//+------------------------------------------------------------------+
//| Выводит сообщение в журнал по идентификатору сообщения           |
//+------------------------------------------------------------------+
void CMessage::ToLog(const int msg_id,const bool code=false)
  {
   CMessage::GetTextByID(msg_id);
   ::Print(m_text,(!code || msg_id>ERR_USER_ERROR_FIRST-1 ? "" : " "+CMessage::Retcode(msg_id)));
  }
//+------------------------------------------------------------------+
//| Отправляет сообщение на почту                                    |
//+------------------------------------------------------------------+
bool CMessage::ToMail(const string message,const string subject=NULL)
  {
   //--- Если в терминале не разрешена отправка сообщений на почту
   if(!::TerminalInfoInteger(TERMINAL_EMAIL_ENABLED))
     {
      //--- выведем об этом сообщение в журнал, запишем код ошибки и вернём false
      CMessage::ToLog(MSG_LIB_TEXT_TERMINAL_NOT_MAIL_ENABLED,false);
      CMessage::m_global_error=ERR_MAIL_SEND_FAILED;
      return false;
     }
   //--- Если не удалось отправить сообщение
   if(!::SendMail(subject==NULL ? CMessage::m_subject : subject,message))
     {
      //--- запишем код ошибки, создадим текст описания ошибки, распечатаем его в журнал и вернём false
      CMessage::m_global_error=::GetLastError();
      string txt=CMessage::Text(MSG_LIB_SYS_ERROR)+CMessage::Text(CMessage::m_global_error);
      string code=CMessage::Retcode(CMessage::m_global_error);
      ::Print(txt+" "+code);
      return false;
     }
   //--- Успешно - возвращаем true
   return true;
  }
//+------------------------------------------------------------------+
//| Отправляет Push-уведомления на мобильное устройство              |
//+------------------------------------------------------------------+
bool CMessage::Push(const string message)
  {
   //--- Если в терминале не разрешена отправка Push-уведомлений
   if(!::TerminalInfoInteger(TERMINAL_NOTIFICATIONS_ENABLED))
     {
      //--- выведем об этом сообщение в журнал, запишем код ошибки и вернём false
      CMessage::ToLog(MSG_LIB_TEXT_TERMINAL_NOT_PUSH_ENABLED,false);
      CMessage::m_global_error=ERR_NOTIFICATION_SEND_FAILED;
      return false;
     }
   //--- Если не удалось отправить сообщение
   if(!::SendNotification(message))
     {
      //--- запишем код ошибки, создадим текст описания ошибки, распечатаем его в журнал и вернём false
      CMessage::m_global_error=::GetLastError();
      string txt=CMessage::Text(MSG_LIB_SYS_ERROR)+CMessage::Text(CMessage::m_global_error);
      string code=CMessage::Retcode(CMessage::m_global_error);
      ::Print(txt+" "+code);
      return false;
     }
   //--- Успешно - возвращаем true
   return true;
  }
//+------------------------------------------------------------------+
//| Отправляет файл по указанному адресу                             |
//+------------------------------------------------------------------+
bool CMessage::ToFTP(const string filename,const string ftp_path=NULL)
  {
   //--- Если в терминале не разрешена отправка файлов на FTP-сервер
   if(!::TerminalInfoInteger(TERMINAL_FTP_ENABLED))
     {
      //--- выведем об этом сообщение в журнал, запишем код ошибки и вернём false
      CMessage::ToLog(MSG_LIB_TEXT_TERMINAL_NOT_FTP_ENABLED,false);
      CMessage::m_global_error=ERR_FTP_SEND_FAILED;
      return false;
     }
   //--- Если не удалось отправить файл
   if(!::SendFTP(filename,ftp_path))
     {
      //--- запишем код ошибки, создадим текст описания ошибки, распечатаем его в журнал и вернём false
      CMessage::m_global_error=::GetLastError();
      string txt=CMessage::Text(MSG_LIB_SYS_ERROR)+CMessage::Text(CMessage::m_global_error);
      string code=CMessage::Retcode(CMessage::m_global_error);
      ::Print(txt+" "+code);
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+
//| Воспроизводит звуковой файл                                      |
//+------------------------------------------------------------------+
bool CMessage::PlaySound(const string file_name)
  {
   if(file_name==NULL)
      return true;
   string pref=(file_name==SND_ALERT       || file_name==SND_ALERT2   || file_name==SND_CONNECT  || 
                file_name==SND_DISCONNECT  || file_name==SND_EMAIL    || file_name==SND_EXPERT   ||
                file_name==SND_NEWS        || file_name==SND_OK       || file_name==SND_REQUEST  || 
                file_name==SND_STOPS       || file_name==SND_TICK     || file_name==SND_TIMEOUT  ||
                file_name==SND_WAIT        ?  "" : "\\Files\\");
   bool res=::PlaySound(pref+file_name);
   CMessage::m_global_error=(res ? ERR_SUCCESS : ::GetLastError());
   return res;
  }
//+------------------------------------------------------------------+
//| Останавливает воспроизведение любого звука                       |
//+------------------------------------------------------------------+
bool CMessage::StopPlaySound(void)
  {
   bool res=::PlaySound(NULL);
   CMessage::m_global_error=(res ? ERR_SUCCESS : ::GetLastError());
   return res;
  }
//+------------------------------------------------------------------+
//| Возвращает текст сообщения по идентификатору сообщения           |
//+------------------------------------------------------------------+
string CMessage::Text(const int msg_id)
  {
   CMessage::GetTextByID(msg_id);
   return m_text;
  }
//+------------------------------------------------------------------+
//| Получение сообщения из массива текстов по идентификатору         |
//+------------------------------------------------------------------+
void CMessage::GetTextByID(const int msg_id)
  {
   CMessage::m_text=
     (
      //--- Ошибки времени выполнения (0, 4001 - 4019)
      msg_id==0                     ?  messages_runtime[msg_id][m_lang_num]                       :
     #ifdef __MQL5__
      msg_id>4000 && msg_id<4020    ?  messages_runtime[msg_id-4000][m_lang_num]                  :
      //--- Ошибки времени выполнения (Графики 4101 - 4116)
      msg_id>4100 && msg_id<4117    ?  messages_runtime_charts[msg_id-4101][m_lang_num]           :
      //--- Ошибки времени выполнения (Графические объекты 4201 - 4205)
      msg_id>4200 && msg_id<4206    ?  messages_runtime_graph_obj[msg_id-4201][m_lang_num]        :
      //--- Ошибки времени выполнения (MarketInfo 4301 - 4305)
      msg_id>4300 && msg_id<4306    ?  messages_runtime_market[msg_id-4301][m_lang_num]           :
      //--- Ошибки времени выполнения (Доступ к истории 4401 - 4407)
      msg_id>4400 && msg_id<4408    ?  messages_runtime_history[msg_id-4401][m_lang_num]          :
      //--- Ошибки времени выполнения (Global Variables 4501 - 4524)
      msg_id>4500 && msg_id<4525    ?  messages_runtime_global[msg_id-4501][m_lang_num]           :
      //--- Ошибки времени выполнения (Пользовательские индикаторы 4601 - 4603)
      msg_id>4600 && msg_id<4604    ?  messages_runtime_custom_indicator[msg_id-4601][m_lang_num] :
      //--- Ошибки времени выполнения (Account 4701 - 4758)
      msg_id>4700 && msg_id<4759    ?  messages_runtime_account[msg_id-4701][m_lang_num]          :
      //--- Ошибки времени выполнения (Индикаторы 4801 - 4812)
      msg_id>4800 && msg_id<4813    ?  messages_runtime_indicator[msg_id-4801][m_lang_num]        :
      //--- Ошибки времени выполнения (Стакан цен 4901 - 4904)
      msg_id>4900 && msg_id<4905    ?  messages_runtime_books[msg_id-4901][m_lang_num]            :
      //--- Ошибки времени выполнения (Файловые операции 5001 - 5027)
      msg_id>5000 && msg_id<5028    ?  messages_runtime_files[msg_id-5001][m_lang_num]            :
      //--- Ошибки времени выполнения (Преобразование строк 5030 - 5044)
      msg_id>5029 && msg_id<5045    ?  messages_runtime_string[msg_id-5030][m_lang_num]           :
      //--- Ошибки времени выполнения (Работа с массивами 5050 - 5063)
      msg_id>5049 && msg_id<5064    ?  messages_runtime_array[msg_id-5050][m_lang_num]            :
      //--- Ошибки времени выполнения (Работа с OpenCL 5100 - 5114)
      msg_id>5099 && msg_id<5115    ?  messages_runtime_opencl[msg_id-5100][m_lang_num]           :
      //--- Ошибки времени выполнения (Работа с WebRequest() 5200 - 5203)
      msg_id>5199 && msg_id<5204    ?  messages_runtime_webrequest[msg_id-5200][m_lang_num]       :
      //--- Ошибки времени выполнения (Работа с сетью (сокетами) 5270 - 5275)
      msg_id>5269 && msg_id<5276    ?  messages_runtime_netsocket[msg_id-5270][m_lang_num]        :
      //--- Ошибки времени выполнения (Пользовательские символы 5300 - 5310)
      msg_id>5299 && msg_id<5311    ?  messages_runtime_custom_symbol[msg_id-5300][m_lang_num]    :
      //--- Ошибки времени выполнения (Экономический календарь 5400 - 5402)
      msg_id>5399 && msg_id<5403    ?  messages_runtime_calendar[msg_id-5400][m_lang_num]         :
      //--- Коды возврата торгового сервера (10004 - 10045)
      msg_id>10003 && msg_id<10046  ?  messages_ts_ret_code[msg_id-10004][m_lang_num]             :
     #else // MQL4
      msg_id>0 && msg_id<10         ?  messages_ts_ret_code_mql4[msg_id][m_lang_num]              :
      msg_id>63 && msg_id<66        ?  messages_ts_ret_code_mql4[msg_id-54][m_lang_num]           :
      msg_id>127 && msg_id<151      ?  messages_ts_ret_code_mql4[msg_id-116][m_lang_num]          :
      msg_id<4000                   ?  messages_ts_ret_code_mql4[26][m_lang_num]                  :
      //--- Ошибки времени выполнения MQL4 (4000 - 4030)
      msg_id<4031                   ?  messages_runtime_4000_4030[msg_id-4000][m_lang_num]        :
      //--- Ошибки времени выполнения MQL4 (4050 - 4075)
      msg_id>4049 && msg_id<4076    ?  messages_runtime_4050_4075[msg_id-4050][m_lang_num]        :
      //--- Ошибки времени выполнения MQL4 (4099 - 4112)
      msg_id>4098 && msg_id<4113    ?  messages_runtime_4099_4112[msg_id-4099][m_lang_num]        :
      //--- Ошибки времени выполнения MQL4 (4200 - 4220)
      msg_id>4199 && msg_id<4221    ?  messages_runtime_4200_4220[msg_id-4200][m_lang_num]        :
      //--- Ошибки времени выполнения MQL4 (4250 - 4266)
      msg_id>4249 && msg_id<4267    ?  messages_runtime_4250_4266[msg_id-4250][m_lang_num]        :
      //--- Ошибки времени выполнения MQL4 (5001 - 5029)
      msg_id>5000 && msg_id<5030    ?  messages_runtime_5001_5029[msg_id-5001][m_lang_num]        :
      //--- Ошибки времени выполнения MQL4 (5200 - 5203)
      msg_id>5199 && msg_id<5204    ?  messages_runtime_5200_5203[msg_id-5200][m_lang_num]        :
     #endif 
      //--- Сообщения библиотеки (ERR_USER_ERROR_FIRST)
      msg_id>ERR_USER_ERROR_FIRST-1 ?  messages_library[msg_id-ERR_USER_ERROR_FIRST][m_lang_num]  : 
      messages_library[MSG_LIB_SYS_ERROR_CODE_OUT_OF_RANGE-ERR_USER_ERROR_FIRST][m_lang_num]
     );
  }
//+------------------------------------------------------------------+
