#!/usr/bin/node

"use strict"

const   Libbfa      =   require( process.env.BFAHOME + '/bin/libbfa.js');
const   rl          =   require('readline').createInterface(
        { input: process.stdin, output: process.stdout }
    );
var     web3;
var     Distillery;
var     bfa;
var     notation    =   [ 6 ];

function    init()
{
    notation.push( Math.pow(10, notation[0]) );
    switch ( notation[0] ) {
        case 6:
            notation.push( "Mwei" );
            break;
        case 9:
            notation.push( "Gwei" );
            break;
        default:
            notation = [ 6, Math.pow(10, 6), "Mwei" ];
    }
    bfa             =   new Libbfa();
    web3            =   bfa.newweb3();
    Distillery      =   bfa.contract( web3, 'Distillery' );
    if ( undefined == Distillery )
        fatal('Can not initialise Distillery contact.');
    web3.eth.getBalance( Distillery.contractaddress ).then(
        function receivedOwnBalance(val) {
            Distillery.contractbalance = val;
            getlist();
        },
        function err(x){ bfa.fatal("Can't do that: "+x);process.exit(1); }
    );
}

function    palletSort(a,b)
{
    if ( b == undefined )
    {
        if ( a == undefined )
            return 0;
        else
            return -1;
    }
    if ( a == undefined )
        return 1;
    var strA    =   a[0].toLowerCase();
    var strB    =   b[0].toLowerCase();
    if ( strA < strB )
        return -1;
    if ( strA > strB )
        return 1;
    return 0;
}

function    requestBalances( count )
{
    var     pallet    =   new Array;
    var     proms   =   new Array;
    var     i;
    // Fetch addresses from the list in the contract.
    for ( i=0; i<count; i++ )
    {
        proms.push(
            Distillery.methods.atPosition(i).call( {} )
                .then(
                    function(res) { pallet.push(res); },
                    function(err) { bfa.fatal("Fetching position data failed: "+err) }
                )
        );
    }
    Promise.all( proms ).then(
        function(x) {
            // The Well has now been filled out
            // so we will ask for the balances of the found accounts
            var     p2          =   new Array();
            for ( i=0; i<count; i++ )
            {
                var     cb      =   setBal.bind(null,pallet,i);
                p2.push( web3.eth.getBalance( pallet[i][0] ).then(cb) );
            }
            Promise.all( p2 ).then(
                function allbalances(a) { displayBalances(pallet) },
                function(err) { bfa.fatal("Getting balances failed: "+err) }
            );
        },
        function(err) { bfa.fatal("Getting account list failed: "+err) }
    );
}

function    setBal( arr, idx, val )
{
    arr[idx][2]=val;
}

function    editAccount( entry, pallet )
{
    if ( entry == undefined )
        return;
    var     acct;
    var     value;
    // it is an existing account address?
    if ( bfa.isAddr(entry) )
    {
        var     i       =   0;
        var     n       =   pallet.length;
        while ( i < n )
        {
            if (String(pallet[i][0]).toLowerCase() == String(entry).toLowerCase() )
                entry   =   i;
            i++;
        }
    }
    // it is a new account address?
    if ( bfa.isAddr(entry) )
    {
        acct            =   entry;
        value           =   0;
    }
    else
    if  ( bfa.isNumeric(entry) && entry < pallet.length )
    {
        acct            =   pallet[entry][0];
        value           =   pallet[entry][1];
    }
    else
    if ( entry == "x" )
    {
        // trigger distribution
        Distillery.methods.distribute().send( {"from": bfa.account, "gas": 4000000 } )
        .then(
            function distOK(x) {
                console.log(
                    "Distribute returned succesfully in block# "
                    + x.blockNumber
                    + " using "
                    + x.gasUsed
                    + " gas."
                );
                getlist();
            },
            function distFail(x) {
                bfa.fatal(
                    "Distribute returned errors in block# "
                    + x.blockNumber
                    + " using "
                    + x.gasUsed
                    + " gas."
                );
                console.log(x);
                process.exit( 1 );
            }
        );
        return;
    }
    else
    if ( entry == "" )
    {
        // Do nothing, basically just update the display
        return;
    }
    else
    {
        bfa.fatal("I don't know what to do with \""+entry+"\"." );
    }
    rl.question(
        "Adjust the "
        + notation[2]
        + " fill value of "
        + acct
        + " (setting to 0 is the same as deleting)\n"
        + "Amount?: ",
        (answer) => {
            if ( bfa.isNumeric(answer) )
            {
                answer      *=  notation[1];
                console.log("Sending update to the SC...");
                Distillery.methods.setEtherAllowance(acct,answer)
                .send( {"from": bfa.account } )
                .then(
                    function(a){
                        console.log("Update accepted.")
                        getlist();
                    },
                    function(b){
                        bfa.fatal(
                            "\nMaybe you are not authorized:\n"
                            +b
                            +"\n\n\nI think you should leave now.\n"
                        );
                    }
                )
            }
            else
                bfa.fatal( "I have no idea what to do with \""+answer+"\"." );
            rl.close;
        }
    );
}

function    displayBalances( pallet )
{
    var     n       =   pallet.length;
    var     i;
    pallet.sort(palletSort);
    console.log(
        "The contract's account ("
        + Distillery.contractaddress
        + ") has "
        + Math.floor(Distillery.contractbalance/notation[1]).toFixed(0)
        + " "
        + notation[2]
        + ".\n"
    );
    var     longest         =   1;
    for ( i=0; i<n; i++ )
    {
        var len =   (pallet[i][1]/notation[1]).toFixed(notation[0]).length;
        if ( len > longest )
            longest = len;
    }
    for ( i=0; i<n; i++ )
    {
        var     entry   =   pallet[i];
        if ( entry == undefined )
            console.log( i+": <undef>" );
        else
        {
            var     numstr  =   (pallet[i][1]/notation[1]).toFixed(notation[0]);
            while ( numstr.length < longest )
                numstr      =   " "+numstr;
            console.log(
                i
                + ": "
                + pallet[i][0]
                + " fills to "
                + numstr
                + " "
                + notation[2]
                + " (has "
                + Number(pallet[i][2]/notation[1]).toFixed(notation[0])
                + ")."
            );
        }
    }
    console.log("\n[ Q=quit x=distribute ]");
    rl.question("Which account to edit (enter index number of full account number)?: ",
        (answer) => {
            if ( answer != undefined )
            {
                if ( String(answer).toUpperCase() == 'Q' )
                    process.exit( 0 );
                else
                    editAccount(answer, pallet);
            }
            rl.close;
        }
    )
}

function    getlist()
{
    Distillery.methods.numberOfBeneficiaries()
    .call()
    .then(
        requestBalances,
        function beneficiaryFail(x){bfa.fatal(x)}
    );
}

init();