// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Copyright (c) 2012-2013 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <boost/uuid/uuid.hpp>
#include <boost/uuid/random_generator.hpp>
#include <unordered_map>


#include "transactions_flow_test.h"
#include "include_base_utils.h"
using namespace epee;
#include "console_handler.h"
#include "storages/http_abstract_invoke.h"
#include "wallet/wallet2.h"
#include "string_coding.h"
#include "math_helper.h"
using namespace currency;

#define TESTS_DEFAULT_FEE                   TX_DEFAULT_FEE 

std::string get_random_hex_text(size_t len)
{
  std::string buff(len / 2, 0);
  crypto::generate_random_bytes(len / 2, (void*)buff.data());
  return string_tools::buff_to_hex_nodelimer(buff);
}


std::wstring generate_random_wallet_name()
{
  std::wstringstream ss;
  ss << boost::uuids::random_generator()();
  return ss.str();
}

inline uint64_t random(const uint64_t max_value) {
  return (uint64_t(rand()) ^
          (uint64_t(rand())<<16) ^
          (uint64_t(rand())<<32) ^
          (uint64_t(rand())<<48)) % max_value;
}

bool do_send_money(tools::wallet2& w1, tools::wallet2& w2, size_t mix_in_factor, uint64_t amount_to_transfer, transaction& tx, size_t parts=1)
{
  CHECK_AND_ASSERT_MES(parts > 0, false, "parts must be > 0");

  std::vector<currency::tx_destination_entry> dsts;
  dsts.reserve(parts);
  uint64_t amount_used = 0;
  uint64_t max_part = amount_to_transfer / parts;

  for (size_t i = 0; i < parts; ++i)
  {
    currency::tx_destination_entry de;
    de.addr.push_back(w2.get_account().get_keys().account_address);

    if (i < parts - 1)
      de.amount = random(max_part);
    else
      de.amount = amount_to_transfer - amount_used;
    amount_used += de.amount;

    //std::cout << "PARTS (" << amount_to_transfer << ") " << amount_used << " " << de.amount << std::endl;

    dsts.push_back(de);
  }

  try
  {  
    uint64_t ticks = epee::misc_utils::get_tick_count();
    w1.transfer(dsts, mix_in_factor, 0, w1.get_core_runtime_config().tx_default_fee, std::vector<currency::extra_v>(), std::vector<currency::attachment_v>(), tools::detail::ssi_digit, tools::tx_dust_policy(w1.get_core_runtime_config().tx_default_fee), tx);
    uint64_t ticks_for_tx = epee::misc_utils::get_tick_count() - ticks;
    LOG_PRINT_GREEN("Send tx took " << ticks_for_tx << "ms", LOG_LEVEL_0);
    return true;
  }
  catch (const std::exception& e)
  {
    LOG_PRINT_RED("Failed to send tx: " << e.what(), LOG_LEVEL_0);
    return false;
  }
}

bool do_register_alias(tools::wallet2& w1, const std::string& alias_name, const std::string& comment, const account_public_address& address, transaction& tx)
{
  try
  {

    uint64_t ticks = epee::misc_utils::get_tick_count();
    currency::extra_alias_entry eae = AUTO_VAL_INIT(eae);
    eae.m_address = address;
    eae.m_alias = alias_name;
    eae.m_text_comment = comment;
    w1.request_alias_registration(eae, tx, TX_DEFAULT_FEE, currency::get_alias_coast_from_fee(eae.m_alias, TX_DEFAULT_FEE*10));
    uint64_t ticks_for_tx = epee::misc_utils::get_tick_count() - ticks;
    LOG_PRINT_GREEN("Send alias_registration_tx took " << ticks_for_tx << "ms", LOG_LEVEL_0);
    return true;
  }
  catch (const std::exception& e)
  {
    LOG_PRINT_RED("Failed to send tx(alias): " << e.what(), LOG_LEVEL_0);
    return false;
  }
}

bool do_register_offer(tools::wallet2& w1, transaction& tx)
{
  const char* currencies[] = { "USD", "EUR", "CNY", "INR", "BRR", "GBP", "CHF", "JPY", "RUR", "BTC", "AUD", "CAD", "SGD", "MYR", "AZN", "ALL", "DZD", "AOA", "ARS", "AMD", "AWG", "AFA", "BSD", "BDT", "BBD", "BHD", "BZD", "BYB", "BGL", "BOB", "BWP", "BND", "BMD", "BIF", "VUV", "HUF", "VEB", "XCD", "VND", "HTG", "GMD", "GHC", "GTQ", "GNF", "GIP", "HML", "GEL", "ANG", "DKK", "RSD", "AED", "STD", "ZWD", "KYD", "SBD", "TTD", "FJD", "DOP", "EGP", "ZMK", "NIO", "ILS", "IDR", "JOD", "IQD", "IRR", "IEP", "ISK", "YER", "KHR", "QAR", "KES", "PGK", "CYP", "COP", "KMF", "BAM", "CRC", "CUP", "KWD", "HRK", "KGS", "MMK", "LAK", "LVL", "SLL", "LRD", "LBP", "LYD", "LTL", "LSL", "MUR", "MRO", "MKD", "MWK", "MGF", "MVR", "MTL", "MAD", "MXN", "MZM", "MDL", "MNT", "ERN", "BTN", "NPR", "NGN", "NZD", "PEN", "TWD", "NOK", "OMR", "TOP", "PKR", "PYG", "PLZ", "ROL", "SVC", "WST", "SAR", "SZL", "KPW", "SCR", "SYP", "SOS", "SDD", "SRG", "THB", "TZS", "KZT", "SIT", "TND", "TRY", "TMM", "UGS", "UZS", "UAH", "UYP", "PHP", "DJF", "XOF", "XAF", "XOP", "XPF", "RWF", "FKP", "CZK", "CLP", "SEK", "LKR", "ESC", "CVE", "EEK", "ETB", "ZAR", "KRW", "JMD" };
  const char* countries[] = { "ZD", "AF", "AX", "AL", "DZ", "AS", "AD", "AO", "AI", "AG", "AR", "AM", "AW", "AU", "AT", "AZ", "BS", "BH", "BD", "BB", "BY", "BE", "BZ", "BJ", "BM", "BT", "BO", "BQ", "BA", "BW", "BV", "BR", "IO", "UM", "VG", "BN", "BG", "BF", "BI", "KH", "CM", "CA", "CV", "KY", "CF", "TD", "CL", "CN", "CX", "CC", "CO", "KM", "CG", "CD", "CK", "CR", "HR", "CU", "CW", "CY", "CZ", "DK", "DJ", "DM", "DO", "EC", "EG", "SV", "GQ", "ER", "EE", "ET", "FK", "FO", "FJ", "FI", "FR", "GF", "PF", "TF", "GA", "GM", "GE", "DE", "GH", "GI", "GR", "GL", "GD", "GP", "GU", "GT", "GG", "GN", "GW", "GY", "HT", "HM", "HN", "HK", "HU", "IS", "IN", "ID", "CI", "IR", "IQ", "IE", "IM", "IL", "IT", "JM", "JP", "JE", "JO", "KZ", "KE", "KI", "KW", "KG", "LA", "LV", "LB", "LS", "LR", "ZF", "LY", "LI", "LT", "LU", "MO", "MK", "MG", "MW", "MY", "MV", "ML", "MT", "MH", "MQ", "MR", "MU", "YT", "MX", "FM", "MD", "MC", "MN", "ME", "MS", "MA", "MZ", "MM", "NA", "NR", "NP", "NL", "NC", "NZ", "NI", "NE", "NG", "NU", "NF", "KP", "MP", "NO", "OM", "PK", "PW", "PS", "PA", "PG", "PY", "PE", "PH", "PN", "PL", "PT", "PR", "QA", "XK", "RE", "RO", "RU", "RW", "BL", "SH", "KN", "LC", "MF", "PM", "VC", "WS", "SM", "ST", "SA", "SN", "RS", "SC", "SL", "SG", "SX", "SK", "SI", "SB", "SO", "ZA", "GS", "KR", "ZE", "SS", "ES", "LK", "SD", "SR", "SJ", "SZ", "SE", "CH", "SY", "TW", "TJ", "TZ", "TH", "TL", "TG", "TK", "TO", "TT", "TN", "TR", "ZC", "TM", "TC", "TV", "UG", "UA", "AE", "GB", "US", "UY", "UZ", "VU", "VE", "VN", "WF", "EH", "YE", "ZM", "ZW"};

  try
  {
    uint64_t ticks = epee::misc_utils::get_tick_count();
    bc_services::offer_details_ex od = AUTO_VAL_INIT(od);
    od.amount_primary = rand()%1000000000000;
    od.amount_target = rand() % 1000000000000;
    od.bonus = get_random_hex_text(7);
    od.target = get_random_hex_text(7); 
    od.primary = currencies[rand() % (sizeof(currencies) / sizeof(currencies[0]))];
    od.location_country = countries[rand() % (sizeof(countries) / sizeof(countries[0]))];
    od.location_city = get_random_hex_text(7);
    od.contacts = "[{\"EMAIL\":\""; od.contacts += get_random_hex_text(5) + "@" + get_random_hex_text(5) + ".com\"}]";           // [] (Skype, mail, ICQ, etc., website)
    od.comment = get_random_hex_text(7);
    od.payment_types = "[]";      // []money accept type(bank transaction, internet money, cash, etc)
    od.deal_option = "";        // []full amount, by parts
    od.category = "NAA,NAB";           // []
    od.expiration_time = 10;        // n-days
    od.fee = TX_DEFAULT_FEE;

    w1.push_offer(od, tx);
    uint64_t ticks_for_tx = epee::misc_utils::get_tick_count() - ticks;
    LOG_PRINT_GREEN("Send push_offer_tx took " << ticks_for_tx << "ms", LOG_LEVEL_0);
    return true;
  }
  catch (const std::exception& e)
  {
    LOG_PRINT_RED("Failed to send tx(offer): " << e.what(), LOG_LEVEL_0);
    return false;
  }
}



