/******************************************************************************
 * Copyright (C) 2006 Tetsuya Kimata <kimata@acapulco.dyndns.org>
 *
 * All rights reserved.
 *
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the authors be held liable for any
 * damages arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any
 * purpose, including commercial applications, and to alter it and
 * redistribute it freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must
 *    not claim that you wrote the original software. If you use this
 *    software in a product, an acknowledgment in the product
 *    documentation would be appreciated but is not required.
 *
 * 2. Altered source versions must be plainly marked as such, and must
 *    not be misrepresented as being the original software.
 *
 * 3. This notice may not be removed or altered from any source
 *    distribution.
 *
 * $Id: UploadItemList.cpp 2201 2006-12-09 02:51:13Z svn $
 *****************************************************************************/

#include "Environment.h"

#include <cstddef>
#include <iostream>
#include <iomanip>

#include "apr_time.h"

#include "UploadItemList.h"
#include "Message.h"
#include "Auxiliary.h"
#include "SourceInfo.h"

SOURCE_INFO_ADD("$Id: UploadItemList.cpp 2201 2006-12-09 02:51:13Z svn $");

#define AS_UHEADER(pointer)     reinterpret_cast<UploadItem::header_t *>(pointer)
#define AS_UITEM_LIST(pointer)  reinterpret_cast<UploadItemList *>(pointer)

/******************************************************************************
 * public メソッド
 *****************************************************************************/
UploadItemList::UploadItemList(apr_shm_t *shm, apr_uint64_t max_file_size,
                               apr_size_t max_list_size)
  : header_list_(0),
    max_file_size_(max_file_size),
    max_list_size_(max_list_size)
{
#ifdef DEBUG
    if (apr_shm_size_get(shm) != get_memory_size(max_list_size)) {
        THROW(MESSAGE_SHM_SIZE_INVALID);
    }
#endif

    init_shm();

    *header_list_size_ = 0;
    *total_file_size_ = 0;
}

void UploadItemList::add(UploadItem *uitem)
{
    apr_size_t i;

    // MEMO: header_list_ は mtime に基づいて降順に並んでいる

    i = get_insert_index(uitem);

    if (i == *header_list_size_) {
        if (*header_list_size_ == max_list_size_) { // 追加する必要なし
            return;
        } else { // 末尾に追加
            load_header(uitem, header_list_ + *header_list_size_);
        }
    } else { // 挿入
        memmove(header_list_ + i + 1, header_list_ + i,
                sizeof(UploadItem::header_t) * (*header_list_size_ - i));
        load_header(uitem, header_list_ + i);
    }

    (*total_file_size_) += uitem->get_file_size();
    (*header_list_size_)++;
}

void UploadItemList::remove(apr_size_t item_id)
{
    apr_size_t i;
    apr_off_t file_size;
    UploadItem::header_t *header;

    i = get_index_by_id(item_id);
    header = get_by_index(i);

    file_size = header->file_size;

    if (i != (*header_list_size_ - 1)) {
        memmove(header_list_ + i, header_list_ + i + 1,
                sizeof(UploadItem::header_t) * (*header_list_size_ - i - 1));
    }

    if (*header_list_size_ == 0) { // 念のため
        THROW(MESSAGE_BUG_FOUND);
    }

    (*total_file_size_) -= file_size;
    (*header_list_size_)--;
}

UploadItemList *UploadItemList::get_instance(apr_shm_t *shm,
                                             apr_uint64_t max_file_size,
                                             apr_size_t max_list_size)
{
    UploadItemList *uheader_list;

    uheader_list = AS_UITEM_LIST(apr_shm_baseaddr_get(shm));
    new(uheader_list) UploadItemList(shm, max_file_size, max_list_size);

    return uheader_list;
}

UploadItemList *UploadItemList::child_init(apr_shm_t *shm)
{
    UploadItemList *item_list;

    item_list = AS_UITEM_LIST(apr_shm_baseaddr_get(shm));
    item_list->child_init_impl();

    return item_list;
}

apr_size_t UploadItemList::get_memory_size(apr_size_t max_list_size)
{
    return sizeof(UploadItemList) +
        (sizeof(UploadItem::header_t)*max_list_size) +
        sizeof(apr_size_t) + sizeof(apr_off_t);
}

void UploadItemList::dump_header_list(apr_pool_t *pool,
                                      UploadItemList *list)
{
    for (apr_size_t i = 0; i < *(list->header_list_size_); i++) {
        dump_header(pool, list->header_list_ + i);
    }
}


/******************************************************************************
 * private メソッド
 *****************************************************************************/
void UploadItemList::child_init_impl()
{
    attach_shm();
}

void UploadItemList::init_shm()
{
    header_list_ = AS_UHEADER(this + 1);
    header_list_size_ = AS_SIZE(header_list_ + max_list_size_);
    total_file_size_ = AS_OFF(header_list_size_ + 1);

#ifdef __INTEL_COMPILER
#pragma warning(disable:1684)
#endif
    ASSERT_EQUAL(AS_CHAR(total_file_size_ + 1),
                 AS_CHAR(header_list_) +
                 (get_memory_size(max_list_size_) - sizeof(UploadItemList)));
#ifdef __INTEL_COMPILER
#pragma warning(default:1684)
#endif
}

void UploadItemList::attach_shm()
{
    init_shm();
}

apr_size_t UploadItemList::get_insert_index(UploadItem *uitem) const
{
    apr_size_t i;

    for (i = 0; i < *header_list_size_; i++) {
        if (uitem->get_mtime() >= header_list_[i].mtime) {
            return i;
        }
    }
    return i;
}

apr_size_t UploadItemList::get_index_by_id(apr_size_t item_id) const
{
    // 頻繁には呼ばれ無いはずなので，線形探索...
    for (apr_size_t i = 0; i < *header_list_size_; i++) {
        if ((header_list_ + i)->id == item_id) {
            return i;
        }
    }

    THROW(MESSAGE_LIST_ID_INVALID);
}

void UploadItemList::load_header(UploadItem *uitem,
                                 UploadItem::header_t *header)
{
    *header = *(AS_UHEADER(uitem));
}

void UploadItemList::dump_header(apr_pool_t *pool,
                               UploadItem::header_t *header)
{
    cerr << setw(10) << header->id << " ";
    cout << "[" << header->file_name << "] ";
    cout << "("; dump_date(pool, header->mtime); cout << ")" << endl;
}

void UploadItemList::dump_date(apr_pool_t *pool, apr_time_t time)
{
    apr_time_exp_t time_exp;
    apr_size_t time_str_size;
    char time_str[64];

    check_apr_error(pool, apr_time_exp_lt(&time_exp, time));
    check_apr_error(pool,
                    apr_strftime(time_str, &time_str_size, sizeof(time_str),
                                 "%y/%m/%d %H:%M:%S",
                                 &time_exp));
    cout << time_str;
}

// Local Variables:
// mode: c++
// coding: utf-8-dos
// End:
