using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Security;

namespace Admin.NET.Core;

public class SM2
{
    public static SM2 Instance
    {
        get
        {
            return new SM2();
        }
    }

    public static SM2 InstanceTest
    {
        get
        {
            return new SM2();
        }
    }

    public static readonly string[] sm2_param = {
        "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF",// p,0
        "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC",// a,1
        "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93",// b,2
        "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123",// n,3
        "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7",// gx,4
        "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0" // gy,5
    };

    public string[] ecc_param = sm2_param;

    public readonly BigInteger ecc_p;
    public readonly BigInteger ecc_a;
    public readonly BigInteger ecc_b;
    public readonly BigInteger ecc_n;
    public readonly BigInteger ecc_gx;
    public readonly BigInteger ecc_gy;

    public readonly ECCurve ecc_curve;
    public readonly ECPoint ecc_point_g;

    public readonly ECDomainParameters ecc_bc_spec;

    public readonly ECKeyPairGenerator ecc_key_pair_generator;

    private SM2()
    {
        ecc_param = sm2_param;

        ecc_p = new BigInteger(ecc_param[0], 16);
        ecc_a = new BigInteger(ecc_param[1], 16);
        ecc_b = new BigInteger(ecc_param[2], 16);
        ecc_n = new BigInteger(ecc_param[3], 16);
        ecc_gx = new BigInteger(ecc_param[4], 16);
        ecc_gy = new BigInteger(ecc_param[5], 16);

        ecc_curve = new FpCurve(ecc_p, ecc_a, ecc_b, null, null);
        ecc_point_g = ecc_curve.CreatePoint(ecc_gx, ecc_gy);

        ecc_bc_spec = new ECDomainParameters(ecc_curve, ecc_point_g, ecc_n);

        ECKeyGenerationParameters ecc_ecgenparam;
        ecc_ecgenparam = new ECKeyGenerationParameters(ecc_bc_spec, new SecureRandom());

        ecc_key_pair_generator = new ECKeyPairGenerator();
        ecc_key_pair_generator.Init(ecc_ecgenparam);
    }

    public virtual byte[] Sm2GetZ(byte[] userId, ECPoint userKey)
    {
        var sm3 = new SM3Digest();
        byte[] p;
        // userId length
        int len = userId.Length * 8;
        sm3.Update((byte)(len >> 8 & 0x00ff));
        sm3.Update((byte)(len & 0x00ff));

        // userId
        sm3.BlockUpdate(userId, 0, userId.Length);

        // a,b
        p = ecc_a.ToByteArray();
        sm3.BlockUpdate(p, 0, p.Length);
        p = ecc_b.ToByteArray();
        sm3.BlockUpdate(p, 0, p.Length);
        // gx,gy
        p = ecc_gx.ToByteArray();
        sm3.BlockUpdate(p, 0, p.Length);
        p = ecc_gy.ToByteArray();
        sm3.BlockUpdate(p, 0, p.Length);

        // x,y
        p = userKey.AffineXCoord.ToBigInteger().ToByteArray();
        sm3.BlockUpdate(p, 0, p.Length);
        p = userKey.AffineYCoord.ToBigInteger().ToByteArray();
        sm3.BlockUpdate(p, 0, p.Length);

        // Z
        byte[] md = new byte[sm3.GetDigestSize()];
        sm3.DoFinal(md, 0);

        return md;
    }
}