/******************************************************************************
 * Copyright © 2014-2016 The SuperNET Developers.                             *
 *                                                                            *
 * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at                  *
 * the top-level directory of this distribution for the individual copyright  *
 * holder information and the developer policies on copyright and licensing.  *
 *                                                                            *
 * Unless otherwise agreed in a custom licensing agreement, no part of the    *
 * SuperNET software, including this file may be copied, modified, propagated *
 * or distributed except according to the terms contained in the LICENSE file *
 *                                                                            *
 * Removal or modification of this copyright notice is prohibited.            *
 *                                                                            *
 ******************************************************************************/

#include "iguana777.h"
//#include "SuperNET.h"

#define RPCARGS struct supernet_info *myinfo,uint16_t port,struct iguana_info *coin,cJSON *params[],int32_t n,cJSON *json,char *remoteaddr,cJSON *array
#define GLUEARGS cJSON *json,struct supernet_info *myinfo,uint16_t port,struct iguana_info *coin,char *remoteaddr,cJSON *params[]

#define CALLGLUE myinfo,port,coin,remoteaddr,params

char *sglue(GLUEARGS,char *agent,char *method)
{
    char *retstr,*rpcretstr,*walletstr,checkstr[64],dcheckstr[64]; cJSON *retjson,*tmpjson,*result,*error,*wallet; int32_t i,j,len; int64_t val; double dval;
    if ( json == 0 )
        json = cJSON_CreateObject();
    printf("sglue.(%s)\n",jprint(json,0));
    jaddstr(json,"agent",agent);
    jaddstr(json,"method",method);
    jaddstr(json,"coin",coin->symbol);
    if ( (retstr= SuperNET_JSON(myinfo,json,remoteaddr,port)) != 0 )
    {
        if ( (retjson= cJSON_Parse(retstr)) != 0 )
        {
            if ( myinfo->dirty != 0 && myinfo->secret[0] != 0 )
            {
                if ( (wallet= iguana_walletjson(myinfo)) != 0 )
                {
                    //printf("WALLETSTR.(%s)\n",jprint(wallet,0));
                    if ( (walletstr= SuperNET_login(myinfo,coin,json,remoteaddr,myinfo->handle,myinfo->secret,myinfo->permanentfile,0)) != 0 )
                    {
                        free(walletstr);
                        walletstr = myinfo->decryptstr, myinfo->decryptstr = 0;
                        if ( walletstr != 0 && (tmpjson= cJSON_Parse(walletstr)) != 0 )
                        {
                            jdelete(tmpjson,"wallet");
                            jadd(tmpjson,"wallet",wallet);
                            if ( iguana_payloadupdate(myinfo,coin,jprint(tmpjson,1),0,0) == 0 )
                            {
                                printf("wallet updated\n");
                                myinfo->dirty = 0;
                            } else printf("iguana_payloadupdate error\n");
                        } else printf("error parsing decryptstr\n");
                        if ( walletstr != 0 )
                            free(walletstr);
                    } else printf("ERROR: dirty wallet is unsaved, iguana_payloadupdate error\n");
                } else printf("ERROR: dirty wallet is unsaved, unlock wallet\n");
            }
            if ( jobj(retjson,"tag") != 0 )
                jdelete(retjson,"tag");
            ///printf("RPCret.(%s) n.%d\n",jprint(retjson,0),cJSON_GetArraySize(retjson));
            result = cJSON_GetObjectItem(retjson,"result");
            error = cJSON_GetObjectItem(retjson,"error");
            if ( result != 0 && cJSON_GetArraySize(retjson) == 1 )
            {
                if ( (error == 0 || (error->type&0xff) == cJSON_NULL) && (result->type&0xff) != cJSON_NULL )
                {
                    rpcretstr = cJSON_Print(result);
                    len = (int32_t)strlen(rpcretstr);
                    if ( rpcretstr[0] == '"' && rpcretstr[len-1] == '"' )
                    {
                        for (i=1,j=0; i<len-1; i++,j++)
                            rpcretstr[j] = rpcretstr[i];
                        //rpcretstr[j++] = '\n';
                        rpcretstr[j] = 0;
                        free_json(retjson);
                        free(retstr);
                        return(rpcretstr);
                    }
                    else
                    {
                        dval = atof(rpcretstr);
                        sprintf(dcheckstr,"%.8f",dval);
                        val = atol(rpcretstr);
                        sprintf(checkstr,"%lld",(long long)val);
                        if ( strcmp(checkstr,rpcretstr) == 0 || strcmp(dcheckstr,rpcretstr) == 0 )
                        {
                            free_json(retjson);
                            free(retstr);
                            return(rpcretstr);
                        }
                    }
                    free(rpcretstr);
                }
                else if ( (error->type&0xff) != cJSON_NULL || (result->type&0xff) != cJSON_NULL )
                    printf("<<<<<<<<<<< bitcoind_RPC: post_process_bitcoind_RPC error.%s\n",retstr);
            }
            free_json(retjson);
        }
    }
    //free_json(json);
    return(retstr);
}

char *sglue1(GLUEARGS,char *agent,char *method,char *field,cJSON *obj)
{
    if ( json == 0 )
        json = cJSON_CreateObject();
    if ( obj != 0 && field != 0 )
        jadd(json,field,obj);
    params[0] = 0;
    //printf("sglue1.(%s)\n",jprint(json,0));
    return(sglue(json,CALLGLUE,agent,method));
}

char *sglueN(GLUEARGS,char *agent,char *method,char *field,double num)
{
    if ( json == 0 )
        json = cJSON_CreateObject();
    jaddnum(json,field,num);
    return(sglue(json,CALLGLUE,agent,method));
}

char *sglue2(GLUEARGS,char *agent,char *method,char *field0,cJSON *obj0,char *field1,cJSON *obj1)
{
    if ( json == 0 )
        json = cJSON_CreateObject();
    if ( obj1 != 0 && field1 != 0 )
        jadd(json,field1,obj1);
    params[1] = 0;
    //printf("sglue2.(%s)\n",jprint(json,0));
    return(sglue1(json,CALLGLUE,agent,method,field0,obj0));
}

