/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#ifdef WIN32
#define random rand /* replace POSIX random with Windows rand */
#include <winsock2.h> /* must always be included before ws2tcpip.h */
#include <ws2tcpip.h> /* for sockaddr_storage */
#include "winport.h"
#endif

#include "addrvec.h"

#define ADDRVEC_DEFAULT_GROW_AMOUNT 16

void addrvec_init(addrvec_t *avec)
{
    assert(avec);
    avec->next = 0;
    avec->count = 0;
    avec->capacity = 0;
    avec->data = NULL;
}

void addrvec_free(addrvec_t *avec)
{
    if (avec == NULL)
    {
        return;
    }

    avec->next = 0;
    avec->count = 0;
    avec->capacity = 0;
    if (avec->data) {
        free(avec->data);
        avec->data = NULL;
    }
}

int addrvec_alloc(addrvec_t *avec)
{
    addrvec_init(avec);
    return addrvec_grow_default(avec);
}

int addrvec_alloc_capacity(addrvec_t* avec, uint32_t capacity)
{
    addrvec_init(avec);
    return addrvec_grow(avec, capacity);
}

int addrvec_grow(addrvec_t *avec, uint32_t grow_amount)
{
    unsigned int old_capacity = 0;
    struct sockaddr_storage *old_data = NULL;
    assert(avec);

    if (grow_amount == 0)
    {
        return 0;
    }

    // Save off old data and capacity in case there is a realloc failure
    old_capacity = avec->capacity;
    old_data = avec->data;

    avec->capacity += grow_amount;
    avec->data = realloc(avec->data, sizeof(*avec->data) * avec->capacity);
    if (avec->data == NULL)
    {
        avec->capacity = old_capacity;
        avec->data = old_data;
        errno = ENOMEM;
        return 1;
    }

    return 0;
}

int addrvec_grow_default(addrvec_t *avec)
{
    return addrvec_grow(avec, ADDRVEC_DEFAULT_GROW_AMOUNT);
}

static int addrvec_grow_if_full(addrvec_t *avec)
{
    assert(avec);
    if (avec->count == avec->capacity)
    {
        int rc = addrvec_grow_default(avec);
        if (rc != 0)
        {
            return rc;
        }
    }

    return 0;
}

int addrvec_contains(const addrvec_t *avec, const struct sockaddr_storage *addr)
{
    uint32_t i = 0;
    if (!avec || !addr)
    { 
        return 0;
    }

    for (i = 0; i < avec->count; i++)
    {
        if (avec->data[i].ss_family != addr->ss_family)
            continue;
        switch (addr->ss_family) {
        case AF_INET:
            if (memcmp(&((struct sockaddr_in*)&avec->data[i])->sin_addr,
                       &((struct sockaddr_in*)addr)->sin_addr,
                       sizeof(struct in_addr)) == 0)
                return 1;
            break;
#ifdef AF_INET6
        case AF_INET6:
            if (memcmp(&((struct sockaddr_in6*)&avec->data[i])->sin6_addr,
                       &((struct sockaddr_in6*)addr)->sin6_addr,
                       sizeof(struct in6_addr)) == 0)
                return 1;
            break;
#endif
        default:
            break;
        }
    }

    return 0;
}

int addrvec_append(addrvec_t *avec, const struct sockaddr_storage *addr)
{
    int rc = 0;
    assert(avec);
    assert(addr);

    rc = addrvec_grow_if_full(avec);
    if (rc != 0)
    {
        return rc;
    }

    // Copy addrinfo into address list
    memcpy(avec->data + avec->count, addr, sizeof(*addr));
    ++avec->count;

    return 0;
}

int addrvec_append_addrinfo(addrvec_t *avec, const struct addrinfo *addrinfo)
{
    int rc = 0;
    assert(avec);
    assert(addrinfo);

    rc = addrvec_grow_if_full(avec);
    if (rc != 0)
    {
        return rc;
    }

    // Copy addrinfo into address list
    memcpy(avec->data + avec->count, addrinfo->ai_addr, addrinfo->ai_addrlen);
    ++avec->count;

    return 0;
}

void addrvec_shuffle(addrvec_t *avec)
{
    int i = 0;
    for (i = avec->count - 1; i > 0; --i) {
        long int j = random()%(i+1);
        if (i != j) {
            struct sockaddr_storage t = avec->data[i];
            avec->data[i] = avec->data[j];
            avec->data[j] = t;
        }
    }
}

int addrvec_hasnext(const addrvec_t *avec)
{
    return avec->count > 0 && (avec->next < avec->count);
}

int addrvec_atend(const addrvec_t *avec)
{
    return avec->count > 0 && avec->next >= avec->count;
}

void addrvec_next(addrvec_t *avec, struct sockaddr_storage *next)
{
    int index;

    // If we're at the end of the list, then reset index to start
    if (addrvec_atend(avec)) {
        avec->next = 0;
    }

    if (!addrvec_hasnext(avec)) {
        if (next) {
            memset(next, 0, sizeof(*next));
        }

        return;
    }

    index = avec->next++;

    if (next) {
        *next = avec->data[index];
    }
}

void addrvec_peek(addrvec_t *avec, struct sockaddr_storage *next)
{
    int index = avec->next;

    if (avec->count == 0) {
        memset(next, 0, sizeof(*next));
        return;
    }

    if (addrvec_atend(avec)) {
        index = 0;
    }

    *next = avec->data[index];
}


int addrvec_eq(const addrvec_t *a1, const addrvec_t *a2)
{
    uint32_t i = 0;
    if (a1->count != a2->count)
    {
        return 0;
    }

    for (i = 0; i < a1->count; ++i)
    {
        if (!addrvec_contains(a2, &a1->data[i]))
            return 0;
    }

    return 1;
}
