<?php

namespace FCC;

require_once('FCC/global.php'); // Include FCC/global.php

class leaf {

    public static $LEAVES = [];
    public static $LEAFID = 0;
    public static $TRANSID = 0;
    public static $DEBUG = false;
    public static $LOOPWAIT = 1000;
    public static $FCCFUNCTION = 'leaf';
    public static $CALLER;
    public static $VERS;
    public static $HOST = '127.0.0.1';
    public static $PORT = 7050;
    public static $fccfunction = 'leaf';
    public static $outbuffer = [];

    public static function startleaf($host = null, $port = null, $caller, $active = null, $miner = null) {
        if (!isset($caller) || !is_callable($caller)) {
            echo "Caller-function missing in FCC::leaf::start\n";
            return;
        }
        self::$CALLER = $caller;
        if (!$host) {
            $host = self::$HOST;
        }
        if (!$port) {
            $port = self::$PORT;
        }
        self::$FCCFUNCTION = 'leaf';
        if ($miner) {
            self::$FCCFUNCTION = 'miner';
        }
        self::$LEAFID++;
        $leaf = gclient::websocket($host, $port, $active, 'handle_leaf');
        if (isset($leaf['error'])) {
            echo "Error connecting " . self::$FCCFUNCTION . ": " . $leaf['error'] . "\n\n";
            return $leaf;
        }
        $leaf['connected'] = 0;
        $leaf['leafcaller'] = $caller;
        $leaf['passive'] = 1;
        $leaf['leafid'] = self::$LEAFID;
        $leaf['outbuffer'] = [];
        self::$LEAVES[] = $leaf;
        return $leaf;
    }

    public static function handle_leaf($leaf, $command, $data) {
        if (!$data) {
            $data = "";
        }
        if ($command == 'init') {
            if (!$leaf['passive']) {
                $leaf['connected'] = 0;
                $leaf['leafcaller'] = self::$CALLER;
                $leaf['fccfunction'] = self::$FCCFUNCTION;
                $leaf['leafid'] = self::$LEAFID;
                $leaf['outbuffer'] = [];
            } else {
                return;
            }
        }
        $func = $leaf['leafcaller'];
        if (!is_callable($func)) {
            $func = self::$CALLER;
        }
        if (self::$DEBUG && $command != 'loop') {
            echo " < [LEAF]: $command - $data\n";
        }
        if ($command == 'loop') {
        } elseif ($command == 'input') {
            self::handleinput($leaf, $data);
        } elseif ($command == 'error') {
            gclient::wsquit($leaf);
            echo "Leaf exited with error: $data\n\n";
            $func($leaf, 'disconnect', ['error' => $data]);
        } elseif ($command == 'quit') {
            echo "Lost connection to node: $data\n\n";
            $func($leaf, 'disconnect', ['error' => $data]);
        } elseif ($command == 'close') {
            echo "Lost connection to node: $data\n\n";
            $func($leaf, 'disconnect', ['error' => $data]);
        } elseif ($command == 'connect') {
            list($tm, $ip) = explode(" ", $data);
            $leaf['connected'] = 1;
            if (self::$DEBUG) {
                echo self::prtm() . "Connected as " . $leaf['fccfunction'] . " v" . self::$VERS . " at " . $leaf['localip'] . " to $ip\n";
            }
        }
    }

    public static function outnode($leaf, $k) {
        if (!is_array($k)) {
            echo "Not a hash-reference given in FCC::leaf::outnode\n";
            return;
        }
        $leaf['outbuffer'][] = $k;
    }

    public static function leafloop() {
        foreach (self::$LEAVES as $leaf) {
            if ($leaf['connected']) {
                if (count($leaf['outbuffer']) > 0) {
                    $json = new JSON();
                    $data = array_shift($leaf['outbuffer']);
                    gclient::wsout($leaf, $json->encode($data));
                }
            }
            $leaf->takeloop();
        }
    }

    public static function closeleaf($leaf, $msg = null) {
        echo " !! Closing leaf $leaf['host']:$leaf['port']\n";
        if (!$msg) {
            $msg = 'Closed';
        }
        $func = $leaf['leafcaller'];
        $func($leaf, 'terminated', ['message' => $msg]);
        if ($leaf['connected']) {
            $leaf->wsquit($msg);
        } else {
            $leaf->quit($msg);
        }
    }

    public static function balance($leaf, $wallet) {
        if (is_array($wallet)) {
            $wallet = $wallet['wallet'];
        }
        self::outnode($leaf, ['command' => 'balance', 'wallet' => $wallet]);
    }

    public static function transfer($leaf, $pubkey, $changewallet, $tolist) {
        // $tolist = ['wallet', 'amount(doggy)', 'fee(doggyfee)']
        self::$TRANSID++;
        self::outnode($leaf, [
            'command' => 'newtransaction',
            'transid' => self::$TRANSID,
            'pubkey' => $pubkey,
            'to' => $tolist
        ]);
    }

