<?php

#######################################
#                                     #
#     FCC Global functions            #
#                                     #
#    (C) 2019 Domero                  #
#                                     #
#######################################

namespace FCC;

class global {

    public static $COIN = "FCC";
    public static $FCCVERSION = "0101"; // ledger version
    public static $FCCBUILD = '2.3.2';   // software version
    public static $FCCTIME;
    public static $FCCMAGIC = 'FF2F89B12F9A29CAB2E2567A7E1B8A27C8FA9BF7A1ABE76FABA7919FC6B6FF0F';
    public static $FCCSERVERIP = '149.210.194.88'; // factorialcoin.nl
    public static $FCCSERVERHOST = 'factorialcoin.nl';
    public static $FCCSERVERPORT = 5151;
    public static $FCCSERVERKEY = "FCC55202FF7F3AAC9A85E22E6990C5ABA8EFBB73052F6EA1867AF7B96AE23FCC";
    public static $FCCEXT = '.fcc';
    public static $MINIMUMFEE = 50;
    public static $MINERPAYOUT = 1000000000;
    public static $MINEBONUS = 50000000;

    public static $HP = [
        0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
        'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 'E' => 14, 'F' => 15
    ];

    public static $TRANSTYPES = [
        'genesis' => '0',
        'in' => '1',
        'out' => '2',
        'coinbase' => '3',
        'fee' => '4'
    ];

    public static $RTRANSTYPES = [];

    public static function setcoin($coin) {
        self::$COIN = strtoupper($coin);
        if (self::$COIN === 'PTTP') {
            self::$FCCMAGIC = "8BF879BEC8FA9EC6CA3E7A96B26F7AA76F6AA4E78BADCFA1665A8A9CD67ADD0F";
            self::$FCCSERVERPORT = 9612;
            self::$FCCSERVERKEY = "1111145AFA4FBB1CF8D406A234C4CC361D797D9F8F561913D479DBC28C7A4F3E";
            self::$FCCEXT = '.pttp';
            self::$FCCBUILD = '1.4.2';
            self::$MINIMUMFEE = 110;
        } elseif (self::$COIN === 'FCC') {
            self::$FCCMAGIC = "FF2F89B12F9A29CAB2E2567A7E1B8A27C8FA9BF7A1ABE76FABA7919FC6B6FF0F";
            self::$FCCSERVERPORT = 5151;
            self::$FCCSERVERKEY = "FCC55202FF7F3AAC9A85E22E6990C5ABA8EFBB73052F6EA1867AF7B96AE23FCC";
            self::$FCCEXT = '.fcc';
            self::$MINIMUMFEE = 50;
        } elseif (self::$COIN !== 'FCC') {
            die("Unknown coin '$coin'");
        }
    }

    public static function tzoffset() {
        $t = time();
        $utc = gmmktime(gmdate('H', $t), gmdate('i', $t), gmdate('s', $t), gmdate('m', $t), gmdate('d', $t), gmdate('Y', $t));
        $local = mktime(date('H', $t), date('i', $t), date('s', $t), date('m', $t), date('d', $t), date('Y', $t));
        return ($utc - $local);
    }

    public static function fcctime($time = null) {
        if (!$time) {
            self::$FCCTIME = 0;
            return;
        }
        $t = time();
        $local = mktime(date('H', $t), date('i', $t), date('s', $t), date('m', $t), date('d', $t), date('Y', $t));
        self::$FCCTIME = $time - $local;
    }

    public static function ledgerversion() {
        $major = intval(substr(self::$FCCVERSION, 0, 2));
        $minor = intval(substr(self::$FCCVERSION, 2, 2));
        return $major . '.' . $minor;
    }

    public static function setfcctime($time) {
        self::$FCCTIME = $time;
    }