char *sglue3(GLUEARGS,char *agent,char *method,char *field0,cJSON *obj0,char *field1,cJSON *obj1,char *field2,cJSON *obj2)
{
    if ( json == 0 )
        json = cJSON_CreateObject();
    if ( obj2 != 0 && field2 != 0 )
        jadd(json,field2,obj2);
    params[2] = 0;
    //printf("sglue3.(%s)\n",jprint(json,0));
    return(sglue2(json,CALLGLUE,agent,method,field0,obj0,field1,obj1));
}

char *sglue4(GLUEARGS,char *agent,char *method,char *field0,cJSON *obj0,char *field1,cJSON *obj1,char *field2,cJSON *obj2,char *field3,cJSON *obj3)
{
    if ( json == 0 )
        json = cJSON_CreateObject();
    if ( obj3 != 0 && field3 != 0 )
        jadd(json,field3,obj3);
    params[3] = 0;
    return(sglue3(json,CALLGLUE,agent,method,field0,obj0,field1,obj1,field2,obj2));
}

char *sglue5(GLUEARGS,char *agent,char *method,char *field0,cJSON *obj0,char *field1,cJSON *obj1,char *field2,cJSON *obj2,char *field3,cJSON *obj3,char *field4,cJSON *obj4)
{
    if ( json == 0 )
        json = cJSON_CreateObject();
    if ( obj4 != 0 && field4 != 0 )
        jadd(json,field4,obj4);
    params[4] = 0;
    return(sglue4(json,CALLGLUE,agent,method,field0,obj0,field1,obj1,field2,obj2,field3,obj3));
}

char *sglue6(GLUEARGS,char *agent,char *method,char *field0,cJSON *obj0,char *field1,cJSON *obj1,char *field2,cJSON *obj2,char *field3,cJSON *obj3,char *field4,cJSON *obj4,char *field5,cJSON *obj5)
{
    if ( json == 0 )
        json = cJSON_CreateObject();
    if ( obj5 != 0 && field5 != 0 )
        jadd(json,field5,obj5);
    params[5] = 0;
    return(sglue5(json,CALLGLUE,agent,method,field0,obj0,field1,obj1,field2,obj2,field3,obj3,field4,obj4));
}

// misc
static char *help(RPCARGS)
{
    return(sglue(0,CALLGLUE,"SuperNET","help"));
}

static char *stop(RPCARGS)
{
    return(sglue(0,CALLGLUE,"iguana","pausecoin"));
}

static char *sendalert(RPCARGS)
{
    return(sglue1(0,CALLGLUE,"iguana","sendalert","message",params[0]));
}

static char *SuperNET(RPCARGS)
{
    return(SuperNET_JSON(myinfo,json,remoteaddr,port));
}

static char *getrawmempool(RPCARGS)
{
    return(sglue(0,CALLGLUE,"iguana","getrawmempool"));
}

// peers
static char *getconnectioncount(RPCARGS)
{
    return(sglue(0,CALLGLUE,"iguana","getconnectioncount"));
}

static char *getpeerinfo(RPCARGS)
{
    return(sglue(0,CALLGLUE,"iguana","peers"));
}

static char *addnode(RPCARGS)
{
    char *mode,*cmd = 0;
    if ( (mode= jstr(params[1],0)) != 0 )
    {
        if ( strcmp(mode,"add") == 0 )
            cmd = "addnode";
        else if ( strcmp(mode,"remove") == 0 )
            cmd = "removenode";
        else if ( strcmp(mode,"onetry") == 0 )
            cmd = "onetry";
        if ( cmd != 0 )
            return(sglue1(0,CALLGLUE,"iguana",cmd,"ipaddr",params[0]));
    }
    // addnode	<node> <add/remove/onetry>	version 0.8 Attempts add or remove <node> from the addnode list or try a connection to <node> once.	N
    return(clonestr("{\"error\":\"invalid addnode parameter\"}"));
}

// address and pubkeys
static char *validateaddress(RPCARGS)
{
    return(sglue1(0,CALLGLUE,"bitcoinrpc","validateaddress","address",params[0]));
}

static char *validatepubkey(RPCARGS)
{
    return(sglue1(0,CALLGLUE,"bitcoinrpc","validatepubkey","pubkey",params[0]));
}

static char *createmultisig(RPCARGS)
{
    return(sglue2(0,CALLGLUE,"bitcoinrpc","createmultisig","M",params[0],"pubkeys",params[1]));
}

static char *addmultisigaddress(RPCARGS)
{
    return(sglue3(0,CALLGLUE,"bitcoinrpc","addmultisigaddress","M",params[0],"pubkeys",params[1],"account",params[2]));
}

// blockchain
static char *getinfo(RPCARGS)
{
    return(sglue(0,CALLGLUE,"bitcoinrpc","getinfo"));
}

static char *getbestblockhash(RPCARGS)
{
    return(sglue(0,CALLGLUE,"bitcoinrpc","getbestblockhash"));
}

static char *getblockcount(RPCARGS)
{
    return(sglue(0,CALLGLUE,"bitcoinrpc","getblockcount"));
}

static char *getdifficulty(RPCARGS)
{
    return(sglue(0,CALLGLUE,"bitcoinrpc","getdifficulty"));
}

static char *getblock(RPCARGS)
{
    cJSON *obj;
    if ( params[1] == 0 )
        params[1] = cJSON_CreateNumber(1);
    else
    {
        obj = params[1];
        if ( is_cJSON_False(params[1]) != 0 )
            params[1] = cJSON_CreateNumber(0);
        else params[1] = cJSON_CreateNumber(1);
        free_json(obj);
    }
    return(sglue3(0,CALLGLUE,"bitcoinrpc","getblock","blockhash",params[0],"verbose",params[1],"remoteonly",params[2]));
}

static char *getblockhash(RPCARGS)
{
    return(sglue1(0,CALLGLUE,"bitcoinrpc","getblockhash","height",params[0]));
}

static char *gettransaction(RPCARGS)
{
    return(sglue1(0,CALLGLUE,"bitcoinrpc","gettransaction","txid",params[0]));
}

