// Copyright (C) 2016, 2017, 2018, 2019  Stefan Vargyas
// 
// This file is part of Json-Type.
// 
// Json-Type 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.
// 
// Json-Type 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 Json-Type.  If not, see <http://www.gnu.org/licenses/>.

#ifndef VECTOR_NAME
#error  VECTOR_NAME is not defined
#endif

#ifndef VECTOR_ELEM_TYPE
#error  VECTOR_ELEM_TYPE is not defined
#endif

#define VECTOR_MAKE_NAME_(n, s) n ## _vector_ ## s
#define VECTOR_MAKE_NAME(n, s)  VECTOR_MAKE_NAME_(n, s)

#define VECTOR_TYPE    VECTOR_MAKE_NAME(VECTOR_NAME, t)
#define VECTOR_INIT    VECTOR_MAKE_NAME(VECTOR_NAME, init)
#define VECTOR_DONE    VECTOR_MAKE_NAME(VECTOR_NAME, done)
#define VECTOR_CLEAR   VECTOR_MAKE_NAME(VECTOR_NAME, clear)
#define VECTOR_ENLARGE VECTOR_MAKE_NAME(VECTOR_NAME, enlarge)
#define VECTOR_ROTATE_ELEM \
                       VECTOR_MAKE_NAME(VECTOR_NAME, rotate_elem)

struct VECTOR_TYPE
{
    VECTOR_ELEM_TYPE* elems;
#ifndef VECTOR_NEED_FIXED_SIZE
    size_t max_size;
#endif
    size_t n_elems;
    size_t size;
};

static void VECTOR_INIT(
    struct VECTOR_TYPE* vect,
#ifndef VECTOR_NEED_FIXED_SIZE
    size_t max_size,
#endif
    size_t init_size)
{
    size_t n = sizeof(*vect->elems);

#ifndef VECTOR_NEED_FIXED_SIZE
    if (max_size == 0)
        max_size = 128;

    if (init_size == 0 ||
        init_size > max_size)
        init_size = max_size;
#else
    if (init_size == 0)
        init_size = 128;
#endif

    memset(vect, 0, sizeof(*vect));

    SIZE_MUL_EQ(n, init_size);
    vect->elems = malloc(n);
    VERIFY(vect->elems != NULL);

    memset(vect->elems, 0, n);

#ifndef VECTOR_NEED_FIXED_SIZE
    vect->max_size = max_size;
#endif
    vect->size = init_size;
}

static void VECTOR_DONE(
    struct VECTOR_TYPE* vect)
{
    free(vect->elems);
}

#ifndef VECTOR_NEED_FIXED_SIZE
#undef  VECTOR_ASSERT_INVARIANTS
#define VECTOR_ASSERT_INVARIANTS(vector)              \
    do {                                              \
        ASSERT((vector)->size > 0);                   \
        ASSERT((vector)->max_size > 0);               \
        ASSERT((vector)->size >= (vector)->n_elems);  \
        ASSERT((vector)->max_size >= (vector)->size); \
    } while (0)
#else
#undef  VECTOR_ASSERT_INVARIANTS
#define VECTOR_ASSERT_INVARIANTS(vector)              \
    do {                                              \
        ASSERT((vector)->size > 0);                   \
        ASSERT((vector)->size >= (vector)->n_elems);  \
    } while (0)
#endif // VECTOR_NEED_FIXED_SIZE

#ifdef VECTOR_NEED_CLEAR

static void VECTOR_CLEAR(
    struct VECTOR_TYPE* vect)
{
    size_t n = sizeof(*vect->elems);

    VECTOR_ASSERT_INVARIANTS(vect);

    SIZE_MUL_EQ(n, vect->size);
    memset(vect->elems, 0, n);
    vect->n_elems = 0;
}

#endif // VECTOR_NEED_CLEAR

#ifndef VECTOR_NEED_FIXED_SIZE

static void VECTOR_ENLARGE(
    struct VECTOR_TYPE* vect)
{
    const size_t n = sizeof(*vect->elems);
    size_t s;

    VECTOR_ASSERT_INVARIANTS(vect);

    ENSURE(vect->size < vect->max_size,
        "vector reached maximum size");

    // stev: the assertions above imply:
    //   0 < s < max_size
    s = vect->size;

    SIZE_MUL_EQ(vect->size, SZ(2));

    if (vect->size > vect->max_size)
        vect->size = vect->max_size;
    // then:
    //   (a) 2 * s > max_size => s < size = max_size
    //   (b) 2 * s < max_size => s < size = 2 * s <= max_size
    // hence either way:
    //   s < size <= max_size
    ASSERT(s < vect->size);

    // stev: the double inequality above shows that
    // the new 'size' will increase strictly over
    // the current size 's', while not exceeding the
    // upper-bound 'max_size'!
    vect->elems = realloc(vect->elems, SIZE_MUL(vect->size, n));
    ENSURE(vect->elems != NULL, "realloc failed");

    memset(vect->elems + s, 0, SIZE_MUL(vect->size - s, n));
}

#undef  VECTOR_ENSURE_APPEND_SIZE
#define VECTOR_ENSURE_APPEND_SIZE(vector)            \
    do {                                             \
        if ((vector)->n_elems >= (vector)->size)     \
            VECTOR_ENLARGE(vector);                  \
        ASSERT((vector)->n_elems < (vector)->size);  \
    } while (0)
