#include "apr.h"
#include "apr_general.h"
#include "apr_pools.h"
#include "svn_diff.h"

typedef struct file_diff_baton_t
{
  apr_pool_t *pool;
  const char **baseline;
  const char **workingcopy;
  const char **repository;
  int baseline_index;
  int workingcopy_index;
  int repository_index;
} file_diff_baton_t;

static
apr_status_t
datasource_open(void *baton, svn_diff_datasource_e datasource)
{
  file_diff_baton_t *fdb = baton;

  if (datasource == svn_diff_datasource_baseline)
    {
      fdb->baseline_index = 0;
    }

  if (datasource == svn_diff_datasource_workingcopy)
    {
      fdb->baseline_index = 0;
    }

  return APR_SUCCESS;
}

static
void
datasource_close(void *baton, svn_diff_datasource_e datasource)
{
}

static
void *
datasource_get_token(void *baton, svn_diff_datasource_e datasource)
{
  file_diff_baton_t *fdb = baton;
  void *token;

  if (datasource == svn_diff_datasource_baseline)
    {
      token = (void *)fdb->baseline[fdb->baseline_index];
      if (token != NULL)
        fdb->baseline_index++;
    }

  if (datasource == svn_diff_datasource_workingcopy)
    {
      token = (void *)fdb->workingcopy[fdb->workingcopy_index];
      if (token != NULL)
        fdb->workingcopy_index++;
    }

  if (datasource == svn_diff_datasource_repository)
    {
      token = (void *)fdb->repository[fdb->repository_index];
      if (token != NULL)
        fdb->repository_index++;
    }
  
  return token;
}

static
int
token_compare(void *baton, void *ltoken, void *rtoken)
{
  return strcmp((const char *)ltoken, (const char *)rtoken);
}

static const char *file_bl[] =
{
  "Line 1",
  "Line 2",
  "Line 3",
  "Line 4 Deleted",
  "Line 5",
  "Line 6",
  "Line 7",
  "Line 8",
  "Line 9",
  "Line 10",
  NULL
};

static const char *file_wc[] =
{
  "Line 1",
  "Line 2 Modified",
  "Line 3",
  "Line 5",
  "Line 6",
  "Line 7",
  "Line 7a Inserted",
  "Line 8",
  "Line 9 Common diff",
  "Line 10",
  "Line 11 Appended",
  NULL
};

static const char *file_rp[] =
{
  "Line 1",
  "Line 2 Conflict",
  "Line 3",
  "Line 4 Deleted",
  "Line 5",
  "Line 6 Modified in repos",
  "Line 7",
  "Line 8",
  "Line 9 Common diff",
  "Line 10",
  NULL
};

static
void
output_common(void *baton,
              apr_off_t baseline_start,
              apr_off_t baseline_length,
              apr_off_t workingcopy_start,
              apr_off_t workingcopy_length,
              apr_off_t repository_start,
              apr_off_t repository_length)
{
    while (baseline_length > 0)
    {
      printf(" %s\n", file_bl[baseline_start++]);
      baseline_length--;
    }
}

static
void
output_diff_workingcopy(void *baton,
                        apr_off_t baseline_start,
                        apr_off_t baseline_length,
                        apr_off_t workingcopy_start,
                        apr_off_t workingcopy_length,
                        apr_off_t repository_start,
                        apr_off_t repository_length)
{
  while (baseline_length > 0)
    {
      printf("-%s\n", file_bl[baseline_start++]);
      baseline_length--;
    }

  while (workingcopy_length > 0)
    {
      printf("+%s\n", file_wc[workingcopy_start++]);
      workingcopy_length--;
    }
}

static
void
output_diff_repository(void *baton,
                       apr_off_t baseline_start,
                       apr_off_t baseline_length,
                       apr_off_t workingcopy_start,
                       apr_off_t workingcopy_length,
                       apr_off_t repository_start,
                       apr_off_t repository_length)
{
  while (baseline_length > 0)
    {
      printf("-%s\n", file_bl[baseline_start++]);
      baseline_length--;
    }

  while (repository_length > 0)
    {
      printf("+%s\n", file_rp[repository_start++]);
      repository_length--;
    }
}

static
void
output_conflict(void *baton,
                apr_off_t baseline_start,
                apr_off_t baseline_length,
                apr_off_t workingcopy_start,
                apr_off_t workingcopy_length,
                apr_off_t repository_start,
                apr_off_t repository_length)
{
  printf("<<<< baseline\n");
  while (baseline_length > 0)
    {
      printf("-%s\n", file_bl[baseline_start++]);
      baseline_length--;
    }

  printf("|||| workingcopy\n");
  while (workingcopy_length > 0)
    {
      printf("+%s\n", file_wc[workingcopy_start++]);
      workingcopy_length--;
    }
  
  printf("|||| repository\n");
  while (repository_length > 0)
    {
      printf("+%s\n", file_rp[repository_start++]);
      repository_length--;
    }
  printf(">>>>\n");
}

static
void
test_diff2(apr_pool_t *pool)
{
  svn_diff_t *diff;
  svn_diff_fns_t vtable;
  file_diff_baton_t baton;

  vtable.datasource_open = datasource_open;
  vtable.datasource_close = datasource_close;
  vtable.datasource_seek = NULL;
  vtable.datasource_get_token = datasource_get_token;
  vtable.token_compare = token_compare;
  vtable.token_discard = NULL;
  vtable.token_discard_all = NULL;

  baton.pool = pool;
  baton.baseline = file_bl;
  baton.baseline_index = 0;
  baton.workingcopy = file_wc;
  baton.workingcopy_index = 0;
  baton.repository = NULL;
  baton.repository_index = 0;

  svn_diff(&diff, &baton, &vtable, pool);

  /* now output the diff */
  {
    svn_diff_output_fns_t output_vtable;

    output_vtable.output_common = output_common;
    output_vtable.output_diff_workingcopy = output_diff_workingcopy;

    svn_diff_output(diff, NULL, &output_vtable);
  }
}

static
void
test_diff3(apr_pool_t *pool)
{
  svn_diff_t *diff;
  svn_diff_fns_t vtable;
  file_diff_baton_t baton;

  vtable.datasource_open = datasource_open;
  vtable.datasource_close = datasource_close;
  vtable.datasource_seek = NULL;
  vtable.datasource_get_token = datasource_get_token;
  vtable.token_compare = token_compare;
  vtable.token_discard = NULL;
  vtable.token_discard_all = NULL;

  baton.pool = pool;
  baton.baseline = file_bl;
  baton.baseline_index = 0;
  baton.workingcopy = file_wc;
  baton.workingcopy_index = 0;
  baton.repository = file_rp;
  baton.repository_index = 0;

  svn_diff3(&diff, &baton, &vtable, pool);

  /* now output the diff */
  {
    svn_diff_output_fns_t output_vtable;

    output_vtable.output_common = output_common;
    output_vtable.output_diff_workingcopy = output_diff_workingcopy;
    output_vtable.output_diff_repository = output_diff_repository;
    output_vtable.output_diff_common = output_diff_workingcopy;
    output_vtable.output_conflict = output_conflict;

    svn_diff_output(diff, NULL, &output_vtable);
  }
}

int main(int argc, char *argv[])
{
  apr_pool_t *pool;

  apr_initialize();

  apr_pool_create(&pool, NULL);

  test_diff2(pool);

  printf("\n");
  
  test_diff3(pool);

  printf("\n");

  apr_terminate();

  return 0;
}