static char *listtransactions(RPCARGS)
{
    return(sglue3(0,CALLGLUE,"bitcoinrpc","listtransactions","account",params[0],"count",params[1],"from",params[2]));
}

static char *getreceivedbyaddress(RPCARGS)
{
    return(sglue2(0,CALLGLUE,"bitcoinrpc","getreceivedbyaddress","address",params[0],"minconfs",params[1]));
}

static char *listreceivedbyaddress(RPCARGS)
{
    return(sglue2(0,CALLGLUE,"bitcoinrpc","listreceivedbyaddress","minconf",params[0],"includeempty",params[1]));
}

static char *listsinceblock(RPCARGS)
{
    return(sglue2(0,CALLGLUE,"bitcoinrpc","listsinceblock","blockhash",params[0],"target",params[1]));
}

// waccount and waddress funcs
static char *getreceivedbyaccount(RPCARGS)
{
    return(sglue2(0,CALLGLUE,"bitcoinrpc","getreceivedbyaccount","account",params[0],"minconfs",params[1]));
}

static char *listreceivedbyaccount(RPCARGS)
{
    return(sglue2(0,CALLGLUE,"bitcoinrpc","listreceivedbyaccount","account",params[0],"includeempty",params[1]));
}

static char *getnewaddress(RPCARGS)
{
    return(sglue1(0,CALLGLUE,"bitcoinrpc","getnewaddress","account",params[0]));
}

static char *vanitygen(RPCARGS)
{
    return(sglue1(0,CALLGLUE,"bitcoinrpc","vanitygen","vanity",params[0]));
}

static char *makekeypair(RPCARGS)
{
    return(sglue(0,CALLGLUE,"bitcoinrpc","makekeypair"));
}

static char *getaccountaddress(RPCARGS)
{
    return(sglue1(0,CALLGLUE,"bitcoinrpc","getaccountaddress","account",params[0]));
}

static char *setaccountrpc(RPCARGS)
{
    return(sglue2(0,CALLGLUE,"bitcoinrpc","setaccount","address",params[0],"account",params[1]));
}

static char *getaccount(RPCARGS)
{
    return(sglue1(0,CALLGLUE,"bitcoinrpc","getaccount","address",params[0]));
}

static char *getaddressesbyaccount(RPCARGS)
{
    return(sglue1(0,CALLGLUE,"bitcoinrpc","getaddressesbyaccount","account",params[0]));
}

static char *listaddressgroupings(RPCARGS)
{
    return(sglue(0,CALLGLUE,"bitcoinrpc","listaddressgroupings"));
}

static char *getbalance(RPCARGS)
{
    return(sglue2(0,CALLGLUE,"bitcoinrpc","getbalance","account",params[0],"minconf",params[1]));
}

// wallet
static char *listaccounts(RPCARGS)
{
    return(sglue1(0,CALLGLUE,"bitcoinrpc","listaccounts","minconf",params[0]));
}

static char *dumpprivkey(RPCARGS)
{
    return(sglue1(0,CALLGLUE,"bitcoinrpc","dumpprivkey","address",params[0]));
}

static char *importprivkey(RPCARGS)
{
    return(sglue3(0,CALLGLUE,"bitcoinrpc","importprivkey","wif",params[0],"account",params[1],"rescan",params[2]));
}

static char *dumpwallet(RPCARGS)
{
    return(sglue(0,CALLGLUE,"bitcoinrpc","dumpwallet"));
}

static char *importwallet(RPCARGS)
{
    return(sglue1(0,CALLGLUE,"bitcoinrpc","importwallet","wallet",params[0]));
}

static char *walletpassphrase(RPCARGS)
{
    return(sglue3(0,CALLGLUE,"bitcoinrpc","walletpassphrase","password",params[0],"permanentfile",params[2],"timeout",params[1]));
}

static char *walletpassphrasechange(RPCARGS)
{
    return(sglue4(0,CALLGLUE,"bitcoinrpc","walletpassphrasechange","oldpassphrase",params[0],"newpassphrase",params[1],"oldpermanentfile",params[2],"oldpermanentfile",params[3]));
}

static char *walletlock(RPCARGS)
{
    return(sglue(0,CALLGLUE,"bitcoinrpc","walletlock"));
}

static char *encryptwallet(RPCARGS)
{
    return(sglue2(0,CALLGLUE,"bitcoinrpc","encryptwallet","passphrase",params[0],"password",params[1]));
}

static char *checkwallet(RPCARGS)
{
    return(sglue(0,CALLGLUE,"bitcoinrpc","checkwallet"));
}

static char *repairwallet(RPCARGS)
{
    return(sglue(0,CALLGLUE,"bitcoinrpc","repairwallet"));
}

static char *backupwallet(RPCARGS)
{
    return(sglue1(0,CALLGLUE,"bitcoinrpc","backupwallet","filename",params[0]));
}

// messages
static char *signmessage(RPCARGS)
{
    return(sglue2(0,CALLGLUE,"bitcoinrpc","signmessage","address",params[0],"message",params[1]));
}

static char *verifymessage(RPCARGS)
{
    return(sglue3(0,CALLGLUE,"bitcoinrpc","verifymessage","address",params[0],"sig",params[1],"message",params[2]));
}

// unspents
static char *listunspent(RPCARGS)
{
    return(sglue3(0,CALLGLUE,"bitcoinrpc","listunspent","minconf",params[0],"maxconf",params[1],"array",params[2]));
}

static char *lockunspent(RPCARGS)
{
    return(sglue2(0,CALLGLUE,"bitcoinrpc","lockunspent","flag",params[0],"array",params[1]));
}

static char *listlockunspent(RPCARGS)
{
    return(sglue(0,CALLGLUE,"bitcoinrpc","listlockunspent"));
}

static char *gettxout(RPCARGS)
{
    return(sglue3(0,CALLGLUE,"bitcoinrpc","gettxout","txid",params[0],"vout",params[1],"mempool",params[2]));
}

static char *gettxoutsetinfo(RPCARGS)
{
    return(sglue(0,CALLGLUE,"bitcoinrpc","gettxoutsetinfo"));
}

// payments
static char *sendtoaddress(RPCARGS)
{
    return(sglue4(0,CALLGLUE,"bitcoinrpc","sendtoaddress","address",params[0],"amount",params[1],"comment",params[2],"comment2",params[3]));
}

static char *movecmd(RPCARGS)
{
    return(sglue5(0,CALLGLUE,"bitcoinrpc","move","fromaccount",params[0],"toaccount",params[1],"amount",params[2],"minconf",params[3],"comment",params[4]));
}

static char *sendfrom(RPCARGS)
{
    return(sglue6(0,CALLGLUE,"bitcoinrpc","sendfrom","fromaccount",params[0],"toaddress",params[1],"amount",params[2],"minconf",params[3],"comment",params[4],"comment2",params[5]));
}

static char *sendmany(RPCARGS)
{
    return(sglue4(0,CALLGLUE,"bitcoinrpc","sendmany","fromaccount",params[0],"payments",params[1],"minconf",params[2],"comment",params[3]));
}

static char *settxfee(RPCARGS)
{
    return(sglue1(0,CALLGLUE,"bitcoinrpc","settxfee","amount",params[0]));
}

// rawtransaction
static char *getrawtransaction(RPCARGS)
{
    return(sglue2(0,CALLGLUE,"bitcoinrpc","getrawtransaction","txid",params[0],"verbose",params[1]));
}

static char *createrawtransaction(RPCARGS)
{
    return(sglue3(0,CALLGLUE,"bitcoinrpc","createrawtransaction","vins",params[0],"vouts",params[1],"locktime",params[2]));
}

static char *decoderawtransaction(RPCARGS)
{
    return(sglue1(0,CALLGLUE,"bitcoinrpc","decoderawtransaction","rawtx",params[0]));
}

static char *validaterawtransaction(RPCARGS)
{
    return(sglue1(0,CALLGLUE,"bitcoinrpc","validaterawtransaction","rawtx",params[0]));
}

static char *decodescript(RPCARGS)
{
    return(sglue1(0,CALLGLUE,"bitcoinrpc","decodescript","scriptstr",params[0]));
}

static char *signrawtransaction(RPCARGS)
{
    return(sglue4(0,CALLGLUE,"bitcoinrpc","signrawtransaction","rawtx",params[0],"vins",params[1],"privkeys",params[2],"sighash",params[3]));
}

static char *sendrawtransaction(RPCARGS)
{
    return(sglue1(0,CALLGLUE,"bitcoinrpc","sendrawtransaction","rawtx",params[0]));
}

static char *getrawchangeaddress(RPCARGS)
{
    return(sglue1(0,CALLGLUE,"bitcoinrpc","getrawchangeaddress","account",params[0]));
}

#define true 1
#define false 0
struct RPC_info { char *name; char *(*rpcfunc)(RPCARGS); int32_t flag0,remoteflag; } RPCcalls[] =
{
    { "validatepubkey",         &validatepubkey,         true,   true },
    { "makekeypair",            &makekeypair,            false,  false },
    { "listunspent",            &listunspent,            false,  false },
    { "getblockhash",           &getblockhash,           false,  true },
    { "walletpassphrase",       &walletpassphrase,       true,   false },
    { "SuperNET",               &SuperNET,               false,  true },
  //{ "SuperNETb",              &SuperNET,               false,  true },
    { "help",                   &help,                   true,   false },
    { "stop",                   &stop,                   true,   true },
    { "getbestblockhash",       &getbestblockhash,       true,   true },
    { "getblockcount",          &getblockcount,          true,   true },
    { "getdifficulty",          &getdifficulty,          true,   true },
    { "getconnectioncount",     &getconnectioncount,     true,   true },
    { "getpeerinfo",            &getpeerinfo,            true,   true },
    { "getinfo",                &getinfo,                true,   true },
    { "getnewaddress",          &getnewaddress,          true,   false },
    { "getnewpubkey",           &makekeypair,            true,   false },
    { "getaccountaddress",      &getaccountaddress,      true,   false },
    { "setaccount",             &setaccountrpc,             true,   false },
    { "getaccount",             &getaccount,             false,  false },
    { "getaddressesbyaccount",  &getaddressesbyaccount,  true,   false },
    { "sendtoaddress",          &sendtoaddress,          false,  false },
    { "getreceivedbyaddress",   &getreceivedbyaddress,   false,  false },
    { "getreceivedbyaccount",   &getreceivedbyaccount,   false,  false },
    { "listreceivedbyaddress",  &listreceivedbyaddress,  false,  false },
    { "listreceivedbyaccount",  &listreceivedbyaccount,  false,  false },
    { "backupwallet",           &backupwallet,           true,   false },
    { "walletpassphrasechange", &walletpassphrasechange, false,  false },
    { "walletlock",             &walletlock,             true,   false },
    { "encryptwallet",          &encryptwallet,          false,  false },
    { "validateaddress",        &validateaddress,        true,   true },
    { "getbalance",             &getbalance,             false,  false },
    { "move",                   &movecmd,                false,  false },
    { "sendfrom",               &sendfrom,               false,  false },
    { "sendmany",               &sendmany,               false,  false },
    { "addmultisig",            &addmultisigaddress,     false,  false },
    { "addmultisigaddress",     &addmultisigaddress,     false,  false },
    { "getblock",               &getblock,               false,  true },
    { "gettransaction",         &gettransaction,         false,  true },
    { "listtransactions",       &listtransactions,       false,  false },
    { "listaddressgroupings",   &listaddressgroupings,   false,  false },
    { "signmessage",            &signmessage,            false,  false },
    { "verifymessage",          &verifymessage,          false,  false },
    { "listaccounts",           &listaccounts,           false,  false },
    { "settxfee",               &settxfee,               false,  false },
    { "listsinceblock",         &listsinceblock,         false,  false },
    { "dumpprivkey",            &dumpprivkey,            false,  false },
    { "dumpwallet",             &dumpwallet,             true,   false },
    { "importwallet",           &importwallet,           false,  false },
    { "importprivkey",          &importprivkey,          false,  false },
    { "getrawtransaction",      &getrawtransaction,      false,  false },
    { "createrawtransaction",   &createrawtransaction,   false,  false },
    { "validaterawtransaction", &validaterawtransaction,   false,  true },
    { "decoderawtransaction",   &decoderawtransaction,   false,  true },
    { "decodescript",           &decodescript,           false,  true },
    { "signrawtransaction",     &signrawtransaction,     false,  false },
    { "sendrawtransaction",     &sendrawtransaction,     false,  true },
    { "checkwallet",            &checkwallet,            false,  false },
    { "repairwallet",           &repairwallet,           false,  false },
    { "sendalert",              &sendalert,              false,  false },
    //
    { "createmultisig",         &createmultisig,         false,  false },
    { "addnode",                &addnode,                false,  false },
    { "getrawmempool",          &getrawmempool,          false,  true },
    { "getrawchangeaddress",    &getrawchangeaddress,    false,  false },
    { "listlockunspent",        &listlockunspent,        false,  false },
    { "lockunspent",            &lockunspent,            false,  false },
    { "gettxout",               &gettxout,               false,  true },
    { "gettxoutsetinfo",        &gettxoutsetinfo,        false,  true },
    { "vanitygen",              &vanitygen,              false,  false }
#ifdef PEGGY
    //{ "peggytx",                &peggytx,                true,   false },
    //{ "peggypayments",          &peggypayments,          true,   false },
    //{ "getpeggyblock",          &getpeggyblock,          true,   false },
#endif
    //{ "resendtx",               &resendtx,               false,  true},
 // { "addredeemscript",        &addredeemscript,        false,  false },
    //  { "getrawmempool",          &getrawmempool,          true,   false },
    //    { "getdifficulty",          &getdifficulty,          true,   false },
    //    { "getsubsidy",             &getsubsidy,             true,   false },
    //    { "getmininginfo",          &getmininginfo,          true,   false },
    //    { "getstakinginfo",         &getstakinginfo,         true,   false },
    // { "getblockbynumber",       &getblockbynumber,       false,  false },
    //{ "getwork",                &getwork,                true,   false },
    //{ "getworkex",              &getworkex,              true,   false },
    // { "keypoolrefill",          &keypoolrefill,          true,   false },
    //{ "getblocktemplate",       &getblocktemplate,       true,   false },
    //{ "submitblock",            &submitblock,            false,  false },
    // { "getcheckpoint",          &getcheckpoint,          true,   false },
    // { "reservebalance",         &reservebalance,         false,  true},
};

