
const Buffer = s2.Buffer.new;

scope {

  var b = Buffer();
  assert b inherits s2.Buffer;
  assert !b.capacity();
  assert !b.length();
  b.length(10);
  assert 10 === b.length();
  assert b.capacity() >= b.length();
}


scope {
    const sz = 20, b = Buffer(sz);
    assert !b.length();
    assert b.isEmpty();
    assert b.capacity() >= sz;
    b.append('a');
    assert !b.isEmpty();
    assert 1 === b.length();
    b.appendf('%1$d', 1);
    assert 2 === b.length();

    b.fill(42);
    assert '**' === b.toString() /* ensure that fill() does not change the length() */;

    b.reset();
    assert 0 === b.length();
    b.fill('!');
    assert undefined === b.byteAt(0) /* ensure that fill() does not change the length() */;

    b.length(sz).fill( '$' );
    assert 36 === b.byteAt( 0 );
    assert 36 === b.byteAt( sz-1 );
    assert undefined === b.byteAt( sz );

    b.fill( '!', 1, 18 );
    assert 36 === b.byteAt( 0 );
    assert 36 === b.byteAt( 19 );
    assert 33 === b.byteAt( 1 );
    assert 33 === b.byteAt( 18 );
    assert '$!' === b.toString( 0, 2 );
}

scope {
    const fmt = proc callee(fmt){
        return callee.buffer.reset( 0 ).appendf.apply( callee.buffer, argv ).toString();
    };
    fmt.buffer = s2.Buffer.new(100);
    const check = proc(cmp){
        argv.shift();
        const s = fmt.apply(fmt,argv);
        (s===cmp) || throw ("Mismatch: <<<"+cmp+">>> !== <<<" +s+">>>");
        assert 1 /* count the above comparison as an assertion.
                    We use throw only to get more info out of it
                    if it fails*/;
    };
    const av = [ 42, "string formatter", "abcde",
                 19.17, true, false, -13.19];

    assert 'CWAL_RC_RANGE' === catch{fmt("%8$+f", 0)/*not enough args*/}.codeString();

    check( '%', '%%' );
    check( '%x%y', '%%x%%y' );

    check( 'hi, world', "%1$s", 'hi, world' );
    check('hi, world (           hi, world)',
          "%1$s (%1$20s)", 'hi, world');
    check('integer 42 042 02A 002a',
          "%1$y %1$d %1$03d %1$03X %1$04x", av.0);
    check('(        42) (42        )',
          "(%1$10d) (%1$-10d)", av.0);
    check('(       +42) (+42       )',
          "(%1$+10d) (%1$+-10d)", av.0);

    check( '00042', "%1$05d", av.0 );
    check( '42.00000', "%1$0.5f", av.0 );
    check('string formatter', "%2$s", 0, av.1);
    check('string', "%2$.6s", 0, av.1);
    check('string formatter and string formatter',
          "%2$s and %2$s", 0, av.1);
    check( '(abcde     ) (     abcde)',
           "(%3$-10s) (%3$10s)", 0, 0, av.2 );
    check( '(     abcde) (abcde     )',
           "(%3$10s) (%3$-10s)", 0, 0, av.2 );
    check( '(       abc) (abc       )',
           "(%3$10.3s) (%3$-10.3s)", 0, 0, av.2 );
    check( '(abc       ) (       abc)',
           "(%3$-10.3s) (%3$10.3s)", 0, 0, av.2 );
    check( '(abcd) (abcd    )',
           "(%3$.4s) (%3$-8.4s)", 0, 0, av.2 );
    check( '19.17 19.2 19.17 19.170',
           "%4$f %4$.1f %4$.2f %4$.3f", 0, 0, 0, av.3 );
    check( '+19.17 (19.170    ) (    19.170) (+00019.170)',
           "%4$+0f (%4$-10.3f) (%4$10.3f) (%4$+010.3f)", 0, 0, 0, av.3 );
    check( '1 true false null undefined',
           "%5$d %5$b %6$b %5$N %5$U", 0, 0, 0, 0, av.4, av.5 );
    check( '0.0', "%6$+f", 0, 0, 0, 0, 0, av.5 );
    check( '-13.19', "%7$+f", 0, 0, 0, 0, 0, 0, av.6 );

    check( '68692C20776F726C64',
           "%1$B", "hi, world" );
    check( '68692C20',
           "%1$.4B", "hi, world" );
    check( '                     68692C20776F726C64',
           "%1$30B", "hi, world" );

    check( '-1.0', '%1$.1f', -1.02 );
    check( '+1.0', '%1$+.1f', 1.02 );

    check( '-1', '%1$d', (-1) );
    check( '-1', '%1$+d', (-1) );
    check( '+1', '%1$+d', 1 );

    check( 'h', '%1$c', 'hi' );
    check( '     hhhhh', '%1$10.5c', 'hi' );
    check( 'hhhhh     ', '%1$-10.5c', 'hi' );
    check( '  ©', '%1$3c', '©' );
    check( '©©©', '%1$0.3c', '©' );
    check( '©©©', '%1$.3c', '©' );
    check( '©©©  ', '%1$-5.3c', '©' );
    check( '  ©©©', '%1$5.3c', '©' );
    check( 'A', '%1$c', 65 );
    check( 'AAAA', '%1$.4c', 65 );
    check( '10', '%1$o', 8 );
    check( '0010', '%1$04o', 8 );
    check( '  10', '%1$4o', 8 );
    assert '1 2 3' === "%1$d %2$d %3$d".applyFormat( 1,2,3 );
    check( '(NULL)', '%1$q', null );
    check( "h''i", '%1$q', "h'i" );
    check( "'h''i'", '%1$Q', "h'i" );
    check( 'NULL', '%1$Q', null );

    // check 0-precision characters...
    check( '', '%1$.0c', '*' );
    check( '', '%1$0.0c', '*' );
    check( '  ', '%1$2.0c', '*' ) /* arguable, but currently true */;

    // check urlencoding/decoding
    check( 'a%20b%26c', '%1$r', 'a b&c' );
    check( 'a b&c', '%1$R', 'a%20b%26c' );
    check( 'a%2zb', '%1$R', 'a%2zb' );

    assert catch {'%1$.1r'.applyFormat()}.message.indexOf('precision')>0;
    assert catch {'%1$1r'.applyFormat()}.message.indexOf('width')>0;
    assert catch {'%1$.1R'.applyFormat()}.message.indexOf('precision')>0;
    assert catch {'%1$1R'.applyFormat()}.message.indexOf('width')>0;
}

scope{
    var b = Buffer(100);
    var x = 0, y = 0;
    b << <<<EOF
    for(var i = 0; i < 5; ++i) ++x, --y; 
    1
    EOF;
    var rc = b.evalContents("foo");
    assert 1 === rc;
    assert 5 === x;
    assert -x === y;

    b.reset() << <<<EOF
    0;
    1;
    return {a:'a',b:'bbbb'};
    EOF;
  
    var name = 'bar';
    rc = b.evalContents(name);
    assert rc.a === 'a';
    assert rc.b === 'bbbb';

    b.reset() << <<<EOF
    0;
    1;
    throw 3;
    return {a:'a',b:'bbbb'};
    EOF;
  
    var ex = catch{
        rc = 1
        ? b.evalContents(name)
            : eval ->b;
        throw "NOT REACHED";
    };
    assert ex;
    //print(__FLC,'ex =',ex) /* a cycle (the exception obj!) in the stack trace!?!?!? Corruption??? */;
    // something here is triggering an assertion in cleanup!
    // Worked around: comments are in evalContents() impl for further consideration later.
    assert name === ex.script;
    assert 3 === ex.message;
    assert 3 === ex.line;
    assert 4 === ex.column;
}

scope {
    var b = s2.Buffer.new() << "h©, ©orld";
    assert '©, ©' === b.substr(1,4);
    assert 'h©, ©orld' === b.substr(0,0);
    assert '©orld' === b.substr(4);
    assert ', ©o' === b.substr(2,4);

    b.reset();
    assert '' === b.substr(1);
}

scope {
    var b = s2.Buffer.new() << 1.00;
    assert "1.0" === b.toString() /* bugfix check: leave final trailing 0 after the dot. */;
}
