/*  =========================================================================
    zdir_patch - work with directory patches
    A patch is a change to the directory (create/delete).

    Copyright (c) the Contributors as noted in the AUTHORS file.
    This file is part of CZMQ, the high-level C binding for 0MQ:
    http://czmq.zeromq.org.

    This Source Code Form is subject to the terms of the Mozilla Public
    License, v. 2.0. If a copy of the MPL was not distributed with this
    file, You can obtain one at http://mozilla.org/MPL/2.0/.
    =========================================================================
*/

/*
@header
    The zdir_patch class works with one patch, which says "create this
    file" or "delete this file" (referring to a zfile item each time).
@discuss
@end
*/

#include "czmq_classes.h"

//  Structure of our class
//  If you modify this beware to also change _dup

struct _zdir_patch_t {
    char *path;                 //  Directory path
    char *vpath;                //  Virtual file path
    zfile_t *file;              //  File we refer to
    int op;                     //  Operation
    char *digest;               //  File SHA-1 digest
};


//  --------------------------------------------------------------------------
//  Constructor
//  Create new patch, create virtual path from alias

zdir_patch_t *
zdir_patch_new (const char *path, zfile_t *file, int op, const char *alias)
{
    zdir_patch_t *self = (zdir_patch_t *) zmalloc (sizeof (zdir_patch_t));
    assert (self);
    self->path = strdup (path);
    assert (self->path);
    self->file = zfile_dup (file);
    assert (self->file);
    self->op = op;

    //  Calculate virtual path for patch (remove path, prefix alias)
    const char *filename = zfile_filename (file, path);
    assert (filename);
    assert (*filename != '/');

    self->vpath = (char *) zmalloc (strlen (alias) + strlen (filename) + 2);
    assert (self->vpath);

    if (strlen (alias) && alias [strlen (alias) - 1] == '/')
        sprintf (self->vpath, "%s%s", alias, filename);
    else
        sprintf (self->vpath, "%s/%s", alias, filename);
    return self;
}


//  --------------------------------------------------------------------------
//  Destroy a patch

void
zdir_patch_destroy (zdir_patch_t **self_p)
{
    assert (self_p);
    if (*self_p) {
        zdir_patch_t *self = *self_p;
        freen (self->path);
        freen (self->vpath);
        freen (self->digest);
        zfile_destroy (&self->file);
        freen (self);
        *self_p = NULL;
    }
}


//  --------------------------------------------------------------------------
//  Create copy of a patch. If the patch is null, or memory was exhausted,
//  returns null.

zdir_patch_t *
zdir_patch_dup (zdir_patch_t *self)
{
    if (self) {
        zdir_patch_t *copy = (zdir_patch_t *) zmalloc (sizeof (zdir_patch_t));
        if (copy) {
            copy->op = self->op;
            copy->path = strdup (self->path);
            if (copy->path)
                copy->file = zfile_dup (self->file);
            if (copy->file)
                copy->vpath = strdup (self->vpath);
            if (copy->vpath)
                //  Don't recalculate hash when we duplicate patch
                copy->digest = self->digest? strdup (self->digest): NULL;

            if (copy->digest == NULL && copy->op != patch_delete)
                zdir_patch_destroy (&copy);
        }
        return copy;
    }
    else
        return NULL;
}


//  --------------------------------------------------------------------------
//  Return patch file directory path

const char *
zdir_patch_path (zdir_patch_t *self)
{
    assert (self);
    return self->path;
}


//  --------------------------------------------------------------------------
//  Return patch file item

zfile_t *
zdir_patch_file (zdir_patch_t *self)
{
    assert (self);
    return self->file;
}


//  --------------------------------------------------------------------------
//  Return operation

int
zdir_patch_op (zdir_patch_t *self)
{
    assert (self);
    return self->op;
}


//  --------------------------------------------------------------------------
//  Return patch virtual file path

const char *
zdir_patch_vpath (zdir_patch_t *self)
{
    assert (self);
    return self->vpath;
}


//  --------------------------------------------------------------------------
//  Calculate hash digest for file (create only)

void
zdir_patch_digest_set (zdir_patch_t *self)
{
    if (self->op == patch_create
    &&  self->digest == NULL) {
        self->digest = strdup (zfile_digest (self->file));
        assert (self->digest);
    }

}


//  --------------------------------------------------------------------------
//  Return hash digest for patch file (create only)

const char *
zdir_patch_digest (zdir_patch_t *self)
{
    assert (self);
    return self->digest;
}


//  --------------------------------------------------------------------------
//  Self test of this class

void
zdir_patch_test (bool verbose)
{
    printf (" * zdir_patch: ");

    //  @selftest

    const char *SELFTEST_DIR_RW = "src/selftest-rw";

    const char *testfile = "bilbo";
    const char *prefix   = "/";
    char *prefixed_testfile = zsys_sprintf ("%s%s", prefix, testfile);
    assert (prefixed_testfile);

    // Make sure old aborted tests do not hinder us
    zsys_file_delete (prefixed_testfile);

    zfile_t *file = zfile_new (SELFTEST_DIR_RW, testfile);
    assert (file);
    zdir_patch_t *patch = zdir_patch_new (SELFTEST_DIR_RW, file, patch_create, prefix);
    assert (patch);
    zfile_destroy (&file);

    file = zdir_patch_file (patch);
    assert (file);
    assert (streq (zfile_filename (file, SELFTEST_DIR_RW), testfile));
    assert (streq (zdir_patch_vpath (patch), prefixed_testfile));
    zdir_patch_destroy (&patch);

    zstr_free (&prefixed_testfile);

#if defined (__WINDOWS__)
    zsys_shutdown();
#endif
    //  @end

    printf ("OK\n");
}