int32_t is_bitcoinrpc(struct supernet_info *myinfo,char *method,char *remoteaddr)
{
    int32_t i;
    for (i=0; i<sizeof(RPCcalls)/sizeof(*RPCcalls); i++)
    {
        if ( strcmp(RPCcalls[i].name,method) == 0 )
        {
            if ( remoteaddr == 0 || remoteaddr[0] == 0 || strcmp(remoteaddr,"127.0.0.1") == 0 )
            return(1);
            if ( RPCcalls[i].remoteflag != 0 && myinfo->publicRPC != 0 )
                return(i);
        }
    }
    return(-1);
}

char *iguana_bitcoinrpc(struct supernet_info *myinfo,uint16_t port,struct iguana_info *coin,char *method,cJSON *params[16],int32_t n,cJSON *json,char *remoteaddr,cJSON *array)
{
    int32_t i;
    for (i=0; i<sizeof(RPCcalls)/sizeof(*RPCcalls); i++)
    {
        if ( strcmp(RPCcalls[i].name,method) == 0 )
            return((*RPCcalls[i].rpcfunc)(myinfo,port,coin,params,n,json,remoteaddr,array));
    }
    return(clonestr("{\"error\":\"invalid coin address\"}"));
}

char *iguana_bitcoinRPC(struct supernet_info *myinfo,char *method,cJSON *json,char *remoteaddr,uint16_t port)
{
    cJSON *params[16],*array; struct iguana_info *tmp,*coin = 0; char symbol[16]; int32_t i,c,n; char *retstr = 0;
    symbol[0] = 0;
    memset(params,0,sizeof(params));
    //printf("bitcoinRPC\n");
    if ( json != 0 )
    {
        if ( port == myinfo->rpcport )
        {
            if ( jstr(json,"coin") == 0 )
            {
                strcpy(symbol,myinfo->rpcsymbol);
                if ( symbol[0] == 0 )
                {
                    c = 'B';
                    sprintf(symbol,"%c%c%c%c",c,'T',c+1,c+2);
                }
            }
            else
            {
                safecopy(symbol,jstr(json,"coin"),sizeof(symbol));
                for (i=0; symbol[i]!=0; i++)
                    symbol[i] = toupper((int32_t)symbol[i]);
            }
            if ( myinfo->rpcsymbol[0] == 0 )
                strcpy(myinfo->rpcsymbol,symbol);
        }
        else
        {
            //portable_mutex_lock(&myinfo->allcoins_mutex);
            HASH_ITER(hh,myinfo->allcoins,coin,tmp)
            {
                if ( coin->chain->rpcport == port )
                    break;
                else coin = 0;
            }
            //portable_mutex_unlock(&myinfo->allcoins_mutex);
        }
        if ( coin == 0 && symbol[0] != 0 )
            coin = iguana_coinfind(symbol);
        if ( coin != 0 )
            safecopy(symbol,coin->symbol,sizeof(symbol));
        //printf("method.(%s) (%s) remote.(%s) symbol.(%s)\n",method,jprint(json,0),remoteaddr,symbol);
        if ( method != 0 && symbol[0] != 0 && (coin != 0 || (coin= iguana_coinfind(symbol)) != 0) )
        {
            if ( (array= jarray(&n,json,"params")) == 0 )
            {
                i = 0, n = 0;
            }
            else if ( n > 0 )
            {
                //printf("n.%d (%s)\n",n,jstr(array,0));
                for (i=n-1; i>=0; i--)
                {
                    params[i] = cJSON_DetachItemFromArray(array,i);//;
                    //printf("add params[%d] of %d <- (%s) %p.(%p %p)\n",i,n,jprint(params[i],0),params[i],params[i]->next,params[i]->prev);
                }
            }
            retstr = iguana_bitcoinrpc(myinfo,IGUANA_RPCPORT,coin,method,params,n,json,remoteaddr,array);
            if ( n > 0 )
                for (i=0; i<n; i++)
                    if ( params[i] != 0 )
                        free_json(params[i]);
        }
    }
    if ( retstr == 0 )
        retstr = clonestr("{\"error\":\"null return from iguana_bitcoinRPC\"}");
    return(retstr);
}

