/*! @file

    @id $Id$
*/
//       1         2         3         4         5         6         7         8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890

#include <mrw/args.hxx>
#include <mrw/vector.hxx>
#include <cryptoki.hxx>

#include <string>
#include <map>
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <sstream>
#include <fstream>
#include <streambuf>
#ifndef MRW__OLD_PRE11_COMPILER
#include <chrono>
#endif


int main(int argc, char** argv) try {

  // options
  unsigned long r(1);
  std::string txt("This is an example.");
  std::string lib("libcvP11.so");
  std::string slot;
  std::string cert;

  mrw::args::parse(argc, argv,
                   "Sign a text (optionally several times for performance"
                   " measurements).",
                   mrw::args::list()
                   <<mrw::args::decl("h", "help", "show help",
                                     mrw::args::decl::param_list()
                                     <<mrw::args::help()
                                     <<mrw::args::exit())
                   <<mrw::args::decl("r", "repeat", "number of repetitions",
                                     mrw::args::decl::param_list()
                                     <<mrw::args::param(r, "number"))
                   <<mrw::args::decl("t", "text", "text to sign",
                                     mrw::args::decl::param_list()
                                     <<mrw::args::param(txt, "text"))
                   <<mrw::args::decl("l", "library", "cryptoki lirary to load",
                                     mrw::args::decl::param_list()
                                     <<mrw::args::param(lib, "lib"))
                   <<mrw::args::decl("s", "slot", "name of slot",
                                     mrw::args::decl::param_list()
                                     <<mrw::args::param(slot, "name"))
                   <<mrw::args::decl("c","cert", "name of certificate",
                                     mrw::args::decl::param_list()
                                     <<mrw::args::param(cert, "name")));
  
  std::cout<<"Sign text "<<r<<" times:"<<std::endl
           <<"-----------------------------------------------------"<<std::endl
           <<txt<<std::endl
           <<"-----------------------------------------------------"<<std::endl;
  cryptoki::Library c(lib);
  cryptoki::SlotList sl(c.slotList());
  for (cryptoki::SlotList::iterator s(sl.begin()); s!=sl.end(); ++s) {
    cryptoki::SlotInfo si(s->slotinfo());
    if (slot.size()&&slot!=si.slotDescription) continue;
    std::cout<<"Found Slot: "<<si.slotDescription<<std::endl;
    cryptoki::TokenInfo ti(s->tokeninfo());
    std::cout<<"Found token: "<<ti.label<<std::endl;
    cryptoki::Session session(*s);
    cryptoki::ObjectList certs
      (session.find(cryptoki::Attribute(CKA_CLASS)
                    .from<CK_OBJECT_CLASS>(CKO_CERTIFICATE)));
    for (cryptoki::ObjectList::iterator it(certs.begin());
         it!=certs.end(); ++it) {
      std::string label(it->attribute(CKA_LABEL).value);
      if (cert.size()&&cert!=label) continue;
      cryptoki::Attribute id(it->attribute(CKA_ID));
      cryptoki::ObjectList keys
        (session.find(cryptoki::Attribute(CKA_CLASS)
                      .from<CK_OBJECT_CLASS>(CKO_PUBLIC_KEY),
                      id));
      if (!keys.size()) continue;
      std::cout<<"Found Certificate: "
                   <<it->attribute(CKA_LABEL).value<<std::endl;
      if (!cert.size()) continue;
      std::cout<<"Pin: ";
      std::string pin;
      std::cin>>pin;
      session.login(pin);
      keys = session.find(cryptoki::Attribute(CKA_CLASS)
                          .from<CK_OBJECT_CLASS>(CKO_PRIVATE_KEY),
                          id);
      if (keys.size()!=1) {
        std::cerr<<"**** ERROR: No private key: "<<keys.size()<<std::endl;
        return 1;
      }
      std::cout<<"Signing ..."<<std::endl;
#ifndef MRW__OLD_PRE11_COMPILER
      auto start = std::chrono::system_clock::now();
#endif
      for (unsigned int i(0); i<r; ++i)
        std::cout<<"Text:"<<std::endl
                 <<crypto::readable(txt)<<std::endl
                 <<"Signature:"<<std::endl
                 <<crypto::readable(keys[0].sign(txt, CKM_RSA_PKCS))
                 <<std::endl;
#ifndef MRW__OLD_PRE11_COMPILER
      auto end = std::chrono::system_clock::now();
      auto elapsed(std::chrono::duration_cast<std::chrono::milliseconds>
                   (end-start));
      std::cout<<"Done in "<<elapsed.count()<<"ms"<<std::endl;
#else
      std::cout<<"Done."<<std::endl;
#endif
    }
  }
  return 0;
 } catch (std::exception& x) {
  std::cerr<<"**** ERROR: "<<x.what()<<std::endl;
  return 1;
 }
