<commit_before>////////////////////////////////////////////////////////////////////////////////
/// @brief debugging helpers
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2014 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
/// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

#include "Basics/Common.h"
#include "Basics/locks.h"
#include "Basics/logging.h"

#ifdef TRI_ENABLE_MAINTAINER_MODE
#if HAVE_BACKTRACE
#ifdef _WIN32
#include <DbgHelp.h>
#else
#include <execinfo.h>
#include <cxxabi.h>
#endif
#endif
#endif

// -----------------------------------------------------------------------------
// --SECTION--                                                 private variables
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief a global string containing the currently registered failure points
/// the string is a comma-separated list of point names
////////////////////////////////////////////////////////////////////////////////

static char* FailurePoints;

////////////////////////////////////////////////////////////////////////////////
/// @brief a read-write lock for thread-safe access to the failure-points list
////////////////////////////////////////////////////////////////////////////////

TRI_read_write_lock_t FailurePointsLock;

#ifdef TRI_ENABLE_FAILURE_TESTS

// -----------------------------------------------------------------------------
// --SECTION--                                                 private functions
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief make a delimited value from a string, so we can unambigiously
/// search for it (e.g. searching for just "foo" would find "foo" and "foobar",
/// so we'll be putting the value inside some delimiter: ",foo,")
////////////////////////////////////////////////////////////////////////////////

static char* MakeValue (char const* value) {
  if (value == nullptr || strlen(value) == 0) {
    return nullptr;
  }

  char* delimited = static_cast<char*>(TRI_Allocate(TRI_CORE_MEM_ZONE, strlen(value) + 3, false));

  if (delimited != nullptr) {
    memcpy(delimited + 1, value, strlen(value));
    delimited[0] = ',';
    delimited[strlen(value) + 1] = ',';
    delimited[strlen(value) + 2] = '\0';
  }

  return delimited;
}

// -----------------------------------------------------------------------------
// --SECTION--                                                  public functions
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief cause a segmentation violation
/// this is used for crash and recovery tests
////////////////////////////////////////////////////////////////////////////////

void TRI_SegfaultDebugging (char const* message) {
  LOG_WARNING("%s: summon Baal!", message);
  // make sure the latest log messages are flushed
  TRI_ShutdownLogging(true);

  // and now crash
#ifndef __APPLE__
  // on MacOS, the following statement makes the server hang but not crash
  *((char*) -1) = '!';
#endif

  // ensure the process is terminated
  abort();
}

////////////////////////////////////////////////////////////////////////////////
/// @brief check whether we should fail at a specific failure point
////////////////////////////////////////////////////////////////////////////////

