import 'dart:convert';
import 'dart:typed_data';

import 'package:pointycastle/asn1.dart';
import 'package:test/test.dart';

void main() {
  ///
  /// Test simple structur
  ///
  /// ```
  ///   SEQUENCE (2 elem)
  ///     OBJECT IDENTIFIER 1.2.840.113549.1.1.11 sha256WithRSAEncryption (PKCS #1)
  ///     NULL
  /// ```
  ///
  test('Test nextObject 1', () {
    var bytes = Uint8List.fromList([
      0x30,
      0x0D,
      0x06,
      0x09,
      0x2A,
      0x86,
      0x48,
      0x86,
      0xF7,
      0x0D,
      0x01,
      0x01,
      0x0B,
      0x05,
      0x00
    ]);
    var parser = ASN1Parser(bytes);
    var sequence = parser.nextObject() as ASN1Sequence;
    expect(sequence.encodedBytes!.length, sequence.totalEncodedByteLength);
    expect(sequence.elements!.length, 2);
    expect(sequence.elements!.elementAt(0) is ASN1ObjectIdentifier, true);
    expect(sequence.elements!.elementAt(1) is ASN1Null, true);
  });

  ///
  /// Test simple structur
  ///
  /// ```
  /// SEQUENCE (4 elem)
  ///    SET (1 elem)
  ///      SEQUENCE (2 elem)
  ///        OBJECT IDENTIFIER 2.5.4.6 countryName (X.520 DN component)
  ///        PrintableString US
  ///    SET (1 elem)
  ///      SEQUENCE (2 elem)
  ///        OBJECT IDENTIFIER 2.5.4.10 organizationName (X.520 DN component)
  ///        PrintableString DigiCert Inc
  ///    SET (1 elem)
  ///      SEQUENCE (2 elem)
  ///        OBJECT IDENTIFIER 2.5.4.11 organizationalUnitName (X.520 DN component)
  ///        PrintableString www.digicert.com
  ///    SET (1 elem)
  ///      SEQUENCE (2 elem)
  ///        OBJECT IDENTIFIER 2.5.4.3 commonName (X.520 DN component)
  ///        PrintableString Thawte RSA CA 2018
  /// ```
  ///
  test('Test nextObject 2', () {
    var bytes = Uint8List.fromList([
      0x30,
      0x5C,
      0x31,
      0x0B,
      0x30,
      0x09,
      0x06,
      0x03,
      0x55,
      0x04,
      0x06,
      0x13,
      0x02,
      0x55,
      0x53,
      0x31,
      0x15,
      0x30,
      0x13,
      0x06,
      0x03,
      0x55,
      0x04,
      0x0A,
      0x13,
      0x0C,
      0x44,
      0x69,
      0x67,
      0x69,
      0x43,
      0x65,
      0x72,
      0x74,
      0x20,
      0x49,
      0x6E,
      0x63,
      0x31,
      0x19,
      0x30,
      0x17,
      0x06,
      0x03,
      0x55,
      0x04,
      0x0B,
      0x13,
      0x10,
      0x77,
      0x77,
      0x77,
      0x2E,
      0x64,
      0x69,
      0x67,
      0x69,
      0x63,
      0x65,
      0x72,
      0x74,
      0x2E,
      0x63,
      0x6F,
      0x6D,
      0x31,
      0x1B,
      0x30,
      0x19,
      0x06,
      0x03,
      0x55,
      0x04,
      0x03,
      0x13,
      0x12,
      0x54,
      0x68,
      0x61,
      0x77,
      0x74,
      0x65,
      0x20,
      0x52,
      0x53,
      0x41,
      0x20,
      0x43,
      0x41,
      0x20,
      0x32,
      0x30,
      0x31,
      0x38
    ]);

    var parser = ASN1Parser(bytes);
    var sequence = parser.nextObject() as ASN1Sequence;

    expect(sequence.encodedBytes!.length, sequence.totalEncodedByteLength);
    expect(sequence.elements!.length, 4);
    expect(sequence.elements!.elementAt(0) is ASN1Set, true);
    expect(sequence.elements!.elementAt(1) is ASN1Set, true);
    expect(sequence.elements!.elementAt(2) is ASN1Set, true);
    expect(sequence.elements!.elementAt(3) is ASN1Set, true);

    var set1 = sequence.elements!.elementAt(0) as ASN1Set;
    expect(set1.elements!.length, 1);
    expect(set1.elements!.elementAt(0) is ASN1Sequence, true);

    var seq1 = set1.elements!.elementAt(0) as ASN1Sequence;
    expect(seq1.encodedBytes!.length, seq1.totalEncodedByteLength);
    expect(seq1.elements!.length, 2);
    expect(seq1.elements!.elementAt(0) is ASN1ObjectIdentifier, true);
    expect(seq1.elements!.elementAt(1) is ASN1PrintableString, true);
    var oi1 = seq1.elements!.elementAt(0) as ASN1ObjectIdentifier;
    var string1 = seq1.elements!.elementAt(1) as ASN1PrintableString;
    expect(oi1.objectIdentifierAsString, '2.5.4.6');
    expect(string1.stringValue, 'US');

    var set2 = sequence.elements!.elementAt(1) as ASN1Set;
    expect(set2.elements!.length, 1);
    expect(set2.elements!.elementAt(0) is ASN1Sequence, true);

    var seq2 = set2.elements!.elementAt(0) as ASN1Sequence;
    expect(seq2.encodedBytes!.length, seq2.totalEncodedByteLength);
    expect(seq2.elements!.length, 2);
    expect(seq2.elements!.elementAt(0) is ASN1ObjectIdentifier, true);
    expect(seq2.elements!.elementAt(1) is ASN1PrintableString, true);
    var oi2 = seq2.elements!.elementAt(0) as ASN1ObjectIdentifier;
    var string2 = seq2.elements!.elementAt(1) as ASN1PrintableString;
    expect(oi2.objectIdentifierAsString, '2.5.4.10');
    expect(string2.stringValue, 'DigiCert Inc');

    var set3 = sequence.elements!.elementAt(2) as ASN1Set;
    expect(set3.elements!.length, 1);
    expect(set3.elements!.elementAt(0) is ASN1Sequence, true);

    var seq3 = set3.elements!.elementAt(0) as ASN1Sequence;
    expect(seq3.encodedBytes!.length, seq3.totalEncodedByteLength);
    expect(seq3.elements!.length, 2);
    expect(seq3.elements!.elementAt(0) is ASN1ObjectIdentifier, true);
    expect(seq3.elements!.elementAt(1) is ASN1PrintableString, true);
    var oi3 = seq3.elements!.elementAt(0) as ASN1ObjectIdentifier;
    var string3 = seq3.elements!.elementAt(1) as ASN1PrintableString;
    expect(oi3.objectIdentifierAsString, '2.5.4.11');
    expect(string3.stringValue, 'www.digicert.com');

    var set4 = sequence.elements!.elementAt(3) as ASN1Set;
    expect(set4.elements!.length, 1);
    expect(set4.elements!.elementAt(0) is ASN1Sequence, true);

    var seq4 = set4.elements!.elementAt(0) as ASN1Sequence;
    expect(seq4.encodedBytes!.length, seq4.totalEncodedByteLength);
    expect(seq4.elements!.length, 2);
    expect(seq4.elements!.elementAt(0) is ASN1ObjectIdentifier, true);
    expect(seq4.elements!.elementAt(1) is ASN1PrintableString, true);
    var oi4 = seq4.elements!.elementAt(0) as ASN1ObjectIdentifier;
    var string4 = seq4.elements!.elementAt(1) as ASN1PrintableString;
    expect(oi4.objectIdentifierAsString, '2.5.4.3');
    expect(string4.stringValue, 'Thawte RSA CA 2018');
  });

  test('Test parse X509', () {
    var pem = '''-----BEGIN CERTIFICATE-----
MIIGuDCCBaCgAwIBAgIQJWoxbMUUPa7apOL527blFzANBgkqhkiG9w0BAQsFADCB
sDELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8w
HQYDVQQLExZGT1IgVEVTVCBQVVJQT1NFUyBPTkxZMR8wHQYDVQQLExZTeW1hbnRl
YyBUcnVzdCBOZXR3b3JrMUAwPgYDVQQDEzdTeW1hbnRlYyBDbGFzcyAzIEV4dGVu
ZGVkIFZhbGlkYXRpb24gU0hBMjU2IFNTTCBURVNUIENBMB4XDTE2MTExODAwMDAw
MFoXDTE2MTEyNTIzNTk1OVowge8xEzARBgsrBgEEAYI3PAIBAxMCREUxFzAVBgsr
BgEEAYI3PAIBAhMGQmF5ZXJuMRswGQYLKwYBBAGCNzwCAQETClJlZ2Vuc2J1cmcx
HTAbBgNVBA8TFFByaXZhdGUgT3JnYW5pemF0aW9uMRAwDgYDVQQFEwczNDY2NTY1
MQswCQYDVQQGEwJERTENMAsGA1UECAwESGllcjETMBEGA1UEBwwKVGVzdGhhdXNl
bjESMBAGA1UECgwJVGVzdGZpcm1hMRQwEgYDVQQLDAtFbnR3aWNrbHVuZzEWMBQG
A1UEAwwNZXBoZW5vZHJvbS5kZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBALHYKBMxalQNBwqQVS/NE/Y5eTBORjxqY7jC/vZ881G4uMM+AFvt5iFKabjN
wLT9itYFuJ/k5o7kVn93f0eirxrP0e3wPkzwELJIR6AdZo1EsTzGPW0p41PGOjGv
/GPZwFWEfb7C0Wz4v3b3LtBrWYNJ12RswQAtKdNey/1nxZT2UwGdwESBK0Rvh3qt
33Wqkf1t8X627XTD91dkB/brq6oO3m2ZDyhbqDfMiWqUWl2CVnO0A+eFZ5uFFVdm
NPFuhNqgCmZ6TfFkQJQEKuBadHDTj47qjU27lTJjksCHVDs9PzJzkH49zStjk9D4
UkPw7y37xJ/gUK27VhkIC6QBTuECAwEAAaOCAoswggKHMCsGA1UdEQQkMCKCDWVw
aGVub2Ryb20uZGWCEXd3dy5lcGhlbm9kcm9tLmRlMAkGA1UdEwQCMAAwDgYDVR0P
AQH/BAQDAgWgMG8GA1UdIARoMGYwWwYLYIZIAYb4RQEHFwYwTDAjBggrBgEFBQcC
ARYXaHR0cHM6Ly9kLnN5bWNiLmNvbS9jcHMwJQYIKwYBBQUHAgIwGQwXaHR0cHM6
Ly9kLnN5bWNiLmNvbS9ycGEwBwYFZ4EMAQEwKwYDVR0fBCQwIjAgoB6gHIYaaHR0
cDovL3NoLnN5bWNiLmNvbS9zaC5jcmwwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsG
AQUFBwMCMB8GA1UdIwQYMBaAFERiHdf3UpjAQwNcU+bmv+ZDJ+tjMFcGCCsGAQUF
BwEBBEswSTAfBggrBgEFBQcwAYYTaHR0cDovL3NoLnN5bWNkLmNvbTAmBggrBgEF
BQcwAoYaaHR0cDovL3NoLnN5bWNiLmNvbS9zaC5jcnQwggEEBgorBgEEAdZ5AgQC
BIH1BIHyAPAAdgAR0wud4RKWE7VpXG+auxQlNw9ew3QWYeKO2GKv4jEwuQAAAVh3
IQXYAAAEAwBHMEUCIAWDOM8fYWHm95CEg8eDf85KvCVzPbTKm2ZcS/7Dx5w7AiEA
k/4u83CLUFizih+iJevMVXPHI99yeb6NT0lQnIZ8MFMAdgCRLn+OXTXy73s/VluY
uYC5VyaVUxTiFkYL1+xTql7swQAAAVh3IQXYAAAEAwBHMEUCIHQR9Geg7t98Gk2t
BSPlHIHDFG+XP5UUKa2eUahE973oAiEA6i8/T8IlppNUXcrMbcjjLLPsaSqd158U
BC44Pr90Nw4wDQYJKoZIhvcNAQELBQADggEBACJqJMVEKtBfmB0mvhFtd5GV7818
G/THYsf1AtMuCt80Rtzm+EXAyBupIhRERa+/J40pAE/ZyhmS5lSOjTzViR1RsIUg
bo190CLpEvKHA7ckBssJRySMFQ6RSPavsOkpHvduCqVS2PSb9W1APFzDJPeC9gaM
/EKIy7iLp2fiBZrYkTCaxYEy0SZGaFkXeHC20/d5PXhnylEXEIPR2aogIlbgzaNg
RVTxIJddHhpHfW5c2lX+ERf3Ni0fcJqcCZBPyGHUDSYqNrDwRLQ6dyVxz1Jl0oAc
+IHuDDDKJ0ewjoXgV+KRXqa7URuonqA5stUl/susZzd4BlT2k/XnuS8+iQc=
-----END CERTIFICATE-----''';

    var lines = LineSplitter.split(pem)
        .map((line) => line.trim())
        .where((line) => line.isNotEmpty)
        .toList();
    var base64 = lines.sublist(1, lines.length - 1).join('');
    var bytes = Uint8List.fromList(base64Decode(base64));
    var asn1Parser = ASN1Parser(bytes);
    var topLevelSeq = asn1Parser.nextObject() as ASN1Sequence;

    expect(topLevelSeq.elements!.length, 3);
    expect(topLevelSeq.totalEncodedByteLength, 1724);
    expect(topLevelSeq.valueByteLength, 1720);
    expect(topLevelSeq.elements!.elementAt(0) is ASN1Sequence, true);
    expect(topLevelSeq.elements!.elementAt(1) is ASN1Sequence, true);
    expect(topLevelSeq.elements!.elementAt(2) is ASN1BitString, true);

    var e1 = topLevelSeq.elements!.elementAt(0) as ASN1Sequence;
    var e2 = topLevelSeq.elements!.elementAt(1) as ASN1Sequence;
    var e3 = topLevelSeq.elements!.elementAt(2) as ASN1BitString;

    expect(e1.elements!.length, 8);
    expect(e1.totalEncodedByteLength, 1444);
    expect(e1.encodedBytes!.length, 1444);
    expect(e1.valueByteLength, 1440);
    expect(e1.elements!.elementAt(1) is ASN1Integer, true);
    expect(e1.elements!.elementAt(2) is ASN1Sequence, true);
    expect(e1.elements!.elementAt(3) is ASN1Sequence, true);
    expect(e1.elements!.elementAt(4) is ASN1Sequence, true);
    expect(e1.elements!.elementAt(5) is ASN1Sequence, true);
    expect(e1.elements!.elementAt(6) is ASN1Sequence, true);

    var asn1Object = e1.elements!.elementAt(0);

    var integer1 =
        ASN1Parser(asn1Object.valueBytes).nextObject() as ASN1Integer;

    expect(integer1.totalEncodedByteLength, 3);
    expect(integer1.valueByteLength, 1);
    expect(integer1.integer.toString(), '2');

    var integer2 = e1.elements!.elementAt(1) as ASN1Integer;
    expect(integer2.totalEncodedByteLength, 18);
    expect(integer2.valueByteLength, 16);
    expect(
        integer2.integer.toString(), '49732821766751726239505489314635506967');

    var seq1 = e1.elements!.elementAt(2) as ASN1Sequence;
    expect(seq1.encodedBytes!.length, seq1.totalEncodedByteLength);

    var seq2 = e1.elements!.elementAt(3) as ASN1Sequence;
    expect(seq2.encodedBytes!.length, seq2.totalEncodedByteLength);

    expect(e2.elements!.length, 2);
    expect(e2.totalEncodedByteLength, 15);
    expect(e2.encodedBytes!.length, 15);
    expect(e2.valueByteLength, 13);
    expect(e2.elements!.elementAt(0) is ASN1ObjectIdentifier, true);
    expect(e2.elements!.elementAt(1) is ASN1Null, true);

    expect(e3.elements, null);
    expect(e3.isConstructed, false);
    expect(e3.encodedBytes!.length, 261);
    expect(e3.valueByteLength, 257);
    expect(e3.stringValues!.length, 256);
  });
}