    public static function fcctimestring($time = null) {
        if (!$time) {
            $time = time() + self::$FCCTIME;
        }
        $t = localtime($time);
        $tm = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][$t['tm_wday']] . ', ';
        $yr = $t['tm_year'] + 1900;
        $mon = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][$t['tm_mon']];
        $tm .= $t['tm_mday'] . ' ' . $mon . ' ' . $yr . ' ';
        $tm .= sprintf('%02d', $t['tm_hour']) . ':' . sprintf('%02d', $t['tm_min']) . ':' . sprintf('%02d', $t['tm_sec']);
        $tm .= ' GMT';
        return $tm;
    }

    public static function securehash($code) {
        if (!$code) {
            die("FCC.Global.SecureHash: No Code given to hash!");
        }
        return strtoupper(hash('sha256', hash('sha512', $code)));
    }

    public static function octhex($key) {
        if ($key === null) {
            return "";
        }
        $hex = '';
        for ($i = 0; $i < strlen($key); $i++) {
            $c = ord(substr($key, $i, 1));
            $hex .= sprintf('%02X', $c);
        }
        return $hex;
    }

    public static function hexoct($hex) {
        if ($hex === null) {
            return "";
        }
        $key = '';
        for ($i = 0; $i < strlen($hex); $i += 2) {
            $h = substr($hex, $i, 2);
            $key .= chr(hexdec($h));
        }
        return $key;
    }

    public static function hexchar($num) {
        return [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'A', 'B', 'C', 'D', 'E', 'F'][$num];
    }

    public static function dechex($dec, $len) {
        if ($dec === null) {
            die("FCC::global::dechex: No decimal given");
        }
        if (!$len) {
            die("FCC::global::dechex - No length given");
        }
        $out = "";
        if ($len == 1) {
            return self::hexchar($dec & 15);
        }
        while ($len > 0) {
            $byte = $dec & 255;
            $hex = self::hexchar($byte >> 4);
            $hex .= self::hexchar($byte & 15);
            $out = $hex . $out;
            $dec >>= 8;
            $len -= 2;
        }
        return $out;
    }

    public static function hexdec($hex) {
        if (preg_match('/[^0-9A-F]/', $hex)) {
            die("FCC::global::hexdec - Illegal hex given '$hex'");
        }
        $dec = 0;
        for ($i = 0; $i < strlen($hex); $i++) {
            $dec <<= 4;
            $dec += hexdec(substr($hex, $i, 1));
        }
        return $dec;
    }

    public static function validh64($hex) {
        if (strlen($hex) != 64) {
            return 0;
        }
        if (preg_match('/[^0-9A-F]/', $hex)) {
            return 0;
        }
        return 1;
    }

    public static function extdec($dec) {
        $dec = $dec ?? 0;
        $d = intval($dec);
        $v = intval(($dec + 0.000000005 - $d) * 100000000);
        while (strlen($v) < 8) {
            $v = "0$v";
        }
        return "$d.$v";
    }

    public static function doggy($amount) {
        return intval(($amount + 0.000000005) * 100000000);
    }

    public static function feeint($fee) {
        return intval($fee * 100);
    }

    public static function calcfee($amount, $fee) {
        if (!$fee) {
            return 0;
        }
        $amount = self::extdec($amount);
        $feefloat = (self::feeint($fee) / 100);
        $cfee = self::extdec($amount * ($feefloat / 100));
        if ($cfee === '0.00000000') {
            $cfee = '0.00000001';
        }
        return $cfee;
    }

    public static function doggyfee($amount, $fee) {
        if (!$fee) {
            return 0;
        }
        $cfee = intval($amount * ($fee / 10000));
        if (!$cfee) {
            $cfee = 1;
        }
        return $cfee;
    }

    public static function fccstring($amount, $fee) {
        return self::extdec(self::extdec($amount) + self::calcfee($amount, $fee));
    }

    public static function fccencode($data, $password) {
        $h1 = self::securehash($password);
        $h2 = self::securehash(strrev($password));
        $pos = 0;
        $offset = 0;
        $todo = strlen($data);
        $dpos = 0;
        $coded = "";
        while ($dpos < $todo) {
            $get = self::$HP[substr($h2, $pos, 1)];
            $pos += $get;
            $pos %= 64;
            if ($pos == 63) {
                $pos = 0;
            }
            $code = (self::$HP[substr($h1, $pos, 1)] << 4) + self::$HP[substr($h1, $pos + 1, 1)];
            $tocode = ord(substr($data, $dpos, 1));
            $coded .= chr($code ^ $tocode);
            $dpos++;
        }
        return self::octhex($coded);
    }

    public static function encode_base64_char($code, $c62 = '+', $c63 = '/') {
        if (!$c62) {
            $c62 = '+';
        }
        if (!$c63) {
            $c63 = '/';
        }
        if ($code < 26) {
            return chr(ord('A') + $code);
        }
        if ($code < 52) {
            return chr(ord('a') + $code - 26);
        }
        if ($code < 62) {
            return chr(ord('0') + $code - 52);
        }
        if ($code == 62) {
            return $c62;
        }
        if ($code == 63) {
            return $c63;
        }
    }

    public static function encode_base64($data) {
        // RFC 3548
        $c62 = '+';
        $c63 = '/';
        $pad = "=";
        $len = strlen($data);
        $pos = 0;
        $val = 0;
        $br = 0;
        $out = "";
        $written = 0;
        while ($pos < $len) {
            $code = ord(substr($data, $pos, 1));
            $val <<= 8;
            $val += $code;
            $br += 8;
            while ($br >= 6) {
                $c = ($val >> ($br - 6));
                $br -= 6;
                $val &= ((1 << $br) - 1);
                $out .= self::encode_base64_char($c, $c62, $c63);
                $written++;
            }
            $pos++;
        }
        if ($br) {
            $val <<= (6 - $br);
            $out .= self::encode_base64_char($val, $c62, $c63);
            $written++;
        }
        // padding
        while ($written % 4 > 0) {
            $out .= $pad;
            $written++;
        }
        return $out;
    }

    public static function decode_base64($data) {
        // RFC 3548
        $c62 = '+';
        $c63 = '/';
        $pad = "=";
        $len = strlen($data);
        $pos = 0;
        $val = 0;
        $br = 0;
        $end = 0;
        $out = "";
        while ($pos < $len && !$end) {
            $enc = substr($data, $pos, 1);
            if (preg_match('/([A-Z])/', $enc)) {
                $val = ($val << 6) + ord($enc) - ord('A');
                $br += 6;
            } elseif (preg_match('/([a-z])/', $enc)) {
                $val = ($val << 6) + 26 + ord($enc) - ord('a');
                $br += 6;
            } elseif (preg_match('/([0-9])/', $enc)) {
                $val = ($val << 6) + 52 + ord($enc) - ord('0');
                $br += 6;
            } elseif ($enc === $c62) {
                $val = ($val << 6) + 62;
                $br += 6;
            } elseif ($enc === $c63) {
                $val = ($val << 6) + 63;
                $br += 6;
            } elseif ($enc === $pad) {
                $val = ($val << 6);
                $br += 6;
                $end++;
            }
            if (!$val && $end) {
                return $out;
            }
            while ($br >= 8) {
                $c = ($val >> ($br - 8));
                $out .= chr($c);
                $br -= 8;
                $val &= ((1 << $br) - 1);
            }
            $pos++;
        }
        if ($br) {
            $c = ($val >> (8 - $br));
            $out .= chr($c);
        }
        return $out;
    }

    public static function unzip($data) {
        return gzdecode($data);
    }

    public static function zip($data) {
        return gzencode($data, 9); // Maximum compression level
    }

    public static function zb64($data) {
        return self::encode_base64(self::zip($data));
    }

    public static function b64z($data) {
        return self::unzip(self::decode_base64($data));
    }

    public static function prtm() {
        $t = localtime(time() + self::$FCCTIME);
        $s = str_pad($t['tm_sec'], 2, '0', STR_PAD_LEFT);
        $m = str_pad($t['tm_min'], 2, '0', STR_PAD_LEFT);
        $h = str_pad($t['tm_hour'], 2, '0', STR_PAD_LEFT);
        echo "[$h:$m:$s] ";
        return "";
    }

    public static function rsp($str, $sp) {
        $x = ($sp - strlen($str));
        $out = str_repeat(' ', $x) . $str;
        return $out;
    }

    // EOF FCC::global (C) 2019 Domero
}
