//
// tardy - a tar post-processor
// Copyright (C) 2011 Peter Miller
//
// 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 <libtardy/ac/string.h>

#include <libtardy/rcstring/list.h>
#include <libtardy/tar/output/filter/ar_long_names.h>


tar_output_filter_ar_long_names::~tar_output_filter_ar_long_names()
{
}


tar_output_filter_ar_long_names::tar_output_filter_ar_long_names(
    const pointer &a_deeper
) :
    tar_output_filter(a_deeper)
{
}


tar_output_filter_ar_long_names::pointer
tar_output_filter_ar_long_names::create(const pointer &a_deeper)
{
    return pointer(new tar_output_filter_ar_long_names(a_deeper));
}


void
tar_output_filter_ar_long_names::write_header(const tar_header &hdr)
{
    current_header = hdr;
    current_data.clear();
    switch (hdr.type)
    {
    case tar_header::type_directory:
        // silently ignore
        break;

    case tar_header::type_normal:
    case tar_header::type_normal_contiguous:
        break;

    default:
        fatal("file %s type not supported", hdr.name.quote_c().c_str());
        break;
    }
}


void
tar_output_filter_ar_long_names::write_header_padding(void)
{
    // Do nothing.
}


void
tar_output_filter_ar_long_names::write_data(const void *data, int data_size)
{
    assert(data_size >= 0);
    current_data.push_back(data, data_size);
}


void
tar_output_filter_ar_long_names::write_data_padding(void)
{
    switch (current_header.type)
    {
    case tar_header::type_normal:
    case tar_header::type_normal_contiguous:
        break;

    default:
        return;
    }
    // stash the file and its data
    contents.push_back(contents_t::value_type(current_header, current_data));

    // frugal
    current_data.clear();
}


static bool
needs_name_map(const rcstring &name, size_t max)
{
    if (max == 0)
        return false;
    return
        (
            name.empty()
        ||
            name.size() > max
        ||
            strchr(name.c_str(), ' ')
        ||
            strchr(name.c_str(), '/')
        ||
            strchr(name.c_str(), '\n')
        );
}


void
tar_output_filter_ar_long_names::write_archive_end(void)
{
    size_t maxnamlen = tar_output_filter::get_maximum_name_length();
    rcstring_accumulator index_data;
    for (contents_t::iterator it = contents.begin(); it != contents.end(); ++it)
    {
        tar_header &hdr = it->first;
        if (needs_name_map(hdr.name, maxnamlen))
        {
            rcstring new_name =
                rcstring::printf("/%lu", (unsigned long)index_data.size());
            rcstring old_name = hdr.name;
            if (old_name.empty())
                old_name = " ";
            else
                old_name = old_name.substitute("\n", " ");
            index_data.push_back(old_name);
            index_data.push_back("/\n");
            hdr.name = new_name;
        }
    }

    //
    // Only write an index file if it has some contents.
    //
    if (!index_data.empty())
    {
        tar_header index_hdr;
        index_hdr.name = "//";
        index_hdr.type = tar_header::type_normal;
        index_hdr.size = index_data.size();

        tar_output_filter::write_header(index_hdr);
        tar_output_filter::write_header_padding();
        tar_output_filter::write_data(index_data.get_data(), index_data.size());
        tar_output_filter::write_data_padding();

        // frugal
        index_data.clear();
    }

    //
    // Write each archive member to the output,
    // the names have been mapped already.
    //
    for (contents_t::iterator it = contents.begin(); it != contents.end(); ++it)
    {
        tar_header &hdr = it->first;
        tar_output_filter::write_header(hdr);
        tar_output_filter::write_header_padding();
        const rcstring_accumulator &data = it->second;
        tar_output_filter::write_data(data.get_data(), data.size());
        tar_output_filter::write_data_padding();
    }

    // frugal
    contents.clear();

    //
    // finialize the resulting archive
    //
    tar_output_filter::write_archive_end();
}


size_t
tar_output_filter_ar_long_names::get_maximum_name_length(void)
    const
{
    return 0;
}
