#include <hive/plugins/sql_serializer/blockchain_data_filter.hpp>

#include <fc/io/json.hpp>
#include <fc/log/logger.hpp>

#include <fstream>
class filter_processor
{
  using collection = std::map<std::string, hive::protocol::operation>;

  collection ops;
  collection result_ops;

  hive::plugins::sql_serializer::blockchain_filter filter;

  void find_ops( const std::string& file_name );

  public:

    filter_processor( const std::string& op_type, const std::string& pattern, const std::string& file_name );

    void cmp();

    void summary( bool verbose );
};

filter_processor::filter_processor( const std::string& op_type, const std::string& pattern, const std::string& file_name )
  : filter( hive::plugins::sql_serializer::utils::make_filter( op_type, pattern ) )
{
  find_ops( file_name );
}

void filter_processor::find_ops( const std::string& file_name )
{
  fc::variant _json;
  try
  {
    std::ifstream f( file_name.c_str() );
    std::stringstream _buffer;
    _buffer << f.rdbuf();

    std::string _content = _buffer.str();

    f.close();

    if( _content.empty() )
    {
      ilog("lack of file content");
      return;
    }

    _json = fc::json::from_string( _content, fc::json::format_validation_mode::relaxed ).as< fc::variant >();
  }
  FC_CAPTURE_LOG_AND_RETHROW(("open file"))

  if( !_json.is_object() || !_json.get_object().contains( "result" ) )
    return;
  auto _result = _json.get_object()[ "result" ];

  if( !_result.is_object() || !_result.get_object().contains( "block" ) )
    return;
  auto _block = _result.get_object()[ "block" ];

  if( !_block.is_object() || !_block.get_object().contains( "transactions" ) )
    return;
  auto _trxs = _block.get_object()[ "transactions" ];

  if( !_trxs.is_array() )
    return;

  auto __trxs = _trxs.as< std::vector< fc::variant > >();

  for( auto& trx : __trxs )
  {
    if( !trx.is_object() || !trx.get_object().contains( "operations" ) )
      continue;
    auto _ops = trx[ "operations" ];

    if( !_ops.is_array() )
      continue;

    auto __ops = _ops.as< std::vector< fc::variant > >();
    for( auto& op : __ops )
    {
      hive::protocol::operation _current_op;
      std::string _current_str_op;

      fc::from_variant( op, _current_op );
      _current_str_op = fc::json::to_string( op );

      ops.emplace( std::make_pair( _current_str_op, _current_op ) );
    }
  }
}

void filter_processor::cmp()
{
  for( auto& op : ops )
  {
    if( filter.is_tracked_operation( op.second ) )
      result_ops.insert( op );
  }
}

void filter_processor::summary( bool verbose )
{
  auto _print = [verbose]( const collection& items )
  {
    uint32_t _cnt = 0;

    for( auto& item : items )
    {
      std::cout<< item.first << std::endl;
      ++_cnt;
    }

    if( verbose )
      std::cout<< "======================================" << std::endl;

    return _cnt;
  };

  if( verbose )
  {
    uint32_t _total = _print( ops );
    uint32_t _filtered = _print( result_ops );

    std::cout<< "total: " << _total << " filtered: " << _filtered << std::endl;
  }
  else
  {
    _print( result_ops );
  }
}

int main( int argc, char** argv, char** envp )
{
  namespace bpo = boost::program_options;
  try
  {
    bpo::options_description _options("Allowed _options");
    _options.add_options()("op-type", bpo::value<std::string>()->required(), "Type of operation that is filtered");
    _options.add_options()("op-body-regex", bpo::value<std::string>()->required(), "Regex used for filtering of body operations");
    _options.add_options()("ops-file", bpo::value<std::string>()->required(), "File with operations generated by `block_api.get_block`");
    _options.add_options()("verbose", bpo::bool_switch()->default_value(false), "Display all operations and summary");

    bpo::variables_map _options_map;
    bpo::store(bpo::command_line_parser(argc, argv).options(_options).run(), _options_map);

    auto _try_arg = [&_options_map]( const std::string& arg_name )
    {
      if( !_options_map.count( arg_name ) )
      {
        std::cerr << "Error: missing parameter for "<<arg_name<< std::endl;
        return false;
      }
      return true;
    };

    if( !_try_arg("op-type") )         return 0;
    if( !_try_arg("op-body-regex") ) return 0;
    if( !_try_arg("ops-file") )        return 0;

    auto _verbose = _options_map["verbose"].as<bool>();

    filter_processor fp(  _options_map["op-type"].as<std::string>(),
                          _options_map["op-body-regex"].as<std::string>(),
                          _options_map["ops-file"].as<std::string>() );

    fp.cmp();
    fp.summary( _verbose );
  }
  catch ( const std::exception& e )
  {
    edump( ( std::string( e.what() ) ) );
    return EXIT_FAILURE;
  }
  catch (...)
  {
    return EXIT_FAILURE;
  }

  return EXIT_SUCCESS;
}
