/* syncfile.c -- 
   Copyright (C) 2008  Casey Marshall.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.

This program 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 Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.  */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sync.h"
#include "base64.h"
#include "bstrlib.h"

static const char *
mybasename (const char *path)
{
  char *ret = strrchr (path, '/');
  if (ret == NULL)
	return path;
  return ret + 1;
}

int
main (int argc, char **argv)
{
  sync_callbacks_t sync_cb;
  store_state_t *store;
  filer_state_t filer;
  file_t basis, newfile;
  extern int STORE_DEBUG;
  char *env;
  
  env = getenv("STORE_DEBUG");
  if (env != NULL)
    STORE_DEBUG = atoi(env);

  if (argc != 2)
	{
	  fprintf (stderr, "expecting an argument\n");
	  fprintf (stderr, "usage: %s arrow-root\n", argv[0]);
	  exit(1);
	}

  if (store_init (argv[1], &store) != 0)
	{
	  perror ("store_init");
	  exit(1);
	}

  if (filer_init (&filer, argv[1]) != 0)
	{
	  perror ("filer_init");
	  exit(1);
	}

  sync_cb.add_ref = sync_store_add_ref;
  sync_cb.put_block = sync_store_put_block;
  sync_cb.store_contains = sync_store_contains;
  sync_cb.state = store;

  char buf[2048];

  while (1)
    {
      char *p;
      char *newpath, *basispath;

      newpath = NULL;
      basispath = NULL;

      p = fgets (buf, 2048, stdin);

      if (p == NULL)
        {
          fprintf (stderr, "end of file on stdin; exiting\n");
          break;
        }

      if (p[strlen(p)-1] == '\n')
        p[strlen(p)-1] = '\0';
/*       fprintf (stderr, "read line: '%s'\n", p); */

      enum { NONE, ADD, SYNC } cmd = NONE;
      bstring bp = bfromcstr (p);
      struct bstrList *l = bsplit (bp, ' ');
      if (l->qty == 2 && biseqcstr (l->entry[0], "add"))
        {
          cmd = ADD;
          newpath = bstr2cstr (l->entry[1], '\0');
/*           fprintf (stderr, "will add file: %s\n", newpath); */
        }
      else if (l->qty == 3 && biseqcstr (l->entry[0], "sync"))
        {
          cmd = SYNC;
          newpath = bstr2cstr (l->entry[1], '\0');
          basispath = bstr2cstr (l->entry[2], '\0');
/*           fprintf (stderr, "will sync file: %s %s\n", newpath, basispath); */
        }

      int emit_this_file = 1;
      FILE *infile;

      if (cmd != NONE)
        {
          infile = fopen (newpath, "r");
          if (infile == NULL)
            {
              perror (newpath);
              exit (1);
            }
        }
      if (cmd == ADD)
        {
          uuid_generate (&newfile.uuid);
          if (file_open (&filer, &newfile, 1) != 0)
            {
              perror ("file_open");
              exit (1);
            }
          file_initialize (&newfile, newpath, infile);

          if (sync_generate (&newfile, infile, &sync_cb) != 0)
            {
              perror ("sync_generate");
              exit (1);
            }
        }
      else if (cmd == SYNC)
        {
          uint64_t upper, lower;
          char *upperstr = strdup (mybasename(basispath));
          char *lowerstr = strchr(upperstr, '.');
          int hash_match = 1;
          if (lowerstr == NULL)
            {
              fprintf (stderr, "bad file name: %s\n", upperstr);
              exit(1);
            }
          *lowerstr = '\0';
          lowerstr++;

          if (b64_decode (upperstr, &upper) != 0)
            {
              fprintf (stderr, "bad name: %s\n", upperstr);
              exit (1);
            }
          if (b64_decode (lowerstr, &lower) != 0)
            {
              fprintf (stderr, "bad name: %s\n", lowerstr);
              exit (1);
            }

          uuid_from_longs(&basis.uuid, upper, lower);

          if (file_open (&filer, &basis, 0) != 0)
            {
              perror (basispath);
              exit (1);
            }

          uuid_generate (&newfile.uuid);
          if (file_open (&filer, &newfile, 1) != 0)
            {
              perror ("file_open");
              exit (1);
            }
          file_initialize (&newfile, newpath, infile);

          hash_match = 1;
          if (sync_file (&basis, &newfile, infile, &sync_cb, &hash_match) != 0)
            {
              perror ("sync_file");
              exit (1);
            }

          /* Don't emit the file if it's the same. */
          if (hash_match)
            emit_this_file = 0;

          file_close (&filer, &basis);
        }

      if (newpath)
        free (newpath);
      if (basispath)
        free (basispath);
      bdestroy (bp);
      bstrListDestroy (l);

      if (cmd != NONE)
        {
          if (emit_this_file)
            {
              file_print_path (stdout, &filer, &newfile);
            }
          else
            file_delete (&filer, &newfile);

          fclose (infile);
          file_close (&filer, &newfile);
        }
      putchar ('\n');
      fflush (stdout);
    }

  store_destroy (store);
  
  exit (0);
}