#define ESTIMATE_INPUTS_COUNT_LIMIT_FOR_TX_BLOWUP (CURRENCY_TX_MAX_ALLOWED_OUTS - 4)

bool do_send_money_by_fractions(tools::wallet2& w1, tools::wallet2& w2, size_t mix_in_factor, uint64_t amount_to_transfer, transaction& tx, uint64_t fraction_size)
{
  //send all of "amount_to_transfer" even with last big transfer to utilize all amount taken from transfers
  CHECK_AND_ASSERT_MES(fraction_size > 0, false, "parts must be > 0");

  std::vector<currency::tx_destination_entry> dsts;
  dsts.reserve(ESTIMATE_INPUTS_COUNT_LIMIT_FOR_TX_BLOWUP);
  uint64_t amount_used = 0;

  for (size_t i = 0; i < ESTIMATE_INPUTS_COUNT_LIMIT_FOR_TX_BLOWUP; ++i)
  {
    currency::tx_destination_entry de;
    de.addr.push_back(w2.get_account().get_keys().account_address);

    if (i == ESTIMATE_INPUTS_COUNT_LIMIT_FOR_TX_BLOWUP - 1)
    {
      de.amount = amount_to_transfer - amount_used;
    }
    else if (amount_used + fraction_size < amount_to_transfer)
    {
      de.amount = fraction_size;
    }
    else
    {
      de.amount = amount_to_transfer - amount_used;
    }
    if (!de.amount)
      break;
    amount_used += de.amount;
    dsts.push_back(de);
  }

  try
  {
    w1.transfer(dsts, mix_in_factor, 0, w1.get_core_runtime_config().tx_default_fee, std::vector<currency::extra_v>(), std::vector<currency::attachment_v>(), tools::detail::ssi_digit, tools::tx_dust_policy(w1.get_core_runtime_config().tx_default_fee), tx);
    LOG_PRINT_GREEN("Split transaction sent " << get_transaction_hash(tx) <<  ", destinations: " << dsts.size() << ", blob size: " << get_object_blobsize(tx), LOG_LEVEL_0);
    return true;
  }
  catch (const std::exception& e)
  {
    LOG_ERROR("exception while sending transfer: " << e.what());
    return false;
  }
}

uint64_t got_money_in_first_transfers(const tools::wallet2::transfer_container& incoming_transfers, size_t n_transfers)
{
  uint64_t summ = 0;
  size_t count = 0;
  BOOST_FOREACH(const tools::wallet2::transfer_details& td, incoming_transfers)
  {
    summ += td.m_ptx_wallet_info->m_tx.vout[td.m_internal_output_index].amount;
    if(++count >= n_transfers)
      return summ;
  }
  return summ;
}

#define FIRST_N_TRANSFERS 10*10
struct flow_test_context
{
  flow_test_context(tools::wallet2& w_) :w(w_), stop_end_exit(false), pause(false)
  {}
  tools::wallet2& w;
  std::atomic<bool> stop_end_exit;
  std::atomic<bool> pause;
};


void wait_unlock_money(tools::wallet2& w, flow_test_context& control)
{
  size_t blocks_fetched = 0;
  uint64_t wait_count = 0;
  while (!control.pause && !control.stop_end_exit)
  {
    LOG_PRINT_GREEN("Wait iteration " << wait_count++ << " ...", LOG_LEVEL_0);
    misc_utils::sleep_no_w(1000);
    bool received_money = false;
    w.refresh(blocks_fetched, received_money, control.stop_end_exit);
    if (blocks_fetched)
    {
      uint64_t unlocked_balance = 0;
      uint64_t balance = w.balance(unlocked_balance);
      LOG_PRINT_GREEN("Balance: " << balance << ", unlocked balance: " << unlocked_balance, LOG_LEVEL_0);
      if (unlocked_balance == balance)
        return;
    }
  }
}

std::string get_incoming_transfers_str(tools::wallet2& w)
{
  tools::wallet2::transfer_container transfers;
  w.get_transfers(transfers);

  uint64_t spent_count = 0;
  uint64_t unspent_count = 0;
  for (const auto& td : transfers)
  {
    if (td.m_flags&WALLET_TRANSFER_DETAIL_FLAG_SPENT)
    {
      ++spent_count;
    }
    else
    {
      ++unspent_count;
    }
  }
  std::stringstream ss;
  ss << "spent: " << spent_count << ", unspent: " << unspent_count;
  return ss.str();
}

void wait_receive_and_unlock_half_balance(tools::wallet2& w, flow_test_context& control)
{
  size_t blocks_fetched = 0;
  uint64_t wait_count = 0;

  while (w.unlocked_balance() < w.balance()/2 && !control.pause && !control.stop_end_exit)
  {
    LOG_PRINT_GREEN("Wait iteration " << wait_count++ << " ...", LOG_LEVEL_0);
    misc_utils::sleep_no_w(1000);
    bool received_money = false;
    w.refresh(blocks_fetched, received_money, control.stop_end_exit);
    if (blocks_fetched)
    {
      uint64_t unlocked_balance = 0;
      uint64_t balance = w.balance(unlocked_balance);
      LOG_PRINT_GREEN("Balance: " << balance << ", unlocked balance: " << unlocked_balance, LOG_LEVEL_0);
    }
  }
  LOG_PRINT_GREEN("Storing wallet... ", LOG_LEVEL_0);
  w.store();
  LOG_PRINT_GREEN("Wallet stored OK", LOG_LEVEL_0);
}