int32_t Supernet_lineparse(char *key,int32_t keymax,char *value,int32_t valuemax,char *src)
{
    int32_t a,b,c,n = 0; //char *origkey=key,*origvalue=value;
    key[0] = value[0] = 0;
    while ( (c= src[n]) == ' ' || c == '\t' || c == '\n' || c == '\t' )
        n++;
    while ( (c= src[n]) != ':' && c != 0 )
    {
        *key++ = c;
        //printf("(%c) ",c);
        if ( ++n >= keymax-1 )
        {
            *key = 0;
            printf("lineparse overflow key.(%s)\n",src);
            return(-1);
        }
    }
    *key = 0;
    //printf("-> key.(%s)\n",origkey);
    if ( src[n] != ':' )
        return(n);
    n++;
    while ( (c= src[n]) == ' ' || c == '\t' )
        n++;
    while ( (c= src[n]) != 0 && c != '\r' && c != '\n' )
    {
        if ( c == '%' && (a= src[n+1]) != 0 && (b= src[n+2]) != 0 )
            c = ((unhex(a) << 4) | unhex(b)), n += 2;
        *value++ = c;
        n++;
        if ( n >= valuemax-1 )
        {
            *value = 0;
            printf("lineparse overflow.(%s)\n",src);
            return(-1);
        }
    }
    *value = 0;
    if ( src[n] != 0 )
    {
        n++;
        while ( (c= src[n]) == '\r' || c == '\n' )
            n++;
    }
    //printf("key.(%s) value.(%s)\n",origkey,origvalue);
    return(n);
}

cJSON *SuperNET_urlconv(char *value,int32_t bufsize,char *urlstr)
{
    int32_t i,n,totallen,datalen,len = 0; cJSON *json,*array; char key[8192],*data;
    json = cJSON_CreateObject();
    array = cJSON_CreateArray();
    totallen = (int32_t)strlen(urlstr);
    while ( 1 )
    {
        for (i=len; urlstr[i]!=0; i++)
            if ( urlstr[i] == '\r' || urlstr[i] == '\n' )
                break;
        if ( i == len && (urlstr[len] == '\r' || urlstr[len] == '\n') )
        {
            len++;
            continue;
        }
        urlstr[i] = 0;
        //printf("URLSTR[%d]=%s\n",i,&urlstr[len]);
        if ( (n= Supernet_lineparse(key,sizeof(key),value,bufsize,&urlstr[len])) > 0 )
        {
            if ( value[0] != 0 )
                jaddstr(json,key,value);
            else jaddistr(array,key);
            len += (n + 1);
            if ( strcmp(key,"Content-Length") == 0 && (datalen= atoi(value)) > 0 )
            {
                data = &urlstr[totallen - datalen];
                data[-1] = 0;
                //printf("post.(%s) (%c)\n",data,data[0]);
                jaddstr(json,"POST",data);
            }
        } else break;
    }
    jadd(json,"lines",array);
    //printf("urlconv.(%s)\n",jprint(json,0));
    return(json);
}

