
#include <vector>
#include <sstream>
#include <iomanip>
#include "../TOC/def.h"
#include "updator.h"
#include "update_catalog.h"

using std::string;
typedef std::vector<string> vector;
using std::ostringstream;
using std::setw;

string PDF::updator(
  PDF &pdf,
  const Page_no::PS_page_numbering &nog,
  const TOC::Table &toc,
  const std::string &pdf_filename,
  const string &title
) {

  const int wo = 10; // number of digits in an xref offset
  const int wg =  5; // number of digits in an xref generation

  // Study the PDF, the PS and the TOC.  Make strings
  // representing PDF objects for the updated PDF catalog
  // and for the PDF outline.
  const string catalog = update_catalog   ( pdf, nog   );
  const string info    = add_title_to_info( pdf, title );
  vector outline;
  for (
    TOC::Item *item = toc.root();
    item;
    item = TOC::next_in_seq(*item)
  ) outline.push_back(
    pdf_object_str( pdf, nog, *item, 0, TOC::OFFSET_RELATIVE )
  );

  int file_offset = file_length(pdf);

  // Make a string representing the PDF xref table update.
  string xref;
  {
    ostringstream oss;
    oss << std::setfill('0');
    oss << "xref\n";
    // The catalog object index is purposely printed
    // with minimal field width.
    oss << iref_catalog(pdf).i << " 1\n";
    {
      // The trailing space in " n \n" is significant.
      // Refer to Adobe's PDF Reference 1.7, sect. 3.4.3.
      oss
        << setw(wo) << file_offset << ' '
        << setw(wg) << 0 << " n \n";
      file_offset += catalog.length();
    }
    oss << iref_info(pdf).i << " 1\n";
    {
      oss
        << setw(wo) << file_offset << ' '
        << setw(wg) << 0 << " n \n";
      file_offset += info.length();
    }
    oss << n_obj(pdf) << ' ' << outline.size() << '\n';
    for (
      vector::const_iterator p = outline.begin();
      p != outline.end();
      ++p
    ) {
      oss
        << setw(wo) << file_offset << ' '
        << setw(wg) << 0 << " n \n";
      file_offset += p->length();
    }
    xref += oss.str();
  }

  // Make a string representing the new PDF trailer.
  string trailer = update_trailer(
    pdf,
    n_obj(pdf) + outline.size(),
    file_offset,
    pdf_filename
  );

  string res;
  {
    res += catalog;
    res += info;
    for (
      vector::const_iterator p = outline.begin();
      p != outline.end();
      ++p
    ) res += *p;
    res += xref;
    res += trailer;
  }

  return res;

}

string PDF::updator(
  const string &filename_pdf,
  const string &filename_ps,
  const string &filename_toc,
  const string &title
) {
  PDF pdf( filename_pdf );
  return updator(
    pdf,
    Page_no::PS_page_numbering( filename_ps ),
    TOC::Table( filename_toc ),
    filename_pdf,
    title
  );
}

