// -*- Mode: C++ -*-

// Copyright (C) 2013-2020 Red Hat, Inc.
//
// This file is part of the GNU Application Binary Interface Generic
// Analysis and Instrumentation Library (libabigail).  This library is
// free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 3, or (at your option) any
// later version.

// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Lesser Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this program; see the file COPYING-LGPLV3.  If
// not, see <http://www.gnu.org/licenses/>.
//
// Author: Dodji Seketeli
//

/// @file
///
/// This file implements a simple command line utility for
/// interactively testing the diff2 algorithms declared and defined in
/// abg-diff-utils.{h,cc}
///
/// The resulting binary name is testdiff2.  Run it to see a help message
/// showing you how to use it.

#include <cstring>
#include <iostream>
#include "abg-diff-utils.h"

using std::cout;
using std::string;

using abigail::diff_utils::ses_len;
using abigail::diff_utils::point;
using abigail::diff_utils::snake;
using abigail::diff_utils::compute_middle_snake;
using abigail::diff_utils::print_snake;
using abigail::diff_utils::compute_lcs;
using abigail::diff_utils::edit_script;
using abigail::diff_utils::compute_ses;
using abigail::diff_utils::display_edit_script;

struct options
{
  bool show_help;
  bool ses_len;
  bool reverse;
  bool middle_snake;
  bool lcs;
  bool ses;
  const char* str1;
  const char* str2;

  options()
    : show_help(false),
      ses_len(false),
      reverse(false),
      middle_snake(false),
      lcs(false),
      ses(false),
      str1(0),
      str2(0)
  {}
};// end struct options

static void
show_help(const string& progname)
{
   cout << "usage: " << progname << " [options] str1 str2\n"
	<< "where [options] can be:\n"
	<< "--seslen	print the length of the SES of the two strings\n"
	<< "--reverse	compute the d-paths in reverse order when applicable\n"
	<< "--middle-snake	display middle snake & length of SES\n"
	<< "--lcs	display the longest common subsequence\n"
	<< "--ses	display the shortest edit script transforming str1 into str2\n";
}

static void
parse_command_line(int argc, char* argv[], options &opts)
{
  if (argc < 3)
    {
      opts.show_help = true;
      return;
    }

  for (int i = 1; i < argc; ++i)
    {
      if (argv[i][0] != '-')
	{
	  if (!opts.str1)
	    opts.str1 = argv[i];
	  else if (!opts.str2)
	    opts.str2 = argv[i];
	  else
	    {
	      opts.show_help = true;
	      return;
	    }
	}
      else if (strcmp(argv[i], "--seslen") == 0)
	opts.ses_len = true;
      else if (strcmp(argv[i], "--reverse") == 0)
	opts.reverse = true;
      else if (strcmp(argv[i], "--middle-snake") == 0)
	opts.middle_snake = true;
      else if (strcmp(argv[i], "--lcs") == 0)
	opts.lcs = true;
      else if (strcmp(argv[i], "--ses") == 0)
	opts.ses = true;
      else
	opts.show_help = true;
    }
}

int
main(int argc, char*argv[])
{
  options opts;
  parse_command_line(argc, argv, opts);

  if (opts.show_help)
    {
      show_help(argv[0]);
      return -1;
    }

  if (opts.ses_len)
    {
      int len = ses_len(opts.str1, opts.str2, opts.reverse);
      cout << len << "\n";
      return 0;
    }

  if (opts.middle_snake)
    {
      int ses_len = 0;
      snake s;
      if (compute_middle_snake(opts.str1, opts.str2,
			       s, ses_len))
	{
	  print_snake(opts.str1, opts.str2, s, cout);
	  cout << "ses len: " << ses_len << "\n";
	}
      return 0;
    }

  if (opts.lcs)
    {
      string lcs;
      int ses_len = 0;
      compute_lcs(opts.str1, opts.str2, ses_len, lcs);
      cout << "lcs: " << lcs << "\n"
	   << "ses len: " << ses_len << "\n";
      return 0;
    }

  if (opts.ses)
    {
      edit_script ses;
      compute_ses(opts.str1, opts.str2, ses);
      display_edit_script(ses, opts.str1, opts.str2, cout);
      return 0;
    }

  return 0;
}
