/**
    merge XML chat history file(s) to a master XML file

    Copyright (c) 2020-2021 The Creators of Simphone

    See the file COPYING.LESSER.txt for copying permission.
**/

#include "logger.h"
#include "table.h"
#include "error.h"
#include "utils.h"
#include "file.h"
#include "msg.h"
#include "api.h"

#include <string.h>
#include <stdio.h>
#include <errno.h>

#ifndef _WIN32
#include <unistd.h>
#else
#define STDERR_FILENO 2
#endif

struct _list {
  struct _list *next;
  struct _message *msg;
};

static struct _list *sim_xmls_add (struct _list **head, struct _list *tail,
                                   struct _message *message, struct _list *next) {
  struct _list *item = sim_new (sizeof (*item));
  if (tail) {
    tail->next = item;
  } else
    *head = item;
  item->msg = message;
  item->next = next;
  return item;
}

static simtype sim_xmls_convert_msg (struct _message *message, unsigned *spaces) {
  unsigned len;
  simtype str, table;
  char buf[512];
  simunsigned datetime = message->sndtime;
  if (message->text.typ == SIMNIL)
    return nil ();
  message->text.len = strlen (message->text.ptr);
  if (message->tonick) {
    if ((datetime = sim_convert_string_to_time (message->tonick, strlen (message->tonick))) == 0) {
      datetime = message->sndtime;
    } else if ((unsigned) datetime != datetime)
      datetime = message->sndtime;
  }
  table = sim_msg_convert_to_xml (message, (unsigned) datetime, buf, spaces, &len);
  str = sim_convert_type_to_xml (table, MSG_HISTORY_ENTRY);
  table_free (table);
  return str;
}

int main (int argc, char **argv) {
  int err, i, fd, len;
  unsigned spaces;
  struct _list *head = NULL, *tail = NULL;
  char *s, buf[512];
  simtype table = nil (), key;
  if (argc < 2) {
    fprintf (stderr, "usage: %s <master>.html {<slave>.html} > <output>.html\n", argv[0]);
    return -1;
  }
  log_init_ ();
  if ((err = log_init_fd_ (STDERR_FILENO)) != SIM_OK) {
    fprintf (stderr, "log: %s (error %d)\n", sim_error_get (err), err);
    return -2;
  }
  log_set_level_ (NULL, SIM_LOG_DEBUG);
  for (i = 1; i < argc; i++) {
    unsigned n, t[2] = { 0, 0 };
    unsigned count = 0;
    struct _message *list;
    simtype msgs = nil ();
    if ((err = sim_file_load_history (argv[i], 1 << 30, 0, argv[i], &msgs, &count)) != SIM_FILE_START) {
      fprintf (stderr, "%s: %s (error %d)\n", argv[i], sim_error_get (err), err);
      return -3;
    }
    list = msgs.ptr;
    fprintf (stderr, "%s: loaded %u messages\n", argv[i], count);
    if (i == 1)
      table = table_new (count < 256 ? 256 : count);
    for (n = 1; n <= count; n++) {
      simbool incoming = list[n].status == SIM_MSG_INCOMING;
      if (list[n].sndtime + 120 < t[incoming])
        fprintf (stderr, "%s:%u #%" SIM_FORMAT_64 "u: time reversal (%u seconds)\n",
                 argv[i], n, list[n].handle, t[incoming] - list[n].sndtime);
      t[incoming] = list[n].sndtime;
      if (i == 1)
        tail = sim_xmls_add (&head, tail, &list[n], NULL);
      if (list[n].handle) {
        key = pointer_new_len (&list[n].handle, sizeof (list[n].handle));
      } else if (! (key = sim_xmls_convert_msg (&list[n], &spaces)).len)
        fprintf (stderr, "%s:%u invalid\n", argv[i], n);
      if (! key.len || table_add_key_number (table, key, 1).num) {
        if (key.len)
          fprintf (stderr, "%s:%u #%" SIM_FORMAT_64 "u: duplicate\n", argv[i], n, list[n].handle);
        t[1]++;
        list[n].index = 0;
      } else
        list[n].index = n;
    }
    if (i != 1) {
      struct _list *next = head, *last = NULL;
      t[0] = t[1] = 0;
      for (n = 1; n <= count; n++) {
        if (list[n].index) {
          while (next && next->msg->sndtime <= list[n].sndtime)
            next = (last = next)->next;
          next = sim_xmls_add (&head, last, &list[n], next);
        }
      }
      fprintf (stderr, "%s: %u merged, %u skipped\n", argv[i], t[0], t[1]);
    }
  }
  table_free (table);
  if ((fd = sim_file_open (argv[1], FILE_BIT_READ | FILE_BIT_TEXT)) < 0) {
    fprintf (stderr, "%s: %s (error %d)\n", argv[1], sim_error_get (errno), errno);
    return -3;
  }
  if ((len = sim_file_read (fd, buf, sizeof (buf) - 1)) < 0) {
    fprintf (stderr, "%s: %s (error %d)\n", argv[1], sim_error_get (errno), errno);
    return -3;
  }
  buf[len] = 0;
  if ((s = strstr (buf, MSG_XML_BODY)) != NULL) {
    s[SIM_STRING_GET_LENGTH_CONST (MSG_XML_BODY)] = 0;
    if (printf ("%s\n", buf) < 0)
      goto quit;
  }
  for (i = 0; head; head = head->next) {
    simtype str = sim_xmls_convert_msg (head->msg, &spaces);
    if (str.len) {
      while (spaces--)
        if (putc (' ', stdout) <= 0)
          goto quit;
      if (printf ("%s\n", str.str) <= 0)
        goto quit;
    }
    string_free (str);
    i++;
  }
  if (printf ("%s\n", MSG_XML_TAIL) < 0 || fflush (stdout)) {
  quit:
    fprintf (stderr, "output error: %s (error %d)\n", sim_error_get (errno), errno);
    return -4;
  }
  uninit_log ();
  fprintf (stderr, "%s: %u messages\n", argv[1], i);
  fflush (stderr);
  return 0;
}