void wait_receive_and_unlock_money(tools::wallet2& w, uint64_t amount_to_wait)
{
  size_t blocks_fetched = 0;
  uint64_t wait_count = 0;
  
  while (w.unlocked_balance() < amount_to_wait )
  {
    LOG_PRINT_GREEN("Wait iteration " << wait_count++ << " ...", LOG_LEVEL_0);
    misc_utils::sleep_no_w(1000);
    bool received_money = false;
    std::atomic<bool> stop(false);
    w.refresh(blocks_fetched, received_money, stop);
    if (blocks_fetched)
    {
      uint64_t unlocked_balance = 0;
      uint64_t balance = w.balance(unlocked_balance);
      LOG_PRINT_GREEN("Balance: " << balance << ", unlocked balance: " << unlocked_balance, LOG_LEVEL_0);
      if (unlocked_balance == balance && unlocked_balance >= amount_to_wait)
        return;
    }
  }
}




class flow_test_console_cmmands_handler
{
  
  epee::console_handlers_binder m_cmd_binder;
  flow_test_context& m_context;
public:
  flow_test_console_cmmands_handler(flow_test_context& contxt):m_context(contxt)
  {
    m_cmd_binder.set_handler("help", boost::bind(&console_handlers_binder::help, &m_cmd_binder, _1), "Show this help");
    m_cmd_binder.set_handler("exit", boost::bind(&flow_test_console_cmmands_handler::exit, this, _1), "Exit");
    m_cmd_binder.set_handler("pause", boost::bind(&flow_test_console_cmmands_handler::pause, this, _1), "Pause");
    m_cmd_binder.set_handler("continue", boost::bind(&flow_test_console_cmmands_handler::do_continue, this, _1), "Continue");
    m_cmd_binder.set_handler("refresh", boost::bind(&flow_test_console_cmmands_handler::refresh, this, _1), "Refresh");
  }

  bool start_handling()
  {
    m_cmd_binder.start_handling("#");
    return true;
  }

private:
  bool exit(const std::vector<std::string>& args)
  {
    m_context.stop_end_exit = true;
    return true;
  }

  bool pause(const std::vector<std::string>& args)
  {
    m_context.pause = true;
    return true;
  }

  bool do_continue(const std::vector<std::string>& args)
  {
    m_context.pause = false;
    return true;
  }
  bool refresh(const std::vector<std::string>& args)
  {
    LOG_PRINT_GREEN("Refreshing...  ", LOG_LEVEL_0);
    m_context.w.refresh();
    LOG_PRINT_GREEN("Storing wallet... ", LOG_LEVEL_0);
    m_context.w.store();
    LOG_PRINT_GREEN("Wallet stored OK", LOG_LEVEL_0);
    return true;
  }

};

uint64_t get_tx_pool_size(std::string& daemon_addr)
{
  epee::net_utils::http::http_simple_client http_client;

  currency::COMMAND_RPC_GET_INFO::request req = AUTO_VAL_INIT(req);
  req.flags = 0;
  currency::COMMAND_RPC_GET_INFO::response res = AUTO_VAL_INIT(res);

  bool r = net_utils::invoke_http_json_remote_command2(daemon_addr + "/getinfo", req, res, http_client, 10000);
  if (!r)
  {
    std::cout << "ERROR: failed to invoke request to daemon" << ENDL;
    return false;
  }
  return res.tx_pool_size;
}

bool test_serialization()
{
  extra_alias_entry ee = {};
  ee.m_text_comment = "adadasdasd";
  std::string ddd = currency::obj_to_json_str(ee);
  return true;
}