#else  // VECTOR_NEED_FIXED_SIZE
#undef  VECTOR_ENSURE_APPEND_SIZE
#define VECTOR_ENSURE_APPEND_SIZE(vector)            \
    ENSURE((vector)->n_elems < (vector)->size,       \
        "vector capacity exceeded")
#endif // VECTOR_NEED_FIXED_SIZE

#ifdef VECTOR_NEED_ROTATE_ELEM

static void VECTOR_ROTATE_ELEM(
    struct VECTOR_TYPE* vect,
    size_t index)
{
    const size_t s =
        sizeof(VECTOR_ELEM_TYPE);
    VECTOR_ELEM_TYPE *p, e;
    size_t l;

    VECTOR_ASSERT_INVARIANTS(vect);
    ASSERT(vect->n_elems > 0);

    l = vect->n_elems - 1;
    ASSERT(index <= l);

    if (l == 0 || l == index)
        return;

    e = *(p = &vect->elems[index]);

    ASSERT_SIZE_MUL_NO_OVERFLOW(l - index, s);
    memmove(p, p + 1, (l - index) * s);

    vect->elems[l] = e;
}

#endif // VECTOR_NEED_ROTATE_ELEM

#ifndef __VECTOR_IMPL_H
#define __VECTOR_IMPL_H

#define TYPEOF_IS_VECTOR_(q, x) \
    TYPEOF_IS(x, q struct VECTOR_TYPE*)
#define TYPEOF_IS_VECTOR_CONST(x) \
        TYPEOF_IS_VECTOR_(const, x)
#define TYPEOF_IS_VECTOR(x) \
        TYPEOF_IS_VECTOR_(, x)

#define VECTOR_N_ELEMS_(q, vector)                   \
    ({                                               \
        STATIC(TYPEOF_IS_VECTOR_(q, vector));        \
        (vector)->n_elems;                           \
    })
#define VECTOR_N_ELEMS_CONST(vector)                 \
        VECTOR_N_ELEMS_(const, vector)
#define VECTOR_N_ELEMS(vector)                       \
        VECTOR_N_ELEMS_(, vector)

#define VECTOR_APPEND_(vector)                       \
    ({                                               \
        STATIC(TYPEOF_IS_VECTOR(vector));            \
        VECTOR_ASSERT_INVARIANTS(vector);            \
        VECTOR_ENSURE_APPEND_SIZE(vector);           \
    })
#define VECTOR_APPEND_REF(vector)                    \
    ({                                               \
        VECTOR_APPEND_(vector);                      \
        &(vector)->elems[(vector)->n_elems ++];      \
    })
#define VECTOR_APPEND(vector, v)                     \
    ({                                               \
        VECTOR_APPEND_(vector);                      \
        (vector)->elems[(vector)->n_elems] = (v);    \
        (vector)->n_elems ++;                        \
    })

#define VECTOR_ELEM_REF_(q, vector, k)               \
    ({                                               \
        STATIC(TYPEOF_IS_SIZET(k));                  \
        STATIC(TYPEOF_IS_VECTOR_(q, vector));        \
        ASSERT((k) < (vector)->n_elems);             \
        &(vector)->elems[k];                         \
    })
#define VECTOR_ELEM_REF_CONST(vector, k)             \
        VECTOR_ELEM_REF_(const, vector, k)
#define VECTOR_ELEM_REF(vector, k)                   \
        VECTOR_ELEM_REF_(, vector, k)

#define VECTOR_ELEM_(q, vector, k)                   \
    ({                                               \
        STATIC(TYPEOF_IS_SIZET(k));                  \
        STATIC(TYPEOF_IS_VECTOR_(q, vector));        \
        ASSERT((k) < (vector)->n_elems);             \
        (vector)->elems[k];                          \
    })
#define VECTOR_ELEM_CONST(vector, k)                 \
        VECTOR_ELEM_(const, vector, k)
#define VECTOR_ELEM(vector, k)                       \
        VECTOR_ELEM_(, vector, k)

#define VECTOR_BACK_ELEM_REF_(q, vector)             \
    ({                                               \
        STATIC(TYPEOF_IS_VECTOR_(q, vector));        \
        ASSERT((vector)->n_elems > 0);               \
        &(vector)->elems[(vector)->n_elems - 1];     \
    })
#define VECTOR_BACK_ELEM_REF_CONST(vector)           \
        VECTOR_BACK_ELEM_REF_(const, vector)
#define VECTOR_BACK_ELEM_REF(vector)                 \
        VECTOR_BACK_ELEM_REF_(, vector)

#define VECTOR_BACK_ELEM_(q, vector)                 \
    ({                                               \
        STATIC(TYPEOF_IS_VECTOR_(q, vector));        \
        ASSERT((vector)->n_elems > 0);               \
        (vector)->elems[(vector)->n_elems - 1];      \
    })
#define VECTOR_BACK_ELEM_CONST(vector)               \
        VECTOR_BACK_ELEM_(const, vector)
#define VECTOR_BACK_ELEM(vector)                     \
        VECTOR_BACK_ELEM_(, vector)

#endif // __VECTOR_IMPL_H