bool TRI_ShouldFailDebugging (char const* value) {
  char* found = nullptr;

  // try without the lock first (to speed things up)
  if (FailurePoints == nullptr) {
    return false;
  }

  TRI_ReadLockReadWriteLock(&FailurePointsLock);

  if (FailurePoints != nullptr) {
    char* checkValue = MakeValue(value);

    if (checkValue != nullptr) {
      found = strstr(FailurePoints, checkValue);
      TRI_Free(TRI_CORE_MEM_ZONE, checkValue);
    }
  }

  TRI_ReadUnlockReadWriteLock(&FailurePointsLock);

  return (found != nullptr);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief add a failure point
////////////////////////////////////////////////////////////////////////////////

void TRI_AddFailurePointDebugging (char const* value) {
  char* found;
  char* checkValue;

  checkValue = MakeValue(value);

  if (checkValue == nullptr) {
    return;
  }

  TRI_WriteLockReadWriteLock(&FailurePointsLock);

  if (FailurePoints == nullptr) {
    found = nullptr;
  }
  else {
    found = strstr(FailurePoints, checkValue);
  }

  if (found == nullptr) {
    // not yet found. so add it
    char* copy;
    size_t n;

    LOG_WARNING("activating intentional failure point '%s'. the server will misbehave!", value);
    n = strlen(checkValue);

    if (FailurePoints == nullptr) {
      copy = static_cast<char*>(TRI_Allocate(TRI_CORE_MEM_ZONE, n + 1, false));

      if (copy == nullptr) {
        TRI_WriteUnlockReadWriteLock(&FailurePointsLock);
        TRI_Free(TRI_CORE_MEM_ZONE, checkValue);
        return;
      }

      memcpy(copy, checkValue, n);
      copy[n] = '\0';
    }
    else {
      copy = static_cast<char*>(TRI_Allocate(TRI_CORE_MEM_ZONE, n + strlen(FailurePoints), false));

      if (copy == nullptr) {
        TRI_WriteUnlockReadWriteLock(&FailurePointsLock);
        TRI_Free(TRI_CORE_MEM_ZONE, checkValue);
        return;
      }

      memcpy(copy, FailurePoints, strlen(FailurePoints));
      memcpy(copy + strlen(FailurePoints) - 1, checkValue, n);
      copy[strlen(FailurePoints) + n - 1] = '\0';
    }

    FailurePoints = copy;
  }

  TRI_WriteUnlockReadWriteLock(&FailurePointsLock);
  TRI_Free(TRI_CORE_MEM_ZONE, checkValue);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief remove a failure points
////////////////////////////////////////////////////////////////////////////////

void TRI_RemoveFailurePointDebugging (char const* value) {
  char* checkValue;

  TRI_WriteLockReadWriteLock(&FailurePointsLock);

  if (FailurePoints == nullptr) {
    TRI_WriteUnlockReadWriteLock(&FailurePointsLock);
    return;
  }

  checkValue = MakeValue(value);

  if (checkValue != nullptr) {
    char* found;
    char* copy;
    size_t n;

    found = strstr(FailurePoints, checkValue);

    if (found == nullptr) {
      TRI_WriteUnlockReadWriteLock(&FailurePointsLock);
      TRI_Free(TRI_CORE_MEM_ZONE, checkValue);
      return;
    }

    if (strlen(FailurePoints) - strlen(checkValue) <= 2) {
      TRI_Free(TRI_CORE_MEM_ZONE, FailurePoints);
      FailurePoints = nullptr;

      TRI_WriteUnlockReadWriteLock(&FailurePointsLock);
      TRI_Free(TRI_CORE_MEM_ZONE, checkValue);
      return;
    }

    copy = static_cast<char*>(TRI_Allocate(TRI_CORE_MEM_ZONE, strlen(FailurePoints) - strlen(checkValue) + 2, false));

    if (copy == nullptr) {
      TRI_WriteUnlockReadWriteLock(&FailurePointsLock);
      TRI_Free(TRI_CORE_MEM_ZONE, checkValue);
      return;
    }

    // copy start of string
    n = found - FailurePoints;
    memcpy(copy, FailurePoints, n);

    // copy remainder of string
    memcpy(copy + n, found + strlen(checkValue) - 1, strlen(FailurePoints) - strlen(checkValue) - n + 1);

    copy[strlen(FailurePoints) - strlen(checkValue) + 1] = '\0';
    TRI_Free(TRI_CORE_MEM_ZONE, FailurePoints);
    FailurePoints = copy;

    TRI_WriteUnlockReadWriteLock(&FailurePointsLock);
    TRI_Free(TRI_CORE_MEM_ZONE, checkValue);
  }
}

////////////////////////////////////////////////////////////////////////////////
/// @brief clear all failure points
////////////////////////////////////////////////////////////////////////////////

void TRI_ClearFailurePointsDebugging () {
  TRI_WriteLockReadWriteLock(&FailurePointsLock);

  if (FailurePoints != nullptr) {
    TRI_Free(TRI_CORE_MEM_ZONE, FailurePoints);
  }

  FailurePoints = nullptr;

  TRI_WriteUnlockReadWriteLock(&FailurePointsLock);
}

#endif

////////////////////////////////////////////////////////////////////////////////
/// @brief initialise the debugging
////////////////////////////////////////////////////////////////////////////////

void TRI_InitialiseDebugging () {
  FailurePoints = nullptr;
  TRI_InitReadWriteLock(&FailurePointsLock);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief shutdown the debugging
////////////////////////////////////////////////////////////////////////////////

void TRI_ShutdownDebugging () {
  if (FailurePoints != nullptr) {
    TRI_Free(TRI_CORE_MEM_ZONE, FailurePoints);
  }

  FailurePoints = nullptr;

  TRI_DestroyReadWriteLock(&FailurePointsLock);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief appends a backtrace to the string provided
////////////////////////////////////////////////////////////////////////////////

void TRI_GetBacktrace (std::string& btstr) {
#if HAVE_BACKTRACE
#ifdef _WIN32
  void         * stack[100];
  unsigned short frames;
  SYMBOL_INFO  * symbol;
  HANDLE         process;

  process = GetCurrentProcess();

  SymInitialize(process, nullptr, true);

  frames               = CaptureStackBackTrace(0, 100, stack, nullptr);
  symbol               = (SYMBOL_INFO*) calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);

  if (symbol == nullptr) {
    // cannot allocate memory
    return;
  }

  symbol->MaxNameLen   = 255;
  symbol->SizeOfStruct = sizeof(SYMBOL_INFO);

  for (unsigned int i = 0; i < frames; i++) {
    char address[64];
    SymFromAddr(process, (DWORD64) stack[i], 0, symbol);

    snprintf(address, sizeof(address), "0x%0X", (unsigned int) symbol->Address);
    btstr += std::to_string(frames - i - 1) + std::string(": ") + symbol->Name + std::string(" [") + address + std::string("]\n");
  }

  TRI_SystemFree(symbol);

#else
  void* stack_frames[50];
  size_t size, i;
  char** strings;

  size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
  strings = backtrace_symbols(stack_frames, size);
  for (i = 0; i < size; i++) {
    std::stringstream ss;
    if (strings != nullptr) {
      char *mangled_name = nullptr, *offset_begin = nullptr, *offset_end = nullptr;

      // find parantheses and +address offset surrounding mangled name
      for (char *p = strings[i]; *p; ++p) {
        if (*p == '(') {
          mangled_name = p;
        }
        else if (*p == '+') {
          offset_begin = p;
        }
        else if (*p == ')') {
          offset_end = p;
          break;
        }
      }
      // TODO: osx demangling works a little bit different.
      // http://oroboro.com/stack-trace-on-crash/
      // says it should look like that:
      //#ifdef DARWIN
      //      // OSX style stack trace
      //      for ( char *p = symbollist[i]; *p; ++p )
      //      {
      //         if (( *p == '_' ) && ( *(p-1) == ' ' ))
      //            begin_name = p-1;
      //         else if ( *p == '+' )
      //            begin_offset = p-1;
      //      }
      //
      //      if ( begin_name && begin_offset && ( begin_name < begin_offset ))
      //      {
      //         *begin_name++ = '\0';
      //         *begin_offset++ = '\0';
      //
      //         // mangled name is now in [begin_name, begin_offset) and caller
      //         // offset in [begin_offset, end_offset). now apply
      //         // __cxa_demangle():
      //         int status;
      //         char* ret = abi::__cxa_demangle( begin_name, &funcname[0],
      //                                          &funcnamesize, &status );
      //         if ( status == 0 )
      //         {
      //            funcname = ret; // use possibly realloc()-ed string
      //            fprintf( out, "  %-30s %-40s %s\n",
      //                     symbollist[i], funcname, begin_offset );
      //         } else {
      //            // demangling failed. Output function name as a C function with
      //            // no arguments.
      //            fprintf( out, "  %-30s %-38s() %s\n",
      //                     symbollist[i], begin_name, begin_offset );
      //         }
      //
      //#else // !DARWIN - but is posix
      // if the line could be processed, attempt to demangle the symbol
      if (mangled_name && offset_begin && offset_end &&
          mangled_name < offset_begin) {
        *mangled_name++ = '\0';
        *offset_begin++ = '\0';
        *offset_end++ = '\0';
        int status = 0;
        char * demangled_name = abi::__cxa_demangle(mangled_name, 0, 0, &status);

        if (demangled_name != nullptr) {
          if (status == 0) {
            ss << stack_frames[i];
            btstr +=  strings[i] +
              std::string("() [") +
              ss.str() +
              std::string("] ") +
              demangled_name +
              std::string("\n");
          }
          else {
            btstr += strings[i] +
              std::string("\n");
          }
          TRI_SystemFree(demangled_name);
        }
      }
      else {
        btstr += strings[i] +
          std::string("\n");
      }
    }
    else {
      ss << stack_frames[i];
      btstr += ss.str() +
        std::string("\n");
    }
  }
  if (strings != nullptr) {
    TRI_SystemFree(strings);
  }
#endif
#endif
}

////////////////////////////////////////////////////////////////////////////////
/// @brief prints a backtrace on stderr
////////////////////////////////////////////////////////////////////////////////

void TRI_PrintBacktrace () {
#if HAVE_BACKTRACE
  std::string out;
  TRI_GetBacktrace(out);
  fprintf(stderr, "%s", out.c_str());
#endif
}

// -----------------------------------------------------------------------------
// --SECTION--                                                       END-OF-FILE
// -----------------------------------------------------------------------------

// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End:
<commit_msg>use ReadWriteLocker<commit_after>////////////////////////////////////////////////////////////////////////////////
/// @brief debugging helpers
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2014 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
/// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

#include "Basics/Common.h"
#include "Basics/logging.h"
#include "Basics/ReadLocker.h"
#include "Basics/ReadWriteLock.h"
#include "Basics/WriteLocker.h"

#ifdef TRI_ENABLE_MAINTAINER_MODE
#if HAVE_BACKTRACE
#ifdef _WIN32
#include <DbgHelp.h>
#else
#include <execinfo.h>
#include <cxxabi.h>
#endif
#endif
#endif

// -----------------------------------------------------------------------------
// --SECTION--                                                 private variables
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief a global string containing the currently registered failure points
/// the string is a comma-separated list of point names
////////////////////////////////////////////////////////////////////////////////

static char* FailurePoints = nullptr;

////////////////////////////////////////////////////////////////////////////////
/// @brief a read-write lock for thread-safe access to the failure-points list
////////////////////////////////////////////////////////////////////////////////

triagens::basics::ReadWriteLock FailurePointsLock;

#ifdef TRI_ENABLE_FAILURE_TESTS

// -----------------------------------------------------------------------------
// --SECTION--                                                 private functions
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief make a delimited value from a string, so we can unambigiously
/// search for it (e.g. searching for just "foo" would find "foo" and "foobar",
/// so we'll be putting the value inside some delimiter: ",foo,")
////////////////////////////////////////////////////////////////////////////////

static char* MakeValue (char const* value) {
  if (value == nullptr || strlen(value) == 0) {
    return nullptr;
  }

  char* delimited = static_cast<char*>(TRI_Allocate(TRI_CORE_MEM_ZONE, strlen(value) + 3, false));

  if (delimited != nullptr) {
    memcpy(delimited + 1, value, strlen(value));
    delimited[0] = ',';
    delimited[strlen(value) + 1] = ',';
    delimited[strlen(value) + 2] = '\0';
  }

  return delimited;
}

// -----------------------------------------------------------------------------
// --SECTION--                                                  public functions
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief cause a segmentation violation
/// this is used for crash and recovery tests
////////////////////////////////////////////////////////////////////////////////

void TRI_SegfaultDebugging (char const* message) {
  LOG_WARNING("%s: summon Baal!", message);
  // make sure the latest log messages are flushed
  TRI_ShutdownLogging(true);

  // and now crash
#ifndef __APPLE__
  // on MacOS, the following statement makes the server hang but not crash
  *((char*) -1) = '!';
#endif

  // ensure the process is terminated
  abort();
}

////////////////////////////////////////////////////////////////////////////////
/// @brief check whether we should fail at a specific failure point
////////////////////////////////////////////////////////////////////////////////

bool TRI_ShouldFailDebugging (char const* value) {
  char* found = nullptr;

  // try without the lock first (to speed things up)
  if (FailurePoints == nullptr) {
    return false;
  }

  READ_LOCKER(FailurePointsLock);

  if (FailurePoints != nullptr) {
    char* checkValue = MakeValue(value);

    if (checkValue != nullptr) {
      found = strstr(FailurePoints, checkValue);
      TRI_Free(TRI_CORE_MEM_ZONE, checkValue);
    }
  }

  return (found != nullptr);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief add a failure point
////////////////////////////////////////////////////////////////////////////////

void TRI_AddFailurePointDebugging (char const* value) {
  char* found;
  char* checkValue = MakeValue(value);

  if (checkValue == nullptr) {
    return;
  }

  WRITE_LOCKER(FailurePointsLock);

  if (FailurePoints == nullptr) {
    found = nullptr;
  }
  else {
    found = strstr(FailurePoints, checkValue);
  }

  if (found == nullptr) {
    // not yet found. so add it
    char* copy;

    LOG_WARNING("activating intentional failure point '%s'. the server will misbehave!", value);
    size_t n = strlen(checkValue);

    if (FailurePoints == nullptr) {
      copy = static_cast<char*>(TRI_Allocate(TRI_CORE_MEM_ZONE, n + 1, false));

      if (copy == nullptr) {
        TRI_Free(TRI_CORE_MEM_ZONE, checkValue);
        return;
      }

      memcpy(copy, checkValue, n);
      copy[n] = '\0';
    }
    else {
      copy = static_cast<char*>(TRI_Allocate(TRI_CORE_MEM_ZONE, n + strlen(FailurePoints), false));

      if (copy == nullptr) {
        TRI_Free(TRI_CORE_MEM_ZONE, checkValue);
        return;
      }

      memcpy(copy, FailurePoints, strlen(FailurePoints));
      memcpy(copy + strlen(FailurePoints) - 1, checkValue, n);
      copy[strlen(FailurePoints) + n - 1] = '\0';
    }

    FailurePoints = copy;
  }

  TRI_Free(TRI_CORE_MEM_ZONE, checkValue);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief remove a failure points
////////////////////////////////////////////////////////////////////////////////

void TRI_RemoveFailurePointDebugging (char const* value) {
  WRITE_LOCKER(FailurePointsLock);

  if (FailurePoints == nullptr) {
    return;
  }

  char* checkValue = MakeValue(value);

  if (checkValue != nullptr) {
    char* found;
    char* copy;
    size_t n;

    found = strstr(FailurePoints, checkValue);

    if (found == nullptr) {
      TRI_Free(TRI_CORE_MEM_ZONE, checkValue);
      return;
    }

    if (strlen(FailurePoints) - strlen(checkValue) <= 2) {
      TRI_Free(TRI_CORE_MEM_ZONE, FailurePoints);
      FailurePoints = nullptr;

      TRI_Free(TRI_CORE_MEM_ZONE, checkValue);
      return;
    }

    copy = static_cast<char*>(TRI_Allocate(TRI_CORE_MEM_ZONE, strlen(FailurePoints) - strlen(checkValue) + 2, false));

    if (copy == nullptr) {
      TRI_Free(TRI_CORE_MEM_ZONE, checkValue);
      return;
    }

    // copy start of string
    n = found - FailurePoints;
    memcpy(copy, FailurePoints, n);

    // copy remainder of string
    memcpy(copy + n, found + strlen(checkValue) - 1, strlen(FailurePoints) - strlen(checkValue) - n + 1);

    copy[strlen(FailurePoints) - strlen(checkValue) + 1] = '\0';
    TRI_Free(TRI_CORE_MEM_ZONE, FailurePoints);
    FailurePoints = copy;

    TRI_Free(TRI_CORE_MEM_ZONE, checkValue);
  }
}

////////////////////////////////////////////////////////////////////////////////
/// @brief clear all failure points
////////////////////////////////////////////////////////////////////////////////

void TRI_ClearFailurePointsDebugging () {
  WRITE_LOCKER(FailurePointsLock);

  if (FailurePoints != nullptr) {
    TRI_Free(TRI_CORE_MEM_ZONE, FailurePoints);
  }

  FailurePoints = nullptr;
}

#endif

////////////////////////////////////////////////////////////////////////////////
/// @brief initialise the debugging
////////////////////////////////////////////////////////////////////////////////

void TRI_InitialiseDebugging () {
}

////////////////////////////////////////////////////////////////////////////////
/// @brief shutdown the debugging
////////////////////////////////////////////////////////////////////////////////

void TRI_ShutdownDebugging () {
  if (FailurePoints != nullptr) {
    TRI_Free(TRI_CORE_MEM_ZONE, FailurePoints);
  }

  FailurePoints = nullptr;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief appends a backtrace to the string provided
////////////////////////////////////////////////////////////////////////////////

void TRI_GetBacktrace (std::string& btstr) {
#if HAVE_BACKTRACE
#ifdef _WIN32
  void         * stack[100];
  unsigned short frames;
  SYMBOL_INFO  * symbol;
  HANDLE         process;

  process = GetCurrentProcess();

  SymInitialize(process, nullptr, true);

  frames               = CaptureStackBackTrace(0, 100, stack, nullptr);
  symbol               = (SYMBOL_INFO*) calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);

  if (symbol == nullptr) {
    // cannot allocate memory
    return;
  }

  symbol->MaxNameLen   = 255;
  symbol->SizeOfStruct = sizeof(SYMBOL_INFO);

  for (unsigned int i = 0; i < frames; i++) {
    char address[64];
    SymFromAddr(process, (DWORD64) stack[i], 0, symbol);

    snprintf(address, sizeof(address), "0x%0X", (unsigned int) symbol->Address);
    btstr += std::to_string(frames - i - 1) + std::string(": ") + symbol->Name + std::string(" [") + address + std::string("]\n");
  }

  TRI_SystemFree(symbol);

#else
  void* stack_frames[50];
  size_t size, i;
  char** strings;

  size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
  strings = backtrace_symbols(stack_frames, size);
  for (i = 0; i < size; i++) {
    std::stringstream ss;
    if (strings != nullptr) {
      char *mangled_name = nullptr, *offset_begin = nullptr, *offset_end = nullptr;

      // find parantheses and +address offset surrounding mangled name
      for (char *p = strings[i]; *p; ++p) {
        if (*p == '(') {
          mangled_name = p;
        }
        else if (*p == '+') {
          offset_begin = p;
        }
        else if (*p == ')') {
          offset_end = p;
          break;
        }
      }
      // TODO: osx demangling works a little bit different.
      // http://oroboro.com/stack-trace-on-crash/
      // says it should look like that:
      //#ifdef DARWIN
      //      // OSX style stack trace
      //      for ( char *p = symbollist[i]; *p; ++p )
      //      {
      //         if (( *p == '_' ) && ( *(p-1) == ' ' ))
      //            begin_name = p-1;
      //         else if ( *p == '+' )
      //            begin_offset = p-1;
      //      }
      //
      //      if ( begin_name && begin_offset && ( begin_name < begin_offset ))
      //      {
      //         *begin_name++ = '\0';
      //         *begin_offset++ = '\0';
      //
      //         // mangled name is now in [begin_name, begin_offset) and caller
      //         // offset in [begin_offset, end_offset). now apply
      //         // __cxa_demangle():
      //         int status;
      //         char* ret = abi::__cxa_demangle( begin_name, &funcname[0],
      //                                          &funcnamesize, &status );
      //         if ( status == 0 )
      //         {
      //            funcname = ret; // use possibly realloc()-ed string
      //            fprintf( out, "  %-30s %-40s %s\n",
      //                     symbollist[i], funcname, begin_offset );
      //         } else {
      //            // demangling failed. Output function name as a C function with
      //            // no arguments.
      //            fprintf( out, "  %-30s %-38s() %s\n",
      //                     symbollist[i], begin_name, begin_offset );
      //         }
      //
      //#else // !DARWIN - but is posix
      // if the line could be processed, attempt to demangle the symbol
      if (mangled_name && offset_begin && offset_end &&
          mangled_name < offset_begin) {
        *mangled_name++ = '\0';
        *offset_begin++ = '\0';
        *offset_end++ = '\0';
        int status = 0;
        char * demangled_name = abi::__cxa_demangle(mangled_name, 0, 0, &status);

        if (demangled_name != nullptr) {
          if (status == 0) {
            ss << stack_frames[i];
            btstr +=  strings[i] +
              std::string("() [") +
              ss.str() +
              std::string("] ") +
              demangled_name +
              std::string("\n");
          }
          else {
            btstr += strings[i] +
              std::string("\n");
          }
          TRI_SystemFree(demangled_name);
        }
      }
      else {
        btstr += strings[i] +
          std::string("\n");
      }
    }
    else {
      ss << stack_frames[i];
      btstr += ss.str() +
        std::string("\n");
    }
  }
  if (strings != nullptr) {
    TRI_SystemFree(strings);
  }
#endif
#endif
}

////////////////////////////////////////////////////////////////////////////////
/// @brief prints a backtrace on stderr
////////////////////////////////////////////////////////////////////////////////

void TRI_PrintBacktrace () {
#if HAVE_BACKTRACE
  std::string out;
  TRI_GetBacktrace(out);
  fprintf(stderr, "%s", out.c_str());
#endif
}

// -----------------------------------------------------------------------------
// --SECTION--                                                       END-OF-FILE
// -----------------------------------------------------------------------------

// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End:
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Monteverdi2
  Language:  C++

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See Copyright.txt for details.

  Monteverdi2 is distributed under the CeCILL licence version 2. See
  Licence_CeCILL_V2-en.txt or
  http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt for more details.

  This software is distributed WITHOUT ANY WARRANTY; without even
  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#include "mvdI18nApplication.h"

/*****************************************************************************/
/* INCLUDE SECTION                                                           */

//
// Qt includes (sorted by alphabetic order)
//// Must be included before system/custom includes.

//
// System includes (sorted by alphabetic order)

//
// OTB includes (sorted by alphabetic order)
#include "otbDEMHandler.h"

//
// Monteverdi includes (sorted by alphabetic order)
#include "mvdAlgorithm.h"
#include "mvdDatasetModel.h"
#include "mvdSystemError.h"
#include "mvdVectorImageModel.h"

namespace mvd
{
/*
  TRANSLATOR mvd::I18nApplication

  Necessary for lupdate to be aware of C++ namespaces.

  Context comment for translator.
*/

/*****************************************************************************/
/* CONSTANTS                                                                 */

const char* I18nApplication::DEFAULT_CACHE_DIR_NAME = "mvd2";

const char* I18nApplication::DEFAULT_CACHE_RESULT_DIR_NAME = "result";

const char* I18nApplication::DATASET_EXT = ".ds";

/*****************************************************************************/
/* STATIC IMPLEMENTATION SECTION                                             */

/*******************************************************************************/
bool
I18nApplication
::IsCacheDirValid( const QString& path )
{
  QDir dir( path );
  QFileInfo fileInfo( path );

  return
    fileInfo.exists() &&
    fileInfo.isDir() &&
    fileInfo.isReadable() &&
    fileInfo.isWritable() &&
    dir.dirName()==I18nApplication::DEFAULT_CACHE_DIR_NAME;
}

/*******************************************************************************/
bool
I18nApplication
::MakeDirTree( const QString& path, const QString& tree, QDir* dir )
{
  QDir pathDir( path );

  /*
  qDebug() << path;
  qDebug() << pathDir.currentPath();
  qDebug() << pathDir.path();
  */

  if( !pathDir.exists() )
    throw SystemError( ToStdString( QString( "('%1')" ).arg( path ) ) );

  QDir treeDir( pathDir.filePath( tree ) );
  if( treeDir.exists() )
    return false;

  if( !pathDir.mkpath( tree ) )
    throw SystemError(
      ToStdString(
	QString( "('%1')" ).arg( pathDir.filePath( tree ) )
      )
    );

  if( !pathDir.cd( tree ) )
    throw SystemError(
      ToStdString(
	QString( "('%1')" ).arg(
	  pathDir.filePath( tree )
	)
      )
    );

  if( dir!=NULL )
    {
    *dir = pathDir;
    }

  return true;
}

/*****************************************************************************/
void
I18nApplication
::DatasetPathName( QString& path,
		   QString& name,
		   const QString& imageFilename )
{
  // convenient QFileInfo
  QFileInfo fileInfo( imageFilename );

  // Dataset is stored into application cache-directory.
  // E.g. '$HOME/<CACHE_DIR>'
  path = I18nApplication::Instance()->GetCacheDir().path();

  // get the md5 of the filename
  QByteArray result = QCryptographicHash::hash(fileInfo.absoluteFilePath().toAscii(),
                                               QCryptographicHash::Md5);

  // store the md5 + the dataset extension at the end
  name = result.toHex() + I18nApplication::DATASET_EXT;
}

/*****************************************************************************/
DatasetModel*
I18nApplication
::LoadDatasetModel( const QString& imageFilename,
		    int width,
		    int height )
{
  // New model.
  DatasetModel* model = new DatasetModel();

  // Retrive path and name.
  QString path;
  QString name;

  I18nApplication::DatasetPathName( path, name, imageFilename );
  qDebug() << "Dataset path: " << path;
  qDebug() << "Dataset name: " << name;

  // Setup QObject
  model->setObjectName( QDir( path ).filePath( name ) );

  try
    {
    // try if the filename is valid
    VectorImageModel::EnsureValidImage(imageFilename);

    // get the basename from the filename to be used as an Alias
    QFileInfo finfo( imageFilename );

    // Build model (relink to cached data).
    DatasetModel::BuildContext context( path, name, finfo.baseName(), width, height );
    model->BuildModel( &context );

    // Load image if DatasetModel is empty.
    if( !model->HasSelectedImageModel() )
      {
      // Import image from filename given (w; h) size to choose
      // best-fit resolution.
      model->ImportImage( imageFilename, width, height );
      }
    }

  catch( std::exception& exc )
    {
    delete model;
    model = NULL;

    throw;
    }

  return model;
}

/*****************************************************************************/
/* CLASS IMPLEMENTATION SECTION                                              */

/*******************************************************************************/
I18nApplication
::I18nApplication( int& argc, char** argv ) :
  QApplication( argc, argv ),
  m_CacheDir(),
  m_Settings( NULL ),
  m_Model( NULL ),
  m_IsRunningFromBuildDir( false )
{
}

/*******************************************************************************/
I18nApplication
::~I18nApplication()
{
}

/*******************************************************************************/
void
I18nApplication
::Initialize()
{
  // Initialize internationlization.
  InitializeLocale();

  //
  // Force numeric options of locale to "C"
  // See issue #635
  //
  // TODO: Move into I18nApplication.
  setlocale( LC_NUMERIC, "C" );

  // Initialize QCoreApplication.
  virtual_InitializeCore();

  // Initialize settings.
  InitializeSettings();

  // Elevation setup
  ElevationSetup();
}

/*******************************************************************************/
void
I18nApplication
::SetModel( AbstractModel* model )
{
  emit AboutToChangeModel( model );

  delete m_Model;

  m_Model = model;

  if( model!=NULL )
    m_Model->setParent( this );

  emit ModelChanged( m_Model );
}

/*******************************************************************************/
bool
I18nApplication
::MakeCacheDir( const QString& path )
{
  qDebug() << this << "::MakeCacheDir(" << path << ")";

  //
  // Check path.
  QDir homeDir( path );

  if (!homeDir.exists())
    SystemError( ToStdString( QString( "('%1')" ).arg( homeDir.path() ) ) );

  //
  // Create cache-dir.
  bool isNew = I18nApplication::MakeDirTree(
    homeDir.path(),
    I18nApplication::DEFAULT_CACHE_DIR_NAME,
    &m_CacheDir
  );

  //
  // Remember cache-dir.
  StoreSettingsKey( "cacheDir", QDir::cleanPath( m_CacheDir.path() ) );

  //
  // Construct result-dir path.
  I18nApplication::MakeDirTree(
    m_CacheDir.path(),
    I18nApplication::DEFAULT_CACHE_RESULT_DIR_NAME,
    &m_ResultsDir
  );

  //
  // Result.
  return isNew;
}

void
I18nApplication
::ElevationSetup()
{
  QSettings settings;

  otb::DEMHandler::Pointer demHandlerInstance = otb::DEMHandler::Instance();

  if(settings.contains("geoidPathActive") && settings.value("geoidPathActive").toBool())
    {
    qDebug() << "Settings/GeoidFile:" <<settings.value("geoidPath").toString() ;
    try
      {
      demHandlerInstance->OpenGeoidFile(settings.value("geoidPath").toString().toStdString());
      }
    catch(itk::ExceptionObject & err)
      {
      qDebug() <<tr("An error occured while loading the geoid file, no geoid file will be used.");
      qDebug()<<tr("Error: ")<<err.what();
      }
    }
  if(settings.contains("srtmDirActive") && settings.value("srtmDirActive").toBool())
    {
    qDebug() << "Settings/DEMDir:" <<settings.value("srtmDir").toString();
    try
      {
      demHandlerInstance->OpenDEMDirectory(settings.value("srtmDir").toString().toStdString());
      }
    catch(itk::ExceptionObject & err)
      {
      qDebug() <<tr("An error occured while loading the DEM directory, no DEM will be used.");
      qDebug()<<tr("Error: ") << err.what();
      }
    }
}

/*******************************************************************************/
void
I18nApplication
::InitializeLocale()
{
  QTextCodec::setCodecForTr( QTextCodec::codecForName( "utf8" ) );
  QTextCodec::setCodecForLocale( QTextCodec::codecForName("utf8") );
  QTextCodec::setCodecForCStrings( QTextCodec::codecForName("utf8") );

  //
  // 1. default UI language is english (no translation).
  QLocale sys_lc( QLocale::system() );
  if( sys_lc.language() == QLocale::C ||
      ( sys_lc.language() == QLocale::English &&
        sys_lc.country() == QLocale::UnitedStates ) )
    {
    return;
    }

  //
  // 2. Choose i18n path between build dir and install dir.

  // Begin from the executable path
  QDir bin_dir( QDir::cleanPath(QCoreApplication::applicationDirPath()) );
  qDebug() << tr( "Executable dir : %1" ).arg( bin_dir.path() );

  // Go up in the directory hierarchy until we have a candidate install prefix
  bool prefixFound = false;
  QDir prefix( bin_dir );
  while ( prefix.cdUp() )
    {
    if ( QDir(prefix).cd(Monteverdi2_INSTALL_BIN_DIR) )
      {
      prefixFound = true;
      break;
      }
    }

  if (prefixFound)
    {
    qDebug() << tr( "Candidate install prefix found : %1" ).arg( prefix.path() );
    }
  else
    {
    QString message( tr( "Unable to locate translation files" ) );
    qDebug() << message;
    QMessageBox::critical( NULL, tr( "Critical error" ), message );
    return;
    }

  QDir i18n_dir;

  // At this point the candidate install prefix can also be the build dir root
  if ( prefix.exists( Monteverdi2_CONFIGURE_FILE )
       && prefix.exists("i18n") )
    {
    m_IsRunningFromBuildDir = true;

    // Report found build dir root
    qDebug() << tr( "Running from build directory '%1'." ).arg( prefix.path() );

    // Go into the i18n dir (it exists we just checked for it)
    i18n_dir = prefix;
    i18n_dir.cd("i18n");

    qDebug() << tr( "Using translation dir '%1'." ).arg( i18n_dir.path() );
    }
  else
    {
    m_IsRunningFromBuildDir = false;

    // Report found install prefix
    qDebug() << tr( "Running from install directory '%1'." ).arg( prefix.path() );

    // Go into the i18n dir configured at cmake-time
    i18n_dir = prefix;

    if (i18n_dir.cd(Monteverdi2_INSTALL_DATA_I18N_DIR))
      {
      qDebug() << tr( "Using translation dir '%1'." ).arg( i18n_dir.path() );
      }
    else
      {
      QString message( tr( "Failed to access translation-files directory '%1'" )
                       .arg(QDir::cleanPath(prefix.path()
                             + QDir::separator()
                             + Monteverdi2_INSTALL_DATA_I18N_DIR)) );
      qDebug() << message;
      QMessageBox::critical( NULL, tr( "Critical error" ), message );
      return;
      }
    }

  //
  // 3.1 Stack Qt translator.
  LoadAndInstallTranslator(
    "qt_" + sys_lc.name(),
    QLibraryInfo::location( QLibraryInfo::TranslationsPath  )
  );

  //
  // 3.2 Stack Monteverdi2 translator as prioritary over Qt translator.
  LoadAndInstallTranslator( sys_lc.name(), i18n_dir.path() );

  // TODO: Record locale translation filename(s) used in UI component (e.g.
  // AboutDialog, Settings dialog, Information dialog etc.)
}

/*******************************************************************************/
void
I18nApplication
::InitializeCore( const QString& appName,
		  const QString& appVersion,
		  const QString& orgName,
		  const QString& orgDomain )
{
  setObjectName( "Application" );

  //
  // Setup application tags.
  //
  QCoreApplication::setApplicationName(
    appName
  );
  QCoreApplication::setApplicationVersion(
    appVersion
  );

  //
  // Setup organization tags.
  //
  QCoreApplication::setOrganizationName( orgName );
  QCoreApplication::setOrganizationDomain( orgDomain );

#ifndef Q_WS_MAC
    setWindowIcon( QIcon( QLatin1String( ":/images/application_icon" ) ) );
#endif
}

/*******************************************************************************/
void
I18nApplication
::InitializeSettings()
{
  //
  // Create settings proxy.
  m_Settings = new QSettings(
    // TODO: Change QSettings::NativeFormat by QSettings::IniFormat.
    QSettings::NativeFormat,
    QSettings::UserScope,
    QCoreApplication::organizationName(),
    QCoreApplication::applicationName(),
    this
  );

  //
  // Restore cache directory.
  QVariant value( RetrieveSettingsKey( "cacheDir" ) );

  if( !value.isNull() )
    {
    QString path( value.toString() );

    qDebug() << "Settings/cacheDir:" << path;

    if( I18nApplication::IsCacheDirValid( path ) )
      {
      m_CacheDir.setPath( path );
      }
    }
}

/*******************************************************************************/
bool
I18nApplication
::LoadAndInstallTranslator(const QString& filename,
                           const QString& directory,
                           const QString& searchDelimiters,
                           const QString& suffix )
{
  QString filename_ext(
    filename +
    ( suffix.isNull()
      ? ".qm"
      : suffix )
  );

  // (a) Do need to new QTranslator() here!
  QTranslator* lc_translator = new QTranslator( this );

  if( !lc_translator->load( filename, directory, searchDelimiters, suffix ) )
    {
    QString message(
      tr( "Failed to load '%1' translation file from '%2'." )
      .arg( filename_ext )
      .arg( directory )
    );

    // TODO: Use log system to trace error while loading locale translation file.

    qWarning() << message;

    // TODO: morph into better HMI design.
#if defined( _DEBUG )
    QMessageBox::warning( NULL, tr( "Warning!" ), message );
#endif

    return false;
    }

  // (a) ...because QTranslator needs to be alive during the whole
  // lifespan of the application.
  QCoreApplication::installTranslator( lc_translator );

  QString message(
    tr( "Successfully loaded '%1' translation file from '%2'." )
    .arg( filename_ext )
    .arg( directory )
  );

  // TODO: Log locale translation filename used.

  qDebug() << message;

  return true;
}

/*******************************************************************************/
/* SLOTS                                                                       */
/*******************************************************************************/

} // end namespace 'mvd'
<commit_msg>ENH: store resultsDir in settings<commit_after>/*=========================================================================

  Program:   Monteverdi2
  Language:  C++

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See Copyright.txt for details.

  Monteverdi2 is distributed under the CeCILL licence version 2. See
  Licence_CeCILL_V2-en.txt or
  http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt for more details.

  This software is distributed WITHOUT ANY WARRANTY; without even
  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#include "mvdI18nApplication.h"

/*****************************************************************************/
/* INCLUDE SECTION                                                           */

//
// Qt includes (sorted by alphabetic order)
//// Must be included before system/custom includes.

//
// System includes (sorted by alphabetic order)

//
// OTB includes (sorted by alphabetic order)
#include "otbDEMHandler.h"

//
// Monteverdi includes (sorted by alphabetic order)
#include "mvdAlgorithm.h"
#include "mvdDatasetModel.h"
#include "mvdSystemError.h"
#include "mvdVectorImageModel.h"

namespace mvd
{
/*
  TRANSLATOR mvd::I18nApplication

  Necessary for lupdate to be aware of C++ namespaces.

  Context comment for translator.
*/

/*****************************************************************************/
/* CONSTANTS                                                                 */

const char* I18nApplication::DEFAULT_CACHE_DIR_NAME = "mvd2";

const char* I18nApplication::DEFAULT_CACHE_RESULT_DIR_NAME = "result";

const char* I18nApplication::DATASET_EXT = ".ds";

/*****************************************************************************/
/* STATIC IMPLEMENTATION SECTION                                             */

/*******************************************************************************/
bool
I18nApplication
::IsCacheDirValid( const QString& path )
{
  QDir dir( path );
  QFileInfo fileInfo( path );

  return
    fileInfo.exists() &&
    fileInfo.isDir() &&
    fileInfo.isReadable() &&
    fileInfo.isWritable() &&
    dir.dirName()==I18nApplication::DEFAULT_CACHE_DIR_NAME;
}

/*******************************************************************************/
bool
I18nApplication
::MakeDirTree( const QString& path, const QString& tree, QDir* dir )
{
  QDir pathDir( path );

  /*
  qDebug() << path;
  qDebug() << pathDir.currentPath();
  qDebug() << pathDir.path();
  */

  if( !pathDir.exists() )
    throw SystemError( ToStdString( QString( "('%1')" ).arg( path ) ) );

  QDir treeDir( pathDir.filePath( tree ) );
  if( treeDir.exists() )
    return false;

  if( !pathDir.mkpath( tree ) )
    throw SystemError(
      ToStdString(
	QString( "('%1')" ).arg( pathDir.filePath( tree ) )
      )
    );

  if( !pathDir.cd( tree ) )
    throw SystemError(
      ToStdString(
	QString( "('%1')" ).arg(
	  pathDir.filePath( tree )
	)
      )
    );

  if( dir!=NULL )
    {
    *dir = pathDir;
    }

  return true;
}

/*****************************************************************************/
void
I18nApplication
::DatasetPathName( QString& path,
		   QString& name,
		   const QString& imageFilename )
{
  // convenient QFileInfo
  QFileInfo fileInfo( imageFilename );

  // Dataset is stored into application cache-directory.
  // E.g. '$HOME/<CACHE_DIR>'
  path = I18nApplication::Instance()->GetCacheDir().path();

  // get the md5 of the filename
  QByteArray result = QCryptographicHash::hash(fileInfo.absoluteFilePath().toAscii(),
                                               QCryptographicHash::Md5);

  // store the md5 + the dataset extension at the end
  name = result.toHex() + I18nApplication::DATASET_EXT;
}

/*****************************************************************************/
DatasetModel*
I18nApplication
::LoadDatasetModel( const QString& imageFilename,
		    int width,
		    int height )
{
  // New model.
  DatasetModel* model = new DatasetModel();

  // Retrive path and name.
  QString path;
  QString name;

  I18nApplication::DatasetPathName( path, name, imageFilename );
  qDebug() << "Dataset path: " << path;
  qDebug() << "Dataset name: " << name;

  // Setup QObject
  model->setObjectName( QDir( path ).filePath( name ) );

  try
    {
    // try if the filename is valid
    VectorImageModel::EnsureValidImage(imageFilename);

    // get the basename from the filename to be used as an Alias
    QFileInfo finfo( imageFilename );

    // Build model (relink to cached data).
    DatasetModel::BuildContext context( path, name, finfo.baseName(), width, height );
    model->BuildModel( &context );

    // Load image if DatasetModel is empty.
    if( !model->HasSelectedImageModel() )
      {
      // Import image from filename given (w; h) size to choose
      // best-fit resolution.
      model->ImportImage( imageFilename, width, height );
      }
    }

  catch( std::exception& exc )
    {
    delete model;
    model = NULL;

    throw;
    }

  return model;
}

/*****************************************************************************/
/* CLASS IMPLEMENTATION SECTION                                              */

/*******************************************************************************/
I18nApplication
::I18nApplication( int& argc, char** argv ) :
  QApplication( argc, argv ),
  m_CacheDir(),
  m_Settings( NULL ),
  m_Model( NULL ),
  m_IsRunningFromBuildDir( false )
{
}

/*******************************************************************************/
I18nApplication
::~I18nApplication()
{
}

/*******************************************************************************/
void
I18nApplication
::Initialize()
{
  // Initialize internationlization.
  InitializeLocale();

  //
  // Force numeric options of locale to "C"
  // See issue #635
  //
  // TODO: Move into I18nApplication.
  setlocale( LC_NUMERIC, "C" );

  // Initialize QCoreApplication.
  virtual_InitializeCore();

  // Initialize settings.
  InitializeSettings();

  // Elevation setup
  ElevationSetup();
}

/*******************************************************************************/
void
I18nApplication
::SetModel( AbstractModel* model )
{
  emit AboutToChangeModel( model );

  delete m_Model;

  m_Model = model;

  if( model!=NULL )
    m_Model->setParent( this );

  emit ModelChanged( m_Model );
}

/*******************************************************************************/
bool
I18nApplication
::MakeCacheDir( const QString& path )
{
  qDebug() << this << "::MakeCacheDir(" << path << ")";

  //
  // Check path.
  QDir homeDir( path );

  if (!homeDir.exists())
    SystemError( ToStdString( QString( "('%1')" ).arg( homeDir.path() ) ) );

  //
  // Create cache-dir.
  bool isNew = I18nApplication::MakeDirTree(
    homeDir.path(),
    I18nApplication::DEFAULT_CACHE_DIR_NAME,
    &m_CacheDir
  );

  //
  // Remember cache-dir.
  StoreSettingsKey( "cacheDir", QDir::cleanPath( m_CacheDir.path() ) );

  //
  // Construct result-dir path.
  I18nApplication::MakeDirTree(
    m_CacheDir.path(),
    I18nApplication::DEFAULT_CACHE_RESULT_DIR_NAME,
    &m_ResultsDir
  );

  //
  // Remember result-dir
  StoreSettingsKey( "resultDir", QDir::cleanPath( m_ResultsDir.path() ) );

  //
  // Result.
  return isNew;
}

void
I18nApplication
::ElevationSetup()
{
  QSettings settings;

  otb::DEMHandler::Pointer demHandlerInstance = otb::DEMHandler::Instance();

  if(settings.contains("geoidPathActive") && settings.value("geoidPathActive").toBool())
    {
    qDebug() << "Settings/GeoidFile:" <<settings.value("geoidPath").toString() ;
    try
      {
      demHandlerInstance->OpenGeoidFile(settings.value("geoidPath").toString().toStdString());
      }
    catch(itk::ExceptionObject & err)
      {
      qDebug() <<tr("An error occured while loading the geoid file, no geoid file will be used.");
      qDebug()<<tr("Error: ")<<err.what();
      }
    }
  if(settings.contains("srtmDirActive") && settings.value("srtmDirActive").toBool())
    {
    qDebug() << "Settings/DEMDir:" <<settings.value("srtmDir").toString();
    try
      {
      demHandlerInstance->OpenDEMDirectory(settings.value("srtmDir").toString().toStdString());
      }
    catch(itk::ExceptionObject & err)
      {
      qDebug() <<tr("An error occured while loading the DEM directory, no DEM will be used.");
      qDebug()<<tr("Error: ") << err.what();
      }
    }
}

/*******************************************************************************/
void
I18nApplication
::InitializeLocale()
{
  QTextCodec::setCodecForTr( QTextCodec::codecForName( "utf8" ) );
  QTextCodec::setCodecForLocale( QTextCodec::codecForName("utf8") );
  QTextCodec::setCodecForCStrings( QTextCodec::codecForName("utf8") );

  //
  // 1. default UI language is english (no translation).
  QLocale sys_lc( QLocale::system() );
  if( sys_lc.language() == QLocale::C ||
      ( sys_lc.language() == QLocale::English &&
        sys_lc.country() == QLocale::UnitedStates ) )
    {
    return;
    }

  //
  // 2. Choose i18n path between build dir and install dir.

  // Begin from the executable path
  QDir bin_dir( QDir::cleanPath(QCoreApplication::applicationDirPath()) );
  qDebug() << tr( "Executable dir : %1" ).arg( bin_dir.path() );

  // Go up in the directory hierarchy until we have a candidate install prefix
  bool prefixFound = false;
  QDir prefix( bin_dir );
  while ( prefix.cdUp() )
    {
    if ( QDir(prefix).cd(Monteverdi2_INSTALL_BIN_DIR) )
      {
      prefixFound = true;
      break;
      }
    }

  if (prefixFound)
    {
    qDebug() << tr( "Candidate install prefix found : %1" ).arg( prefix.path() );
    }
  else
    {
    QString message( tr( "Unable to locate translation files" ) );
    qDebug() << message;
    QMessageBox::critical( NULL, tr( "Critical error" ), message );
    return;
    }

  QDir i18n_dir;

  // At this point the candidate install prefix can also be the build dir root
  if ( prefix.exists( Monteverdi2_CONFIGURE_FILE )
       && prefix.exists("i18n") )
    {
    m_IsRunningFromBuildDir = true;

    // Report found build dir root
    qDebug() << tr( "Running from build directory '%1'." ).arg( prefix.path() );

    // Go into the i18n dir (it exists we just checked for it)
    i18n_dir = prefix;
    i18n_dir.cd("i18n");

    qDebug() << tr( "Using translation dir '%1'." ).arg( i18n_dir.path() );
    }
  else
    {
    m_IsRunningFromBuildDir = false;

    // Report found install prefix
    qDebug() << tr( "Running from install directory '%1'." ).arg( prefix.path() );

    // Go into the i18n dir configured at cmake-time
    i18n_dir = prefix;

    if (i18n_dir.cd(Monteverdi2_INSTALL_DATA_I18N_DIR))
      {
      qDebug() << tr( "Using translation dir '%1'." ).arg( i18n_dir.path() );
      }
    else
      {
      QString message( tr( "Failed to access translation-files directory '%1'" )
                       .arg(QDir::cleanPath(prefix.path()
                             + QDir::separator()
                             + Monteverdi2_INSTALL_DATA_I18N_DIR)) );
      qDebug() << message;
      QMessageBox::critical( NULL, tr( "Critical error" ), message );
      return;
      }
    }

  //
  // 3.1 Stack Qt translator.
  LoadAndInstallTranslator(
    "qt_" + sys_lc.name(),
    QLibraryInfo::location( QLibraryInfo::TranslationsPath  )
  );

  //
  // 3.2 Stack Monteverdi2 translator as prioritary over Qt translator.
  LoadAndInstallTranslator( sys_lc.name(), i18n_dir.path() );

  // TODO: Record locale translation filename(s) used in UI component (e.g.
  // AboutDialog, Settings dialog, Information dialog etc.)
}

/*******************************************************************************/
void
I18nApplication
::InitializeCore( const QString& appName,
		  const QString& appVersion,
		  const QString& orgName,
		  const QString& orgDomain )
{
  setObjectName( "Application" );

  //
  // Setup application tags.
  //
  QCoreApplication::setApplicationName(
    appName
  );
  QCoreApplication::setApplicationVersion(
    appVersion
  );

  //
  // Setup organization tags.
  //
  QCoreApplication::setOrganizationName( orgName );
  QCoreApplication::setOrganizationDomain( orgDomain );

#ifndef Q_WS_MAC
    setWindowIcon( QIcon( QLatin1String( ":/images/application_icon" ) ) );
#endif
}

/*******************************************************************************/
void
I18nApplication
::InitializeSettings()
{
  //
  // Create settings proxy.
  m_Settings = new QSettings(
    // TODO: Change QSettings::NativeFormat by QSettings::IniFormat.
    QSettings::NativeFormat,
    QSettings::UserScope,
    QCoreApplication::organizationName(),
    QCoreApplication::applicationName(),
    this
  );

  //
  // Restore cache directory.
  QVariant value( RetrieveSettingsKey( "cacheDir" ) );

  if( !value.isNull() )
    {
    QString path( value.toString() );

    qDebug() << "Settings/cacheDir:" << path;

    if( I18nApplication::IsCacheDirValid( path ) )
      {
      m_CacheDir.setPath( path );
      }
    }

  //
  // Restore result directory.
  QVariant resultDir( RetrieveSettingsKey( "resultDir" ) );

  if( !resultDir.isNull() )
    {
    QString resultPath( resultDir.toString() );

    qDebug() << "Settings/cacheDir:" << resultPath;

    if( I18nApplication::IsCacheDirValid(resultDir  ) )
      {
      m_ResultsDir.setPath( path );
      }
    }
}

/*******************************************************************************/
bool
I18nApplication
::LoadAndInstallTranslator(const QString& filename,
                           const QString& directory,
                           const QString& searchDelimiters,
                           const QString& suffix )
{
  QString filename_ext(
    filename +
    ( suffix.isNull()
      ? ".qm"
      : suffix )
  );

  // (a) Do need to new QTranslator() here!
  QTranslator* lc_translator = new QTranslator( this );

  if( !lc_translator->load( filename, directory, searchDelimiters, suffix ) )
    {
    QString message(
      tr( "Failed to load '%1' translation file from '%2'." )
      .arg( filename_ext )
      .arg( directory )
    );

    // TODO: Use log system to trace error while loading locale translation file.

    qWarning() << message;

    // TODO: morph into better HMI design.
#if defined( _DEBUG )
    QMessageBox::warning( NULL, tr( "Warning!" ), message );
#endif

    return false;
    }

  // (a) ...because QTranslator needs to be alive during the whole
  // lifespan of the application.
  QCoreApplication::installTranslator( lc_translator );

  QString message(
    tr( "Successfully loaded '%1' translation file from '%2'." )
    .arg( filename_ext )
    .arg( directory )
  );

  // TODO: Log locale translation filename used.

  qDebug() << message;

  return true;
}

/*******************************************************************************/
/* SLOTS                                                                       */
/*******************************************************************************/

} // end namespace 'mvd'
<|endoftext|>


<commit_before>/*=========================================================================

Program:   Medical Imaging & Interaction Toolkit
Module:    $RCSfile$
Language:  C++
Date:      $Date$
Version:   $Revision$

Copyright (c) German Cancer Research Center, Division of Medical and
Biological Informatics. All rights reserved.
See MITKCopyright.txt or http://www.mitk.org/copyright.html for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#include "mitkMaskImageFilter.h"
#include "mitkImageTimeSelector.h"
#include "mitkTimeHelper.h"
#include "mitkProperties.h"

#include "mitkImageToItk.h"

#include "itkImageRegionConstIterator.h"
#include "itkImageRegionIteratorWithIndex.h"

mitk::MaskImageFilter::MaskImageFilter() : m_Mask(NULL)
{
  this->SetNumberOfInputs(2);
  this->SetNumberOfRequiredInputs(2);
  m_InputTimeSelector  = mitk::ImageTimeSelector::New();
  m_MaskTimeSelector   = mitk::ImageTimeSelector::New();
  m_OutputTimeSelector = mitk::ImageTimeSelector::New();
}

mitk::MaskImageFilter::~MaskImageFilter()
{

}

void mitk::MaskImageFilter::SetMask( const mitk::Image* mask )
{
	// Process object is not const-correct so the const_cast is required here
  m_Mask = const_cast< mitk::Image * >( mask );
	this->ProcessObject::SetNthInput(1, m_Mask );
}

const mitk::Image* mitk::MaskImageFilter::GetMask() const
{
  return m_Mask;
}

void mitk::MaskImageFilter::GenerateInputRequestedRegion()
{
  Superclass::GenerateInputRequestedRegion();

  mitk::Image* output = this->GetOutput();
  mitk::Image* input = const_cast< mitk::Image * > ( this->GetInput() );
  mitk::Image* mask  = m_Mask ;
  if((output->IsInitialized()==false) || (mask == NULL) || (mask->GetTimeSlicedGeometry()->GetTimeSteps() == 0))
    return;

  input->SetRequestedRegionToLargestPossibleRegion();
  mask->SetRequestedRegionToLargestPossibleRegion();

  GenerateTimeInInputRegion(output, input);
  GenerateTimeInInputRegion(output, mask);
}

void mitk::MaskImageFilter::GenerateOutputInformation()
{
  mitk::Image::ConstPointer input = this->GetInput();
  mitk::Image::Pointer output = this->GetOutput();

  if ((output->IsInitialized()) && (this->GetMTime() <= m_TimeOfHeaderInitialization.GetMTime()))
    return;

  itkDebugMacro(<<"GenerateOutputInformation()");

  unsigned int i;
  unsigned int *tmpDimensions = new unsigned int[input->GetDimension()];

  for(i=0;i<input->GetDimension();++i)
    tmpDimensions[i]=input->GetDimension(i);

  output->Initialize(input->GetPixelType(),
    input->GetDimension(),
    tmpDimensions,
    input->GetNumberOfChannels());

  delete [] tmpDimensions;

  output->SetGeometry(static_cast<mitk::Geometry3D*>(input->GetGeometry()->Clone().GetPointer()));

  output->SetPropertyList(input->GetPropertyList()->Clone());

  m_TimeOfHeaderInitialization.Modified();
}

template < typename TPixel, unsigned int VImageDimension >
void mitk::_InternalComputeMask(itk::Image<TPixel, VImageDimension>* inputItkImage, mitk::MaskImageFilter* MaskImageFilter)
{
  typedef itk::Image<TPixel, VImageDimension> ItkInputImageType;

  typedef itk::Image<unsigned char, VImageDimension> ItkMaskImageType;
  typedef itk::Image<TPixel, VImageDimension> ItkOutputImageType;

  typedef itk::ImageRegionConstIterator< ItkInputImageType > ItkInputImageIteratorType;
  typedef itk::ImageRegionConstIterator< ItkMaskImageType > ItkMaskImageIteratorType;
  typedef itk::ImageRegionIteratorWithIndex< ItkOutputImageType > ItkOutputImageIteratorType;

  typename mitk::ImageToItk<ItkMaskImageType>::Pointer maskimagetoitk = mitk::ImageToItk<ItkMaskImageType>::New();
  maskimagetoitk->SetInput(MaskImageFilter->m_MaskTimeSelector->GetOutput());
  maskimagetoitk->Update();
  typename ItkMaskImageType::Pointer maskItkImage = maskimagetoitk->GetOutput();

  typename mitk::ImageToItk<ItkOutputImageType>::Pointer outputimagetoitk = mitk::ImageToItk<ItkOutputImageType>::New();
  outputimagetoitk->SetInput(MaskImageFilter->m_OutputTimeSelector->GetOutput());
  outputimagetoitk->Update();
  typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput();

  // create the iterators
  ItkInputImageType::RegionType inputRegionOfInterest = inputItkImage->GetLargestPossibleRegion();
  ItkInputImageIteratorType  inputIt( inputItkImage, inputRegionOfInterest );
  ItkMaskImageIteratorType  maskIt ( maskItkImage, inputRegionOfInterest );
  ItkOutputImageIteratorType outputIt( outputItkImage, inputRegionOfInterest );

  typename ItkOutputImageType::PixelType m_OutsideValue = itk::NumericTraits<typename ItkOutputImageType::PixelType>::min();

  for ( inputIt.GoToBegin(), maskIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd() && !maskIt.IsAtEnd(); ++inputIt, ++maskIt, ++outputIt)
  {
    if ( maskIt.Get() > itk::NumericTraits<typename ItkMaskImageType::PixelType>::Zero )
    {
      outputIt.Set(inputIt.Get());
    }
    else
    {
      outputIt.Set(m_OutsideValue);
    }
  }
}

#include "mitkImageAccessByItk.h"

void mitk::MaskImageFilter::GenerateData()
{
  mitk::Image::ConstPointer input = this->GetInput();
  mitk::Image::ConstPointer mask  = m_Mask;
  mitk::Image::Pointer output = this->GetOutput();

  if((output->IsInitialized()==false) || (mask.IsNull()) || (mask->GetTimeSlicedGeometry()->GetTimeSteps() == 0))
    return;

  m_InputTimeSelector->SetInput(input);
  m_MaskTimeSelector->SetInput(mask);
  m_OutputTimeSelector->SetInput(this->GetOutput());

  mitk::Image::RegionType outputRegion = output->GetRequestedRegion();
  const mitk::TimeSlicedGeometry *outputTimeGeometry = output->GetTimeSlicedGeometry();
  const mitk::TimeSlicedGeometry *inputTimeGeometry = input->GetTimeSlicedGeometry();
  const mitk::TimeSlicedGeometry *maskTimeGeometry = mask->GetTimeSlicedGeometry();
  ScalarType timeInMS;

  int timestep=0;
  int tstart=outputRegion.GetIndex(3);
  int tmax=tstart+outputRegion.GetSize(3);

  int t;
  for(t=tstart;t<tmax;++t)
  {
    timeInMS = outputTimeGeometry->TimeStepToMS( t );

    timestep = inputTimeGeometry->MSToTimeStep( timeInMS );

    m_InputTimeSelector->SetTimeNr(timestep);
    m_InputTimeSelector->UpdateLargestPossibleRegion();
    m_OutputTimeSelector->SetTimeNr(t);
    m_OutputTimeSelector->UpdateLargestPossibleRegion();

    timestep = maskTimeGeometry->MSToTimeStep( timeInMS );
    m_MaskTimeSelector->SetTimeNr(timestep);
    m_MaskTimeSelector->UpdateLargestPossibleRegion();

    AccessByItk_1(m_InputTimeSelector->GetOutput(),_InternalComputeMask,this);
  }

  m_TimeOfHeaderInitialization.Modified();
}
<commit_msg>FIX: linux compatibility<commit_after>/*=========================================================================

Program:   Medical Imaging & Interaction Toolkit
Module:    $RCSfile$
Language:  C++
Date:      $Date$
Version:   $Revision$

Copyright (c) German Cancer Research Center, Division of Medical and
Biological Informatics. All rights reserved.
See MITKCopyright.txt or http://www.mitk.org/copyright.html for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#include "mitkMaskImageFilter.h"
#include "mitkImageTimeSelector.h"
#include "mitkTimeHelper.h"
#include "mitkProperties.h"

#include "mitkImageToItk.h"

#include "itkImageRegionConstIterator.h"
#include "itkImageRegionIteratorWithIndex.h"

mitk::MaskImageFilter::MaskImageFilter() : m_Mask(NULL)
{
  this->SetNumberOfInputs(2);
  this->SetNumberOfRequiredInputs(2);
  m_InputTimeSelector  = mitk::ImageTimeSelector::New();
  m_MaskTimeSelector   = mitk::ImageTimeSelector::New();
  m_OutputTimeSelector = mitk::ImageTimeSelector::New();
}

mitk::MaskImageFilter::~MaskImageFilter()
{

}

void mitk::MaskImageFilter::SetMask( const mitk::Image* mask )
{
	// Process object is not const-correct so the const_cast is required here
  m_Mask = const_cast< mitk::Image * >( mask );
	this->ProcessObject::SetNthInput(1, m_Mask );
}

const mitk::Image* mitk::MaskImageFilter::GetMask() const
{
  return m_Mask;
}

void mitk::MaskImageFilter::GenerateInputRequestedRegion()
{
  Superclass::GenerateInputRequestedRegion();

  mitk::Image* output = this->GetOutput();
  mitk::Image* input = const_cast< mitk::Image * > ( this->GetInput() );
  mitk::Image* mask  = m_Mask ;
  if((output->IsInitialized()==false) || (mask == NULL) || (mask->GetTimeSlicedGeometry()->GetTimeSteps() == 0))
    return;

  input->SetRequestedRegionToLargestPossibleRegion();
  mask->SetRequestedRegionToLargestPossibleRegion();

  GenerateTimeInInputRegion(output, input);
  GenerateTimeInInputRegion(output, mask);
}

void mitk::MaskImageFilter::GenerateOutputInformation()
{
  mitk::Image::ConstPointer input = this->GetInput();
  mitk::Image::Pointer output = this->GetOutput();

  if ((output->IsInitialized()) && (this->GetMTime() <= m_TimeOfHeaderInitialization.GetMTime()))
    return;

  itkDebugMacro(<<"GenerateOutputInformation()");

  unsigned int i;
  unsigned int *tmpDimensions = new unsigned int[input->GetDimension()];

  for(i=0;i<input->GetDimension();++i)
    tmpDimensions[i]=input->GetDimension(i);

  output->Initialize(input->GetPixelType(),
    input->GetDimension(),
    tmpDimensions,
    input->GetNumberOfChannels());

  delete [] tmpDimensions;

  output->SetGeometry(static_cast<mitk::Geometry3D*>(input->GetGeometry()->Clone().GetPointer()));

  output->SetPropertyList(input->GetPropertyList()->Clone());

  m_TimeOfHeaderInitialization.Modified();
}

template < typename TPixel, unsigned int VImageDimension >
void mitk::_InternalComputeMask(itk::Image<TPixel, VImageDimension>* inputItkImage, mitk::MaskImageFilter* MaskImageFilter)
{
  typedef itk::Image<TPixel, VImageDimension> ItkInputImageType;

  typedef itk::Image<unsigned char, VImageDimension> ItkMaskImageType;
  typedef itk::Image<TPixel, VImageDimension> ItkOutputImageType;

  typedef itk::ImageRegionConstIterator< ItkInputImageType > ItkInputImageIteratorType;
  typedef itk::ImageRegionConstIterator< ItkMaskImageType > ItkMaskImageIteratorType;
  typedef itk::ImageRegionIteratorWithIndex< ItkOutputImageType > ItkOutputImageIteratorType;

  typename mitk::ImageToItk<ItkMaskImageType>::Pointer maskimagetoitk = mitk::ImageToItk<ItkMaskImageType>::New();
  maskimagetoitk->SetInput(MaskImageFilter->m_MaskTimeSelector->GetOutput());
  maskimagetoitk->Update();
  typename ItkMaskImageType::Pointer maskItkImage = maskimagetoitk->GetOutput();

  typename mitk::ImageToItk<ItkOutputImageType>::Pointer outputimagetoitk = mitk::ImageToItk<ItkOutputImageType>::New();
  outputimagetoitk->SetInput(MaskImageFilter->m_OutputTimeSelector->GetOutput());
  outputimagetoitk->Update();
  typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput();

  // create the iterators
  typename ItkInputImageType::RegionType inputRegionOfInterest = inputItkImage->GetLargestPossibleRegion();
  ItkInputImageIteratorType  inputIt( inputItkImage, inputRegionOfInterest );
  ItkMaskImageIteratorType  maskIt ( maskItkImage, inputRegionOfInterest );
  ItkOutputImageIteratorType outputIt( outputItkImage, inputRegionOfInterest );

  typename ItkOutputImageType::PixelType m_OutsideValue = itk::NumericTraits<typename ItkOutputImageType::PixelType>::min();

  for ( inputIt.GoToBegin(), maskIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd() && !maskIt.IsAtEnd(); ++inputIt, ++maskIt, ++outputIt)
  {
    if ( maskIt.Get() > itk::NumericTraits<typename ItkMaskImageType::PixelType>::Zero )
    {
      outputIt.Set(inputIt.Get());
    }
    else
    {
      outputIt.Set(m_OutsideValue);
    }
  }
}

#include "mitkImageAccessByItk.h"

void mitk::MaskImageFilter::GenerateData()
{
  mitk::Image::ConstPointer input = this->GetInput();
  mitk::Image::Pointer mask  = m_Mask;
  mitk::Image::Pointer output = this->GetOutput();

  if((output->IsInitialized()==false) || (mask.IsNull()) || (mask->GetTimeSlicedGeometry()->GetTimeSteps() == 0))
    return;

  m_InputTimeSelector->SetInput(input);
  m_MaskTimeSelector->SetInput(mask);
  m_OutputTimeSelector->SetInput(this->GetOutput());

  mitk::Image::RegionType outputRegion = output->GetRequestedRegion();
  const mitk::TimeSlicedGeometry *outputTimeGeometry = output->GetTimeSlicedGeometry();
  const mitk::TimeSlicedGeometry *inputTimeGeometry = input->GetTimeSlicedGeometry();
  const mitk::TimeSlicedGeometry *maskTimeGeometry = mask->GetTimeSlicedGeometry();
  ScalarType timeInMS;

  int timestep=0;
  int tstart=outputRegion.GetIndex(3);
  int tmax=tstart+outputRegion.GetSize(3);

  int t;
  for(t=tstart;t<tmax;++t)
  {
    timeInMS = outputTimeGeometry->TimeStepToMS( t );

    timestep = inputTimeGeometry->MSToTimeStep( timeInMS );

    m_InputTimeSelector->SetTimeNr(timestep);
    m_InputTimeSelector->UpdateLargestPossibleRegion();
    m_OutputTimeSelector->SetTimeNr(t);
    m_OutputTimeSelector->UpdateLargestPossibleRegion();

    timestep = maskTimeGeometry->MSToTimeStep( timeInMS );
    m_MaskTimeSelector->SetTimeNr(timestep);
    m_MaskTimeSelector->UpdateLargestPossibleRegion();

    AccessByItk_1(m_InputTimeSelector->GetOutput(),_InternalComputeMask,this);
  }

  m_TimeOfHeaderInitialization.Modified();
}
<|endoftext|>


<commit_before>// This code is based on Sabberstone project.
// Copyright (c) 2017-2019 SabberStone Team, darkfriend77 & rnilva
// RosettaStone is hearthstone simulator using C++ with reinforcement learning.
// Copyright (c) 2019 Chris Ohk, Youngjoong Kim, SeungHyun Jeon

#include <Rosetta/Actions/PlayCard.hpp>
#include <Rosetta/Actions/Targeting.hpp>
#include <Rosetta/Games/Game.hpp>
#include <Rosetta/Tasks/Tasks.hpp>

namespace RosettaStone::Generic
{
void PlayCard(Player& player, Entity* source, Character* target, int fieldPos)
{
    // Verify mana is sufficient
    if (source->card.cost > player.currentMana)
    {
        return;
    }

    // Verify target is valid
    if (!IsValidTarget(source, target))
    {
        return;
    }

    // Spend mana to play cards
    if (source->card.cost > 0)
    {
        player.currentMana -= source->card.cost;
    }

    // Erase from player's hand
    player.GetHand().RemoveCard(*source);

    // Set card's owner
    source->SetOwner(player);

    // Pass to sub-logic
    switch (source->card.cardType)
    {
        case CardType::MINION:
        {
            const auto minion = dynamic_cast<Minion*>(source);
            PlayMinion(player, minion, target, fieldPos);
            break;
        }
        case CardType::SPELL:
        {
            const auto spell = dynamic_cast<Spell*>(source);
            PlaySpell(player, spell, target);
            break;
        }
        case CardType::WEAPON:
        {
            const auto weapon = dynamic_cast<Weapon*>(source);
            PlayWeapon(player, weapon, target);
            break;
        }
        default:
            throw std::invalid_argument(
                "Generic::PlayCard() - Invalid card type!");
    }
}

void PlayMinion(Player& player, Minion* minion, Character* target, int fieldPos)
{
    (void)target;

    // Add minion to battlefield
    player.GetField().AddMinion(*minion, fieldPos);

    // Apply card mechanics tags
    for (const auto tags : minion->card.gameTags)
    {
        minion->SetGameTag(tags.first, tags.second);
    }

    // Process power tasks
    for (auto& powerTask : minion->card.power.GetPowerTask())
    {
        if (powerTask == nullptr)
        {
            continue;
        }

        powerTask->SetSource(minion);
        powerTask->SetTarget(target);
        powerTask->Run(player);
    }

    player.GetGame()->ProcessDestroyAndUpdateAura();
}

void PlaySpell(Player& player, Spell* spell, Character* target)
{
    // Process power tasks
    for (auto& powerTask : spell->card.power.GetPowerTask())
    {
        powerTask->SetSource(spell);
        powerTask->SetTarget(target);
        powerTask->Run(player);
    }

    player.GetGame()->ProcessDestroyAndUpdateAura();
}

void PlayWeapon(Player& player, Weapon* weapon, Character* target)
{
    (void)target;

    player.GetHero()->AddWeapon(*weapon);
}

bool IsPlayableByCardReq(Entity* source)
{
    for (auto& requirement : source->card.playRequirements)
    {
        switch (requirement.first)
        {
            case PlayReq::REQ_MINIMUM_ENEMY_MINIONS:
            {
                auto& opField = source->GetOwner().GetOpponent().GetField();
                if (opField.GetNumOfMinions() < requirement.second)
                {
                    return false;
                }
                break;
            }
            case PlayReq::REQ_MINION_TARGET:
            case PlayReq::REQ_ENEMY_TARGET:
            case PlayReq::REQ_NONSELF_TARGET:
                break;
            default:
                break;
        }
    }

    return true;
}
}  // namespace RosettaStone::Generic
<commit_msg>feat(card-impl): Add code to call IsPlayableByCardReq() function<commit_after>// This code is based on Sabberstone project.
// Copyright (c) 2017-2019 SabberStone Team, darkfriend77 & rnilva
// RosettaStone is hearthstone simulator using C++ with reinforcement learning.
// Copyright (c) 2019 Chris Ohk, Youngjoong Kim, SeungHyun Jeon

#include <Rosetta/Actions/PlayCard.hpp>
#include <Rosetta/Actions/Targeting.hpp>
#include <Rosetta/Games/Game.hpp>
#include <Rosetta/Tasks/Tasks.hpp>

namespace RosettaStone::Generic
{
void PlayCard(Player& player, Entity* source, Character* target, int fieldPos)
{
    // Verify mana is sufficient
    if (source->card.cost > player.currentMana)
    {
        return;
    }

    // Check if we can play this card and the target is valid
    if (!IsPlayableByCardReq(source) || !IsValidTarget(source, target))
    {
        return;
    }

    // Spend mana to play cards
    if (source->card.cost > 0)
    {
        player.currentMana -= source->card.cost;
    }

    // Erase from player's hand
    player.GetHand().RemoveCard(*source);

    // Set card's owner
    source->SetOwner(player);

    // Pass to sub-logic
    switch (source->card.cardType)
    {
        case CardType::MINION:
        {
            const auto minion = dynamic_cast<Minion*>(source);
            PlayMinion(player, minion, target, fieldPos);
            break;
        }
        case CardType::SPELL:
        {
            const auto spell = dynamic_cast<Spell*>(source);
            PlaySpell(player, spell, target);
            break;
        }
        case CardType::WEAPON:
        {
            const auto weapon = dynamic_cast<Weapon*>(source);
            PlayWeapon(player, weapon, target);
            break;
        }
        default:
            throw std::invalid_argument(
                "Generic::PlayCard() - Invalid card type!");
    }
}

void PlayMinion(Player& player, Minion* minion, Character* target, int fieldPos)
{
    (void)target;

    // Add minion to battlefield
    player.GetField().AddMinion(*minion, fieldPos);

    // Apply card mechanics tags
    for (const auto tags : minion->card.gameTags)
    {
        minion->SetGameTag(tags.first, tags.second);
    }

    // Process power tasks
    for (auto& powerTask : minion->card.power.GetPowerTask())
    {
        if (powerTask == nullptr)
        {
            continue;
        }

        powerTask->SetSource(minion);
        powerTask->SetTarget(target);
        powerTask->Run(player);
    }

    player.GetGame()->ProcessDestroyAndUpdateAura();
}

void PlaySpell(Player& player, Spell* spell, Character* target)
{
    // Process power tasks
    for (auto& powerTask : spell->card.power.GetPowerTask())
    {
        powerTask->SetSource(spell);
        powerTask->SetTarget(target);
        powerTask->Run(player);
    }

    player.GetGame()->ProcessDestroyAndUpdateAura();
}

void PlayWeapon(Player& player, Weapon* weapon, Character* target)
{
    (void)target;

    player.GetHero()->AddWeapon(*weapon);
}

bool IsPlayableByCardReq(Entity* source)
{
    for (auto& requirement : source->card.playRequirements)
    {
        switch (requirement.first)
        {
            case PlayReq::REQ_MINIMUM_ENEMY_MINIONS:
            {
                auto& opField = source->GetOwner().GetOpponent().GetField();
                if (opField.GetNumOfMinions() < requirement.second)
                {
                    return false;
                }
                break;
            }
            case PlayReq::REQ_MINION_TARGET:
            case PlayReq::REQ_ENEMY_TARGET:
            case PlayReq::REQ_NONSELF_TARGET:
                break;
            default:
                break;
        }
    }

    return true;
}
}  // namespace RosettaStone::Generic
<|endoftext|>