/*
 This file is part of GNUnet.
 (C) 2011, 2012 Christian Grothoff (and other contributing authors)

 GNUnet is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published
 by the Free Software Foundation; either version 3, or (at your
 option) any later version.

 GNUnet is distributed in the hope that it will be useful, but
 WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with GNUnet; see the file COPYING.  If not, write to the
 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 Boston, MA 02111-1307, USA.
 */

package org.gnunet.util.getopt;


import org.junit.Assert;
import org.junit.Test;

class Target {
    @Argument(
            action = ArgumentAction.STORE_STRING,
            shortname = "s",
            longname = "string",
            argumentName = "SOME_STRING",
            description = "just some string"
    )
    String someString;

    @Argument(
            action = ArgumentAction.SET,
            shortname = "y",
            longname = "set",
            description = "enable, default disabled"
    )
    boolean set = false;

    @Argument(
            action = ArgumentAction.RESET,
            shortname = "n",
            longname = "reset",
            description = "disable, default enabled"
    )
    boolean reset = true;


    @Argument(
            action = ArgumentAction.STORE_NUMBER,
            shortname = "w",
            longname = "value",
            description = "some value"
    )
    int intVal = 0;

    static int someConstant = 42;
}

class InvalidTarget {
    @Argument(action = ArgumentAction.SET, shortname = "foo", longname = "bar", description = "bla bla")
    boolean foo;
}

public class GetoptTest {
    @Test
    public void test_str() {
        Target t = new Target();
        Parser p = new Parser(t);


        t.someString = null;
        
        // argument directly with shortopt
        p.parse(new String[]{"-sfoo"});
        Assert.assertEquals("foo", t.someString);

        t.someString = null;

        // argument after shortopt
        p.parse(new String[]{"-s", "foo"});
        Assert.assertEquals("foo", t.someString);

        t.someString = null;

        p.parse(new String[]{"--string=foo"});
        Assert.assertEquals("foo", t.someString);

        t.someString = null;

        p.parse(new String[]{"--string", "foo"});
        Assert.assertEquals("foo", t.someString);


        t.someString = null;

        // last argument counts
        p.parse(new String[]{"--string", "bar", "--string", "foo"});
        Assert.assertEquals("foo", t.someString);
        
        t.someString = null;
        
        boolean thrown;
                
        thrown = false;
        
        // absence of argument throws ArgumentError (longopt)
        try {
            p.parse(new String[]{"--string"});
        } catch (Parser.ArgumentError e) {
            thrown = true;
        }
        
        Assert.assertTrue(thrown);
        
        thrown = false;
        // absence of argument throws ArgumentError (shortopt)
        try {
            p.parse(new String[]{"-s"});
        } catch (Parser.ArgumentError e) {
            thrown = true;
        }

        Assert.assertTrue(thrown);
        
        t.someString = null;
        
        // a way to specify an empty string
        p.parse(new String[]{"--string="});
        Assert.assertEquals("", t.someString);
    }

    @Test
    public void test_help() {
        Target t = new Target();
        Parser p = new Parser(t);
        
        String help = p.getHelp();
        
        Assert.assertTrue(help.contains("--string"));
        Assert.assertTrue(help.contains("-s"));

        Assert.assertTrue(help.contains("-y"));
        Assert.assertTrue(help.contains("--set"));
    }


    @Test
    public void test_bool() {
        Target t = new Target();
        Parser p = new Parser(t);
        
        p.parse(new String[]{"--set", "--reset"});
        Assert.assertTrue(t.set);
        Assert.assertTrue(!t.reset);
        
        t.set = false;
        t.reset = true;

        p.parse(new String[]{"-y", "-n"});

        Assert.assertTrue(t.set);
        Assert.assertTrue(!t.reset);

        t.set = false;
        t.reset = true;

        p.parse(new String[]{"-yn"});

        Assert.assertTrue(t.set);
        Assert.assertTrue(!t.reset);
    }
    
    @Test
    public void test_positional() {
        Target t = new Target();
        Parser p = new Parser(t);
        
        String[] rest = p.parse(new String[]{"--string=bla", "foo", "bar", "--set", "--", "--reset", "baz"});
        
        Assert.assertArrayEquals(new String[]{"foo", "bar", "--reset", "baz"}, rest);
    }


    @Test(expected = Parser.ArgumentError.class)
    public void test_missingLongopt() {
        Target t = new Target();
        Parser p = new Parser(t);
        p.parse(new String[]{"--foobar"});
    }

    @Test(expected = Parser.ArgumentError.class)
    public void test_missingShortopt_1() {
        Target t = new Target();
        Parser p = new Parser(t);
        p.parse(new String[]{"-x"});
    }

    @Test
    public void test_long() {
        Target t = new Target();
        Parser p = new Parser(t);

        String[] rest;

        rest = p.parse(new String[]{"-w5"});
        Assert.assertEquals(5, t.intVal);

        rest = p.parse(new String[]{"-w", "5"});
        Assert.assertEquals(5, t.intVal);

        rest = p.parse(new String[]{"--value=6"});
        Assert.assertEquals(6, t.intVal);

        rest = p.parse(new String[]{"--value", "7"});
        Assert.assertEquals(7, t.intVal);

        rest = p.parse(new String[]{"--value", "-7"});
        Assert.assertEquals(-7, t.intVal);

        boolean thrown = false;
        try {
            rest = p.parse(new String[]{"--value", "x"});
        } catch (Parser.ArgumentError e) {
            thrown = true;
        }
        Assert.assertTrue(thrown);
    }


    @Test(expected = Parser.ArgumentError.class)
    public void test_missingNumberArgument_short() {
        Target t = new Target();
        Parser p = new Parser(t);

        p.parse(new String[]{"-w"});
        Assert.assertEquals(5, t.intVal);
    }

    @Test(expected = Parser.ArgumentError.class)
    public void test_missingNumberArgument_long() {
        Target t = new Target();
        Parser p = new Parser(t);


        p.parse(new String[]{"--w"});
        Assert.assertEquals(5, t.intVal);
    }


    @Test(expected = Parser.ArgumentError.class)
    public void test_invalidNumberFormat() {
        Target t = new Target();
        Parser p = new Parser(t);


        p.parse(new String[]{"-w", "abc"});
        Assert.assertEquals(5, t.intVal);
    }


    @Test
    public void test_dashRest() {
        Target t = new Target();
        Parser p = new Parser(t);

        String[] rest;

        rest = p.parse(new String[]{"-w", "123", "-"});
        Assert.assertArrayEquals(new String[]{"-"}, rest);
    }


    @Test(expected = AssertionError.class)
    public void test_invalid() {
        InvalidTarget it = new InvalidTarget();
        Parser p = new Parser(it);

        p.parse(new String[]{"-foo"});
    }
}
