// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2020 Red Hat, Inc.

#include <cstring>

#include "abg-fwd.h"
#include "abg-internal.h"
// <headers defining libabigail's API go under here>
ABG_BEGIN_EXPORT_DECLARATIONS

#include "abg-diff-utils.h"

ABG_END_EXPORT_DECLARATIONS
// </headers defining libabigail's API>

/// @file
///
/// This file defines the declarations found in abg-diff-utils.h
namespace abigail
{

namespace diff_utils
{
/// @return true iff the end of a furthest forward d_path overlaps the
/// end of a furtherst reverse d_path on the same diagonal.  This is
/// defined by the lemma 3 of the paper.
///
/// @param forward_d_path_end
bool
ends_of_furthest_d_paths_overlap(const point& forward_d_path_end,
				 const point& reverse_d_path_end)
{
  return ((forward_d_path_end.x() - forward_d_path_end.y())
	  == (reverse_d_path_end.x() - reverse_d_path_end.y())
	  && forward_d_path_end.x() >= reverse_d_path_end.x());
}
//// Test if a point is within the boundaries of the edit graph.
///
/// @param p the point to test.
///
/// @param a_size the size of abscissas.  That is, the size of the
/// first input to consider.
///
/// @param b_size the of ordinates.  That is, the size of the second
/// input to consider.
///
/// @return true iff a point is within the boundaries of the edit
/// graph.
bool
point_is_valid_in_graph(point& p,
			unsigned a_size,
			unsigned b_size)
{
  return (p.x() > -1
	  && p.x() < (int) a_size
	  && p.y() > -1
	  && p.y() < (int) b_size);
}

/// Get the end points of the snake, as intended by the paper.
///
/// @param s the snake to consider.
///
/// @param x the starting point of the snake.
///
/// @param u the end point of the snake.
bool
snake_end_points(const snake& s, point& x, point& u)
{
  if (s.is_empty())
    return false;

  if (s.is_forward())
    {
      x = s.intermediate();
      u = s.end();
    }
  else
    {
      x = s.end();
      u = s.intermediate();
    }
  return true;
}

/// Returns the middle snake of two strings, as well as the length of
/// their shortest editing script.
///
/// @param str1 the first string to consider.
///
/// @param str2 the second string to consider.
///
/// @param snake_begin the point representing the beginning
bool
compute_middle_snake(const char* str1, const char* str2,
		     snake& s, int& ses_len)
{
  bool has_snake = false;
  int str1_size = strlen(str1), str2_size = strlen(str2);

  if (compute_middle_snake<const char*,
      default_eq_functor>(str1, str1 + str1_size,
			  str2 , str2 + str2_size,
			  s, ses_len))
    has_snake = true;

  return has_snake;
}

/// Compute the length of the shortest edit script for two strings.
/// This is done using the "Greedy LCS/SES" of figure 2 in the paper.
/// It can walk the edit graph either foward (when reverse is false)
/// or backward starting from the end (when reverse is true).
///
/// @param str1 the first string to consider.
///
/// @param str2 the second string to consider.
///
/// @param reverse when true, walk the edit graph backward, starting
/// from the point (M,N) (with M and N being the length of str1 and
/// str2).  When false, walk the edit graph forward, starting from the
/// point (0,0).
///
/// @return the length of the shortest edit script.
int
ses_len(const char* str1,
	const char* str2,
	bool reverse)
{
  int str1_size = strlen(str1), str2_size = strlen(str2);

  d_path_vec v(str1_size, str2_size);
  return ses_len(str1, str1 + str1_size,
		 str2, str2 + str2_size,
		 v, reverse);
}

/// Compute the longest common subsequence of two strings and return
/// the length of the shortest edit script for transforming the first
/// string into the second.
///
/// @param str1 the first string to consider.
///
/// @param str2 the second string to consider.
///
/// @param ses_len the length of the shortest edit script.  This is
/// set by the function iff it returns true.
///
/// @param the longest common subsequence of the two strings.  This is
/// set the function iff it returns true.
///
/// @return true if the function could compute an lcs, false
/// otherwise.
void
compute_lcs(const char* str1, const char* str2, int &ses_len, string& lcs)
{
  vector<point> result;
  edit_script ses;

  compute_diff(str1, str1 + strlen(str1),
	       str2, str2 + strlen(str2),
	       result, ses);

  ses_len = ses.length();

  for (unsigned i = 0; i < result.size(); ++i)
    {
      int x = result[i].x(), y = result[i].y();
      ABG_ASSERT(str1[x] == str2[y]);
      lcs.push_back(str1[x]);
    }
}

/// Compute the shortest edit script for transforming a string into
/// another.
///
/// @param str1 the first string to consider.
///
/// @param str2 the second string to consider.
///
/// @param ses the resulting edit script.
///
/// @pram ses_len the length of the edit script.
void
compute_ses(const char* str1, const char* str2, edit_script& ses)
{
  vector<point> lcs;
  compute_diff(str1, str1 + strlen(str1),
	       str2, str2 + strlen(str2),
	       lcs, ses);
}

}//end namespace diff_utils

}//end namespace abigail