// This file is part of Extract PDFmark.
//
// Copyright (C) 2016, 2018 Masamichi Hosoda
//
// Extract PDFmark 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.
//
// Extract PDFmark 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 Extract PDFmark.  If not, see <http://www.gnu.org/licenses/>.

#include "config.h"

#include "poppler-core.hh"

#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <Link.h>
#include <PDFDoc.h>

#ifndef HAVE_POPPLER_CORE_IF
#include "destname-private.hh"
#endif

inline std::string goo_to_string (GooString *goo)
{
 return std::string {
#ifdef HAS_GOOSTRING_C_STR
   goo->c_str (),
#else
   goo->getCString (),
#endif
     static_cast<std::string::size_type>(goo->getLength ())};
}

inline void poppler_core::add_destname (GooString *name,
                                       LinkDest *link_dest)
{
 std::string str = goo_to_string (name);
 add_destname (str, link_dest);
}

inline void poppler_core::add_destname (GooString *name)
{
 std::string str = goo_to_string (name);
 std::unique_ptr<LinkDest> link_dest {doc->findDest (name)};
 add_destname (str, link_dest.get ());
}

inline void poppler_core::add_destname (const char *name)
{
 GooString goo {name};
 std::unique_ptr<LinkDest> link_dest {doc->findDest (&goo)};
 add_destname (name, link_dest.get ());
}

inline void poppler_core::add_destname (const std::string &name,
                                       LinkDest *link_dest)
{
 if (link_dest)
   {
     name_map.insert (std::make_pair (name, *link_dest));
   }
}

std::string poppler_core::build_destname (const std::string &name,
                                         LinkDest *link_dest)
{
 std::stringstream ss;

 if (link_dest)
   {
     int pagenum;
     if (link_dest->isPageRef ())
       {
         Ref page_ref = link_dest->getPageRef ();
         pagenum = doc->findPage (page_ref.num, page_ref.gen);
       }
     else
       {
         pagenum = link_dest->getPageNum ();
       }

     switch (link_dest->getKind ())
       {
       case destXYZ:
         ss
           << "[ /Dest " << encode_name (name)
           << " /Page " << pagenum
           << " /View [/XYZ " << link_dest->getLeft ()
           << " " << link_dest->getTop ()
           << " " << link_dest->getZoom ()
           << "] /DEST pdfmark" << std::endl;
         break;
       case destFit:
         ss
           << "[ /Dest " << encode_name (name)
           << " /Page " << pagenum
           << " /View [/Fit] /DEST pdfmark" << std::endl;
         break;
       case destFitH:
         ss
           << "[ /Dest " << encode_name (name)
           << " /Page " << pagenum
           << " /View [/FitH " << link_dest->getTop ()
           << "] /DEST pdfmark" << std::endl;
         break;
       case destFitV:
         ss
           << "[ /Dest " << encode_name (name)
           << " /Page " << pagenum
           << " /View [/FitV " << link_dest->getLeft ()
           << "] /DEST pdfmark" << std::endl;
         break;
       case destFitR:
         ss
           << "[ /Dest " << encode_name (name)
           << " /Page " << pagenum
           << " /View [/FitR " << link_dest->getLeft ()
           << " " << link_dest->getBottom ()
           << " " << link_dest->getRight ()
           << " " << link_dest->getTop ()
           << "] /DEST pdfmark" << std::endl;
         break;
       case destFitB:
         ss
           << "[ /Dest " << encode_name (name)
           << " /Page " << pagenum
           << " /View [/FitB] /DEST pdfmark" << std::endl;
         break;
       case destFitBH:
         ss
           << "[ /Dest " << encode_name (name)
           << " /Page " << pagenum
           << " /View [/FitBH " << link_dest->getTop ()
           << "] /DEST pdfmark" << std::endl;
         break;
       case destFitBV:
         ss
           << "[ /Dest " << encode_name (name)
           << " /Page " << pagenum
           << " /View [/FitBV " << link_dest->getLeft ()
           << "] /DEST pdfmark" << std::endl;
         break;
       default:
         ss << "%  link_dest kind is unknown." << std::endl;
         break;
       }
   }
 else
   {
     ss << "%  link_dest is null." << std::endl;
   }

 return ss.str ();
}

std::string poppler_core::build_destnames (void)
{
 std::string str;

 for (auto &elm: name_map)
   {
     str += build_destname (elm.first, &elm.second);
   }

 return str;
}

#ifdef HAVE_POPPLER_CORE_IF

// Use poppler-core interface
std::string poppler_core::destname (void)
{
 Catalog *catalog = doc->getCatalog ();
 name_map.clear ();

 if (catalog && catalog->isOk ())
   {
     int len = catalog->numDestNameTree ();
     for (int i=0; i<len; ++i)
       {
         add_destname (catalog->getDestNameTreeName (i),
                       catalog->getDestNameTreeDest (i));
       }

     len = catalog->numDests ();
     for (int i=0; i<len; ++i)
       {
         add_destname (catalog->getDestsName (i),
                       catalog->getDestsDest (i));
       }
   }
 else
   std::cerr << "Catalog is not OK" << std::endl;

 return build_destnames ();
}

#else  // HAVE_POPPLER_CORE_IF

// Use poppler-core private access
std::string poppler_core::destname (void)
{
 Catalog *catalog = doc->getCatalog ();
 name_map.clear ();

 if (catalog && catalog->isOk ())
   {
     NameTree *destnametree = get_destnametree (catalog);
     if (destnametree)
       {
         int len = destnametree->numEntries ();
         for (int i=0; i<len; ++i)
           {
             add_destname (destnametree->getName (i));
           }
       }

     Object *obj = catalog->getDests ();
     if (obj && obj->isDict ())
       {
         int len = obj->dictGetLength ();
         for (int i=0; i<len; ++i)
           {
             add_destname (obj->dictGetKey (i));
           }
       }
   }
 else
   std::cerr << "Catalog is not OK" << std::endl;

 return build_destnames ();
}

#endif  // HAVE_POPPLER_CORE_IF