/**
 * @file       paramgen.cpp
 *
 * @brief      Parameter generation utility for Zerocoin.
 *
 * @author     Ian Miers, Christina Garman and Matthew Green
 * @date       June 2013
 *
 * @copyright  Copyright 2013 Ian Miers, Christina Garman and Matthew Green
 * @license    This project is released under the MIT license.
 **/
// Copyright (c) 2017-2019 The PIVX developers
// Copyright (c) 2020 The CryptoDev developers
// Copyright (c) 2020 The peony developers

#include <string>
#include <iostream>
#include <fstream>
//#include <curses.h>
#include <exception>
#include "Zerocoin.h"

#define DEFAULT_MODULUS_SIZE    3072
#define MIN_MODULUS_SIZE        1026


void
PrintWarning()
{
    cout << "Zerocoin parameter generation utility" << endl;
    cout << "-------------------------------------" << endl << endl;
    cout << "This utility generates an l-bit modulus N as the product of" << endl;
    cout << "two safe primes p, q. The values p and q are not stored." << endl;
    cout << "Call this program with no arguments to see usage options." << endl;
    cout << endl;
    cout << "SECURITY WARNING: ZEROCOIN PARAMETERS MUST BE GENERATED BY" << endl;
    cout << "A TRUSTED PARTY WHO DOES NOT STORE THE FACTORS. WHILE WE MAKE" << endl;
    cout << "A BEST EFFORT TO DESTROY THIS INFORMATION WE DO NOT TAKE" << endl;
    cout << "SPECIAL PRECAUTIONS TO ENSURE THAT THEY ARE DESTROYED." << endl;
    cout << endl;
    cout << "USE THIS UTILITY AT YOUR OWN RISK" << endl << endl;
}

void usage()
{
    printf("Usage:\n");
    printf(" -b <numbits>\n");
    printf(" -o <output file>\n");

    exit (8);
}

int main(int argc, char **argv)
{
    static CBigNum resultModulus(0);
    uint32_t numBits = DEFAULT_MODULUS_SIZE;
    ofstream outfile;
    char* outfileName;
    bool writeToFile = false;

    while ((argc > 1) && (argv[1][0] == '-'))
    {
        switch (argv[1][1])
        {
        case 'b':
            numBits = atoi(argv[2]);
            ++argv;
            --argc;
            break;

        case 'o':
            outfileName = argv[2];
            writeToFile = true;
            break;

        case 'h':
            usage();
            break;

        default:
            printf("Wrong Argument: %s\n", argv[1]);
            usage();
            break;
        }

        ++argv;
        --argc;
    }

    if (numBits < MIN_MODULUS_SIZE) {
        cout << "Modulus is below minimum length (" << MIN_MODULUS_SIZE << ") bits" << endl;
        return(0);
    }

    PrintWarning();

    cout << "Modulus size set to " << numBits << " bits." << endl;
    cout << "Generating parameters. This may take a few minutes..." << endl;

    // Generate two safe primes "p" and "q"
    CBigNum *p, *q;
    p = new CBigNum(0);
    q = new CBigNum(0);
    *p = CBigNum::generatePrime(numBits / 2, true);
    *q = CBigNum::generatePrime(numBits / 2, true);

    // Multiply to compute N
    resultModulus = (*p) * (*q);

    // Wipe out the factors
    delete p;
    delete q;

    // Convert to a hexidecimal string
    std::string resultHex = resultModulus.ToString(16);

    cout << endl << "N = " << endl << resultHex << endl;

    if (writeToFile) {
        try {
            outfile.open (outfileName);
            outfile << resultHex;
            outfile.close();
            cout << endl << "Result has been written to file '" << outfileName << "'." << endl;
        } catch (const std::runtime_error& e) {
            cout << "Unable to write to file:" << e.what() << endl;
        }
    }
}