bool transactions_flow_test(
  std::wstring path_source_wallet, std::string source_wallet_pass,
  std::wstring path_terget_wallet, std::string target_wallet_pass,
  std::string& daemon_addr_a,
  std::string& daemon_addr_b,
  size_t mix_in_factor, size_t action, uint64_t max_tx_in_pool)
{
  LOG_PRINT_L0("-----------------------STARTING TRANSACTIONS FLOW TEST (action " << action << ")-----------------------");
  LOG_PRINT_L0("Max tx in pool: " << max_tx_in_pool);
  tools::wallet2 w1, w2;
  flow_test_context control(w1);
  flow_test_console_cmmands_handler cc(control);
  cc.start_handling();

  try
  {
    w1.load(path_source_wallet, source_wallet_pass);
    w2.load(path_terget_wallet, target_wallet_pass);
  }
  catch (const std::exception& e)
  {
    LOG_ERROR("failed to generate wallet: " << e.what());
    return false;
  }

  w1.init(daemon_addr_a);
  w2.init(daemon_addr_b);

  size_t blocks_fetched = 0;
  bool received_money;
  bool ok;
  std::atomic<bool> stop;
  if(!w1.refresh(blocks_fetched, received_money, ok, stop))
  {
    LOG_ERROR( "failed to refresh source wallet from " << daemon_addr_a );
    return false;
  }
  LOG_PRINT_GREEN("Using wallets: " << ENDL
    << "Source:  " << w1.get_account().get_public_address_str() << ENDL << "Path: " << epee::string_encoding::wstring_to_utf8(path_source_wallet) << ENDL
    << "Target:  " << w2.get_account().get_public_address_str() << ENDL << "Path: " << epee::string_encoding::wstring_to_utf8(path_terget_wallet), LOG_LEVEL_1);

  //lets make a lot of small outs to ourselves
  //since it is not possible to start from transaction that bigger than 500Kb, we gonna make transactions 
  //with 500 outs (about 400kb), and we have to wait appropriate count blocks, mined for test wallet
  LOG_PRINT_GREEN("Transfers: " << get_incoming_transfers_str(w1), LOG_LEVEL_0);

  uint64_t transfer_size = TX_DEFAULT_FEE;//amount_to_transfer / transactions_count;
  tools::wallet2::transfer_container incoming_transfers; 
  size_t prepared_transfers = 0;

   
  if (action == TRANSACTIONS_FLOW_TESTACTION_SPLIT_INITAL)
  {
    LOG_PRINT_GREEN("Waiting to unlock money...", LOG_LEVEL_0);
    wait_unlock_money(w1, control);
    while (!control.stop_end_exit)
    {
      LOG_PRINT_GREEN("Getting transfers...", LOG_LEVEL_0);
      w1.get_transfers(incoming_transfers);
      LOG_PRINT_GREEN("Got " << incoming_transfers.size() << " transfers, splitting to fractions...", LOG_LEVEL_0);
      //lets go!
      size_t count = 0;
      prepared_transfers = 0;
      BOOST_FOREACH(tools::wallet2::transfer_details& td, incoming_transfers)
      {
        if (td.is_spent())
          continue;

        if (td.m_ptx_wallet_info->m_tx.vout[td.m_internal_output_index].amount <= transfer_size + TX_DEFAULT_FEE)
        {
          ++prepared_transfers;
          continue;
        }

        ++count;
        currency::transaction tx_s;
        if (w1.unlocked_balance() >= td.m_ptx_wallet_info->m_tx.vout[td.m_internal_output_index].amount)
        {
          bool r = do_send_money_by_fractions(w1, w1, 0, td.m_ptx_wallet_info->m_tx.vout[td.m_internal_output_index].amount - TX_DEFAULT_FEE, tx_s, transfer_size);
          CHECK_AND_ASSERT_MES(r, false, "Failed to send starter tx " << get_transaction_hash(tx_s));
        }
        else
        {
          break;
        }
      }
      if (!count)
        break;

      //waiting to unlock money
      LOG_PRINT_GREEN("Waiting to unlock money...", LOG_LEVEL_0);
      wait_unlock_money(w1, control);
    }
    LOG_PRINT_GREEN("Storing wallet...", LOG_LEVEL_0);
    w1.store();
    LOG_PRINT_GREEN("Wallet splitted, " << prepared_transfers << " prepared_transfers.", LOG_LEVEL_0);
    return true;
  }

      //do actual transfer
  uint64_t transfered_money = 0;
  size_t i = 0;
  struct tx_test_entry
  {
    transaction tx;
    size_t m_received_count;
    uint64_t amount_transfered;
  };

  size_t current_action = action;
  if (current_action == TRANSACTIONS_FLOW_TESTACTION_MIXED)
    current_action = TRANSACTIONS_FLOW_TESTACTION_DEFAULT;

  crypto::key_image lst_sent_ki = AUTO_VAL_INIT(lst_sent_ki);
  std::unordered_map<crypto::hash, tx_test_entry> txs;

  enum test_state
  {
    inflate_pool = 0, 
    deflate_pool = 1
  };
  test_state state = inflate_pool;

  epee::math_helper::once_a_time_seconds<10> state_check_interval;
  while (!control.stop_end_exit)
  {

    state_check_interval.do_call([&]()
    {
      uint64_t pool_size = get_tx_pool_size(daemon_addr_a);
      switch (state)
      {
      case inflate_pool:
        if (pool_size > max_tx_in_pool)
        {
          LOG_PRINT_GREEN("POOL SIZE " << pool_size << ", switched to deflate mode...", LOG_LEVEL_0);
          state = deflate_pool;
        }
        break;
      case deflate_pool:
        if (pool_size == 0)
        {
          LOG_PRINT_GREEN("POOL SIZE " << pool_size << ", switched to inflate mode...", LOG_LEVEL_0);
          state = inflate_pool;
        }
        else if (pool_size < 20)
        {
          LOG_PRINT_GREEN("Pool is about to be empty: " << pool_size, LOG_LEVEL_0);
        }
        break;
      default:
        break;
      }
      return true;
    });
    
    if (control.pause || state == deflate_pool)
    {
      --i;
      epee::misc_utils::sleep_no_w(100);
      continue;
    }
    

    //uint64_t amount_to_tx = (amount_to_transfer - transfered_money) > transfer_size ? transfer_size : (amount_to_transfer - transfered_money);
    uint64_t sent_amount = 0;

    transaction tx;
    bool res = false;
    if (current_action == TRANSACTIONS_FLOW_TESTACTION_DEFAULT)
    {
      res = do_send_money(w1, w2, mix_in_factor, TX_DEFAULT_FEE, tx);
    }
    else if (current_action == TRANSACTIONS_FLOW_TESTACTION_ALIAS)
    {
      sent_amount = currency::get_alias_coast_from_fee(get_random_hex_text(12), TX_DEFAULT_FEE);
      currency::account_base acc;
      acc.generate();
      res = do_register_alias(w1, get_random_hex_text(12), get_random_hex_text(15), acc.get_public_address(), tx);
    }
    else if (current_action == TRANSACTIONS_FLOW_TESTACTION_OFFERS)
    {
      sent_amount = TX_DEFAULT_FEE;
      currency::account_base acc;
      acc.generate();
      res = do_register_offer(w1, tx);
    }


    if(!res)
    {
      LOG_PRINT_GREEN("Sync wallet(transfers: " << get_incoming_transfers_str(w1) << ")...", LOG_LEVEL_0);
      wait_receive_and_unlock_half_balance(w1, control);
      LOG_PRINT_GREEN("Sync wallet finished(transfers: " << get_incoming_transfers_str(w1) << ")", LOG_LEVEL_0);
      continue;
    }
    lst_sent_ki = boost::get<txin_to_key>(tx.vin[0]).k_image;

    transfered_money += sent_amount;

    LOG_PRINT_L0("Transferred[type: "<< current_action << "]" << print_money(get_outs_money_amount(tx)) << "(+fee:" << print_money(get_tx_fee(tx)) << "), i=" << i );
    tx_test_entry& ent = txs[get_transaction_hash(tx)] = boost::value_initialized<tx_test_entry>();
    ent.amount_transfered = sent_amount;
    ent.tx = tx;
    if (action == TRANSACTIONS_FLOW_TESTACTION_MIXED)
    {
      current_action++;
      if (current_action >= TRANSACTIONS_FLOW_TESTACTION_OFFERS)
        current_action = TRANSACTIONS_FLOW_TESTACTION_DEFAULT;
    }
  }

  LOG_PRINT_GREEN("Exiting, storing wallet... ", LOG_LEVEL_0);
  w1.store();
  LOG_PRINT_GREEN("Wallet stored OK", LOG_LEVEL_0);

  return true;
}

