#include <cassert>
#include <fstream>
using namespace std;

#include <NTL/ZZ.h>
using namespace NTL;

#include <aclock.h>
#include <commandline.h>
#include <primegen.h>
#include <rsa.h>
#include <rsastate.h>
#include <streamconversion.h>




rsastate::rsastate(int argc, char** argv)
{
  commandline cmd(argc,argv);
  cmd.readfile("read");

  bool encryptb(false);
  bool decryptb(false);
  bool generateb(false);
  bool generate2b(false);

  cmd.mapvar(encryptb,"encrypt");
  cmd.mapvar(decryptb,"decrypt");
  cmd.mapvar(generateb,"generate");
  cmd.mapvar(generate2b,"generate2");

  cmd.mapvar(e,"e");
  cmd.mapvar(n,"n");
  cmd.mapvar(p,"p");
  cmd.mapvar(q,"q");
  cmd.mapvar(file,"file");

  blocksize=0;
  cmd.mapvar(blocksize,"blocksize");
  nbits=20;
  cmd.mapvar(nbits,"nbits");

  cmd.enablehelp();
  //cmd.enablehelp(cout);

  if (encryptb) { encrypt(); return; }
  if (decryptb) { decrypt(); return; }
  if (generateb) { generate(); return; }
  if (generate2b) { generate2(); return; }

  displaymessage();
}

void rsastate::encrypt()
{
  if ( file.empty() || n.empty() || e.empty() 
    || (blocksize==0) )
  {
    cout << "error: one or more is incorrect" << endl;
    cout << "  " << SHOW(file.empty()) << endl;
    cout << "  " << SHOW(n.empty()) << endl;
    cout << "  " << SHOW(blocksize) << endl;

    return;
  }

  cout << "Encrypting File" << endl;

  string s;
  {
  ifstream f1(file.c_str());
  asciitodig().forward(s,f1);
  //f1 >> s;
  }

  digdiv d;
  d.blksz = blocksize;
  d.forward(s);

  rsaE E;
  conv(E.e,e.c_str());
  conv(E.n,n.c_str());

  d.eval(E);
//printv(d.v);

  //d.reverse(s);
  {
  ofstream f2(file.c_str());
  d.save(f2);
  }

}


void rsastate::decrypt()
{
  if ( file.empty() )
  {
    cout << "error:  " << SHOW(file.empty()) << endl;
    return;
  }

  if ( e.empty() )
  {
    cout << "error:  " << SHOW(e.empty()) << endl;
    return;
  }

  if ( p.empty() )
  {
    cout << "error:  " << SHOW(p.empty()) << endl;
    return;
  }

  if ( q.empty() )
  {
    cout << "error:  " << SHOW(q.empty()) << endl;
    return;
  }

  cout << "Decrypting File" << endl;

  digdiv d;
  {
  ifstream f1(file.c_str());
  d.restore(f1);
  }

  rsaD D;
  conv(D.e,e.c_str());
  conv(D.p,p.c_str());
  conv(D.q,q.c_str());
  D.init();

  d.eval(D);

//printv(d.v);

//cout << SHOW(d.v.size()) << endl;
//cout << SHOW(d.blksz) << endl;

  {
  ofstream f2(file.c_str());
  string s;
  d.reverse(s);
  asciitodig().reverse(f2,s);
  //f2 << s;
//cout << s << endl;
  }
}

void rsastate::generate()
{
  rsaGenKey G(nbits);
}

void rsastate::generate2()
{
  if ( p.empty() || q.empty() )
  {
    cout << "error: one or more is incorrect" << endl;
    cout << "  " << SHOW(p.empty()) << endl;
    cout << "  " << SHOW(q.empty()) << endl;

    return;
  }

  rsaGenKey G(nbits,p,q);

}



void rsastate::displaymessage() const
{
  cout << endl;
  cout << "RSA encryption/decryption program." << endl;
  cout << endl;
  cout << "You can take full control as seen in example 2 or work with files" << endl;
  cout << "  and let the application generate the key.  This is the easiest way," << endl;
  cout << "  but you should manage the key length through nbits. Two files" << endl;
  cout << "  \"encrypt.txt\" and \"decrypt.txt\" are generated. Keep \"decrypt.txt\"" << endl;
  cout << "  secret.  Because the algorithm is public key it does not matter if someone" << endl;
  cout << "  has both the encrypted and decrypted files they will not be able to use this" << endl;
  cout << "  to find your key. Its safe to have the encrypted file public." << endl;
  cout << endl;
  cout << "Example 1" << endl;
  cout << "  $./main rsa generate=true nbits=1000" << endl;
  cout << "To encrypt a file:" << endl;
  cout << "  $./main rsa read=encrypt.txt file=msg.txt" << endl;
  cout << "To decrypt a file:" << endl;
  cout << "  $./main rsa read=decrypt.txt file=msg.txt" << endl;
  cout << endl;


  cout << "Example 2" << endl;
  cout << "To encrypt a file:" << endl;
  cout << "  $./main rsa encrypt=true file=msg.txt n=3337 e=79 blocksize=3" << endl;
  cout << "To decrypt a file:" << endl;
  cout << "  $./main rsa decrypt=true p=47 q=71 file=msg.txt e=79" << endl;
  cout << endl;
  cout << "Paranoid: Imagine that there is a back door in this system. Even" << endl;
  cout << "  if you think I am an honest person and would not do this to you," << endl;
  cout << "  the random library number generator could have been tampered with," << endl;
  cout << "  so the primes you think are difficult for another to generate could" << endl;
  cout << "  be found by a superior enemy. Whatever your condition there is another" << endl;
  cout << "  way to generate the primes. generate2 takes two numbers p and q and " << endl;
  cout << "  sequentially searches for the first prime after that number. " << endl;
  cout << "  Set numbits to be large which just adds this large number to p and q." << endl;
  cout << endl;
  cout << "Example 3" << endl;
  cout << "  $./main rsa generate2=true nbits=4000 p=84982438458382834841 q=83839388477473777221" << endl;
  cout << "On my P3 it takes less than 10 minutes to generate the key." << endl;
  cout << endl;

}