    public static function sign($leaf, $transid, $signature) {
        self::outnode($leaf, ['command' => 'signtransaction', 'transid' => $transid, 'signature' => $signature]);
    }

    public static function history($leaf, $wallet) {
        // Implement the history function
    }

    public static function solution($leaf, $wallet, $solhash) {
        self::outnode($leaf, ['command' => 'solution', 'wallet' => $wallet, 'solhash' => $solhash]);
    }

    public static function ledgerinfo($leaf) {
        self::outnode($leaf, ['command' => 'ledgerinfo']);
    }

    public static function getledgerdata($leaf, $pos, $length, $final = 0) {
        if (!$final) {
            $final = 0;
        }
        self::outnode($leaf, ['command' => 'reqledger', 'pos' => $pos, 'length' => $length, 'final' => $final]);
    }

    public static function handleinput($leaf, $data) {
        $json = new JSON();
        $k = $json->decode($data);
        $cmd = $k['command'];
        $func = $leaf['leafcaller'];
        if (isset($k['error'])) {
            $func($leaf, 'error', [
                'command' => 'error',
                'message' => $cmd,
                'error' => $k['error'],
                'available' => $k['available'],
                'spendable' => $k['spendable']
            ]);
            return;
        }
        $proc = "c_$cmd";
        if (function_exists($proc)) {
            $proc($leaf, $k);
        } else {
            if (defined($GLOBALS['ILLEGAL_CALLBACK']) && is_callable($GLOBALS['ILLEGAL_CALLBACK'])) {
                $GLOBALS['ILLEGAL_CALLBACK']($data);
            } else {
                echo "Illegal command sent to leaf: [$cmd]\n";
            }
        }
    }

    public static function c_wrong($leaf, $data) {
        if (isset($GLOBALS['WRONG_CALLBACK']) && is_callable($GLOBALS['WRONG_CALLBACK'])) {
            $GLOBALS['WRONG_CALLBACK']($data);
        } else {
            echo "Wrong Solution Send: $data\n";
        }
    }

    public static function c_error($leaf, $k) {
        echo "Error: $k['message']\n";
        self::outnode($leaf, ['command' => 'quit']);
        gclient::quit($leaf);
        exit;
    }

    public static function c_hello($leaf, $k) {
        self::outnode($leaf, ['command' => 'identify', 'type' => $leaf['fccfunction'], 'version' => self::$VERS]);
        $func = $leaf['leafcaller'];
        $func($leaf, 'response', ['node' => "$k['host']:$k['port']", 'version' => $k['version']]);
    }

    public static function c_quit($leaf, $k) {
        $func = $leaf['leafcaller'];
        $func($leaf, 'disconnect', ['message' => $k['message']]);
        $leaf->quit();
    }

    public static function c_balance($leaf, $k) {
        $func = $leaf['leafcaller'];
        $func($leaf, 'balance', ['balance' => $k['balance'], 'wallet' => $k['wallet']]);
    }

    public static function c_newtransaction($leaf, $k) {
        $func = $leaf['leafcaller'];
        if (isset($k['error'])) {
            $func($leaf, 'transstatus', ['error' => $k['error'], 'transid' => $k['transid'], 'transhash' => $k['transhash']]);
        } else {
            $func($leaf, 'sign', ['data' => $k['sign'], 'transid' => $k['transid']]);
        }
    }

    public static function c_signtransaction($leaf, $k) {
        $func = $leaf['leafcaller'];
        $func($leaf, 'transstatus', ['error' => $k['error'], 'transid' => $k['transid'], 'transhash' => $k['transhash']]);
    }

    public static function c_processed($leaf, $k) {
        $func = $leaf['leafcaller'];
        if (isset($k['error'])) {
            $func($leaf, 'transstatus', ['error' => $k['error'], 'transhash' => $k['transhash']]);
        } else {
            $func($leaf, 'transstatus', ['status' => 'success', 'transhash' => $k['transhash'], 'wallet' => $k['wallet'], 'amount' => $k['amount'], 'fee' => $k['fee']]);
        }
    }

    public static function c_history($leaf, $k) {
        // Implement the history function
    }

    public static function c_mine($leaf, $k) {
        $func = $leaf['leafcaller'];
        $func($leaf, 'mine', $k);
    }

    public static function c_solution($leaf, $k) {
        $func = $leaf['leafcaller'];
        $func($leaf, 'solution', $k);
    }

    public static function c_ledgerresponse($leaf, $k) {
        $func = $leaf['leafcaller'];
        $func($leaf, 'ledgerinfo', $k);
    }

    public static function c_ledgerdata($leaf, $k) {
        $func = $leaf['leafcaller'];
        $func($leaf, 'ledgerdata', $k);
    }

    public static function prtm() {
        // Implement prtm() function
    }
}

?>