char *SuperNET_rpcparse(struct supernet_info *myinfo,char *retbuf,int32_t bufsize,int32_t *jsonflagp,int32_t *postflagp,char *urlstr,char *remoteaddr,char *filetype,uint16_t port)
{
    cJSON *tokens,*argjson,*origargjson,*json = 0; long filesize;
    char symbol[16],buf[4096],urlmethod[16],*data,url[1024],*retstr,*filestr,*token = 0; int32_t i,j,n,num=0;
    //printf("rpcparse.(%s)\n",urlstr);
    for (i=0; i<sizeof(urlmethod)-1&&urlstr[i]!=0&&urlstr[i]!=' '; i++)
        urlmethod[i] = urlstr[i];
    urlmethod[i++] = 0;
    n = i;
    //printf("URLMETHOD.(%s)\n",urlmethod);
    *postflagp = (strcmp(urlmethod,"POST") == 0);
    for (i=0; i<sizeof(url)-1&&urlstr[n+i]!=0&&urlstr[n+i]!=' '; i++)
        url[i] = urlstr[n+i];
    url[i++] = 0;
    n += i;
    j = i = 0;
    filetype[0] = 0;
    //printf("url.(%s) method.(%s)\n",&url[i],urlmethod);
    if ( strcmp(&url[i],"/") == 0 && strcmp(urlmethod,"GET") == 0 )
    {
        static int counter;
        *jsonflagp = 1;
        if ( counter++ == 0 || (filestr= OS_filestr(&filesize,"index7778.html")) == 0 )
        {
            printf("call htmlstr\n");
            if ( (filestr= SuperNET_htmlstr("index7778.html",retbuf,bufsize,0)) != 0 )
                printf("created index7778.html size %d\n",(int32_t)strlen(filestr));
            else printf("got null filestr\n");
        }
        if ( filestr != 0 )
            return(filestr);
        else return(clonestr("{\"error\":\"cant find index7778\"}"));
    }
    else if ( (filestr= OS_filestr(&filesize,url+1)) != 0 )
    {
        *jsonflagp = 1;
        for (i=(int32_t)strlen(url)-1; i>0; i--)
            if ( url[i] == '.' || url[i] == '/' )
                break;
        if ( url[i] == '.' )
            strcpy(filetype,url+i+1);
        //printf("return filetype.(%s) size.%ld\n",filetype,filesize);
        return(filestr);
    }
    if ( strncmp(&url[i],"/api",strlen("/api")) == 0 )
    {
        *jsonflagp = 1;
        i += strlen("/api");
    } else *jsonflagp = 0;
    if ( strncmp(&url[i],"/bitmap",strlen("/bitmap")) == 0 )
    {
        i += strlen("/bitmap");
        *jsonflagp = 2;
        if ( url[i] == '/' )
            i++;
        iguana_bitmap(retbuf,bufsize,&url[i]);
        return(retbuf);
    }
    //printf("URL.(%s)\n",url);
    if ( strcmp(url,"/favicon.ico") == 0 )
    {
        *jsonflagp = 1;
        return(0);
    }
    if ( url[i] != '/' )
        token = &url[i];
    n = i;
    tokens = cJSON_CreateArray();
    for (; url[i]!=0; i++)
    {
        //printf("i.%d (%c)\n",i,url[i]);
        if ( url[i] == '/' )
        {
            url[i] = 0;
            if ( token != 0 )
            {
                //printf("TOKEN.(%s) i.%d\n",token,i);
                jaddistr(tokens,token);
                num++;
            }
            token = &url[i+1];
            i++;
            //printf("new token.(%s) i.%d\n",token,i+1);
            continue;
        }
    }
    if ( token != 0 )
    {
        //printf("add token.(%s)\n",token);
        jaddistr(tokens,token);
        num++;
    }
    argjson = cJSON_CreateObject();
    if ( num > 0 )
        jaddstr(argjson,"agent",jstri(tokens,0));
    if ( num > 1 )
        jaddstr(argjson,"method",jstri(tokens,1));
    //printf("urlstr.(%s)\n",urlstr+n);
    if ( (json= SuperNET_urlconv(retbuf,bufsize,urlstr+n)) != 0 )
    {
        jadd(json,"tokens",tokens);
        jaddstr(json,"urlmethod",urlmethod);
        if ( (data= jstr(json,"POST")) == 0 || (argjson= cJSON_Parse(data)) == 0 )
        {
            if ( (n= cJSON_GetArraySize(tokens)) > 0 )
            {
                if ( n > 1 )
                {
                    if ( jstri(tokens,1) != 0 )
                    {
                        char *key,*value;
                        strcpy(buf,jstri(tokens,1));
                        key = value = 0;
                        i = 0;
                        for (; buf[i]!=0; i++)
                        {
                            if ( buf[i] == '?' )
                            {
                                buf[i] = 0;
                                jdelete(argjson,"method");
                                jaddstr(argjson,"method",buf);
                                i++;
                                key = &buf[i];
                                break;
                            }
                        }
                        while ( buf[i] != 0 )
                        {
                            //printf("iter.[%s]\n",&buf[i]);
                            if ( buf[i] != 0 && key != 0 )
                            {
                                for (; buf[i]!=0; i++)
                                {
                                    if ( buf[i] == '=' )
                                    {
                                        buf[i] = 0;
                                        i++;
                                        //printf("got key.(%s)\n",key);
                                        value = &buf[i];
                                        break;
                                    }
                                }
                                if ( buf[i] != 0 && value != 0 )
                                {
                                    for (; buf[i]!=0; i++)
                                    {
                                        if ( buf[i] == '&' )
                                        {
                                            buf[i] = 0;
                                            jaddstr(argjson,key,value);
                                            i++;
                                            //printf("got value.(%s)\n",value);
                                            value = 0;
                                            key = &buf[i];
                                            break;
                                        }
                                        else if ( buf[i] == '+' )
                                            buf[i] = ' ';
                                    }
                                }
                            }
                        }
                        if ( key != 0 && value != 0 )
                            jaddstr(argjson,key,value);
                    }
                    else
                    {
                        //jdelete(argjson,"method");
                        //jaddstr(argjson,"method",buf);
                    }
                }
                for (i=2; i<n; i++)
                {
                    if ( i == n-1 )
                        jaddstr(argjson,"data",jstri(tokens,i));
                    else
                    {
                        if ( strcmp(jstri(tokens,i),"coin") == 0 && strlen(jstri(tokens,i+1)) < 8 )
                        {
                            strcpy(symbol,jstri(tokens,i+1));
                            touppercase(symbol);
                            jaddstr(argjson,jstri(tokens,i),symbol);
                        } else jaddstr(argjson,jstri(tokens,i),jstri(tokens,i+1));
                        i++;
                    }
                }
            }
        }
        if ( is_cJSON_Array(argjson) != 0 && (n= cJSON_GetArraySize(argjson)) > 0 )
        {
            cJSON *retitem,*retarray = cJSON_CreateArray();
            origargjson = argjson;
            for (i=0; i<n; i++)
            {
                argjson = jitem(origargjson,i);
                //printf("after urlconv.(%s) argjson.(%s)\n",jprint(json,0),jprint(argjson,0));
                if ( (retstr= SuperNET_JSON(myinfo,argjson,remoteaddr,port)) != 0 )
                {
                    if ( (retitem= cJSON_Parse(retstr)) != 0 )
                        jaddi(retarray,retitem);
                    free(retstr);
                }
                //printf("(%s) {%s} -> (%s) postflag.%d (%s)\n",urlstr,jprint(argjson,0),cJSON_Print(json),*postflagp,retstr);
            }
            free_json(origargjson);
            retstr = jprint(retarray,1);
        } else retstr = SuperNET_JSON(myinfo,argjson,remoteaddr,port);
        return(retstr);
    }
    *jsonflagp = 1;
    return(clonestr("{\"error\":\"couldnt process packet\"}"));
}

int32_t iguana_getcontentlen(char *buf,int32_t recvlen)
{
    char *str,*clenstr = "Content-Length: "; int32_t len = -1;
    if ( (str= strstr(buf,clenstr)) != 0 )
    {
        //printf("strstr.(%s)\n",str);
        str += strlen(clenstr);
        len = atoi(str);
        //printf("len.%d\n",len);
    }
    return(len);
}

int32_t iguana_getheadersize(char *buf,int32_t recvlen)
{
    char *str,*delim = "\r\n\r\n";
    if ( (str= strstr(buf,delim)) != 0 )
        return((int32_t)(((long)str - (long)buf) + strlen(delim)));
    return(recvlen);
}

void iguana_rpcloop(void *args)
{
    static char *jsonbuf;
    uint16_t port; struct supernet_info *myinfo = args; char filetype[128],content_type[128];
    int32_t recvlen,flag,bindsock,postflag,contentlen,sock,remains,numsent,jsonflag,hdrsize,len;
    socklen_t clilen; char helpname[512],remoteaddr[64],*buf,*retstr,*space;//,*retbuf; ,n,i,m
    struct sockaddr_in cli_addr; uint32_t ipbits,i,size = IGUANA_WIDTH*IGUANA_HEIGHT*16 + 512;
    if ( (port= myinfo->argport) == 0 )
        port = myinfo->rpcport;
    else myinfo->argport = 0;
    if ( jsonbuf == 0 )
        jsonbuf = calloc(1,IGUANA_MAXPACKETSIZE);
    while ( (bindsock= iguana_socket(1,"127.0.0.1",port)) < 0 )
    {
        //if ( coin->MAXPEERS == 1 )
        //    break;
        //exit(-1);
        sleep(3);
    }
    printf(">>>>>>>>>> iguana_rpcloop 127.0.0.1:%d bind sock.%d iguana API enabled <<<<<<<<<\n",port,bindsock);
    space = calloc(1,size);
    while ( bindsock >= 0 )
    {
        clilen = sizeof(cli_addr);
        //printf("ACCEPT (%s:%d) on sock.%d\n","127.0.0.1",port,bindsock);
        sock = accept(bindsock,(struct sockaddr *)&cli_addr,&clilen);
        if ( sock < 0 )
        {
            //printf("iguana_rpcloop ERROR on accept usock.%d\n",sock);
            continue;
        }
        memcpy(&ipbits,&cli_addr.sin_addr.s_addr,sizeof(ipbits));
        expand_ipbits(remoteaddr,ipbits);
        memset(jsonbuf,0,IGUANA_MAXPACKETSIZE);
        remains = (int32_t)(IGUANA_MAXPACKETSIZE - 1);
        buf = jsonbuf;
        recvlen = flag = 0;
        retstr = 0;
        while ( remains > 0 )
        {
            //printf("flag.%d remains.%d recvlen.%d\n",flag,remains,recvlen);
            if ( (len= (int32_t)recv(sock,buf,remains,0)) < 0 )
            {
                if ( errno == EAGAIN )
                {
                    printf("EAGAIN for len %d, remains.%d\n",len,remains);
                    usleep(10000);
                }
                break;
            }
            else
            {
                if ( len > 0 )
                {
                    buf[len] = 0;
                    if ( recvlen == 0 )
                    {
                        if ( (contentlen= iguana_getcontentlen(buf,recvlen)) > 0 )
                        {
                            hdrsize = iguana_getheadersize(buf,recvlen);
                            if ( hdrsize > 0 )
                            {
                                if ( len < (hdrsize + contentlen) )
                                {
                                    remains = (hdrsize + contentlen) - len;
                                    buf = &buf[len];
                                    flag = 1;
                                    //printf("got.(%s) %d remains.%d of len.%d contentlen.%d hdrsize.%d remains.%d\n",buf,recvlen,remains,len,contentlen,hdrsize,(hdrsize+contentlen)-len);
                                    continue;
                                }
                            }
                        }
                    }
                    recvlen += len;
                    remains -= len;
                    buf = &buf[len];
                    if ( flag == 0 || remains <= 0 )
                        break;
                }
                else
                {
                    usleep(10000);
                //printf("got.(%s) %d remains.%d of total.%d\n",jsonbuf,recvlen,remains,len);
                //retstr = iguana_rpcparse(space,size,&postflag,jsonbuf);
                    if ( flag == 0 )
                        break;
                }
            }
        }
        content_type[0] = 0;
        if ( recvlen > 0 )
        {
            retstr = SuperNET_rpcparse(myinfo,space,size,&jsonflag,&postflag,jsonbuf,remoteaddr,filetype,port);
            if ( filetype[0] != 0 )
            {
                static cJSON *mimejson; char *tmp,*typestr=0; long tmpsize;
                sprintf(helpname,"%s/mime.json",GLOBAL_HELPDIR);
                if ( (tmp= OS_filestr(&tmpsize,helpname)) != 0 )
                {
                    mimejson = cJSON_Parse(tmp);
                    free(tmp);
                }
                if ( mimejson != 0 )
                {
                    if ( (typestr= jstr(mimejson,filetype)) != 0 )
                        sprintf(content_type,"Content-Type: %s\r\n",typestr);
                } else printf("parse error.(%s)\n",tmp);
                //printf("filetype.(%s) json.%p type.%p tmp.%p [%s]\n",filetype,mimejson,typestr,tmp,content_type);
            }
        }
        if ( retstr != 0 )
        {
            char *response,hdrs[1024];
            if ( jsonflag != 0 || postflag != 0 )
            {
                response = malloc(strlen(retstr)+1024+1+1);
                sprintf(hdrs,"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Credentials: true\r\nAccess-Control-Allow-Methods: GET, POST\r\nCache-Control :  no-cache, no-store, must-revalidate\r\n%sContent-Length : %8d\r\n\r\n",content_type,(int32_t)strlen(retstr));
                response[0] = '\0';
                strcat(response,hdrs);
                strcat(response,retstr);
                strcat(response,"\n");
                if ( retstr != space )
                    free(retstr);
                retstr = response;
            }
            remains = (int32_t)strlen(retstr);
            i = 0;
            while ( remains > 0 )
            {
                if ( (numsent= (int32_t)send(sock,&retstr[i],remains,MSG_NOSIGNAL)) < 0 )
                {
                    if ( errno != EAGAIN && errno != EWOULDBLOCK )
                    {
                        //printf("%s: %s numsent.%d vs remains.%d len.%d errno.%d (%s) usock.%d\n",retstr,ipaddr,numsent,remains,recvlen,errno,strerror(errno),sock);
                        break;
                    }
                }
                else if ( remains > 0 )
                {
                    remains -= numsent;
                    i += numsent;
                    if ( remains > 0 )
                        printf("iguana sent.%d remains.%d of len.%d\n",numsent,remains,recvlen);
                }
            }
            if ( retstr != space)
                free(retstr);
        }
        closesocket(sock);
    }
}