<?php

/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

namespace Phing\Test;

use Exception;
use Phing\Exception\BuildException;
use Phing\IntrospectionHelper;
use Phing\Project;
use Phing\Test\Support\IHCreatorFail1;
use Phing\Test\Support\IHCreatorFail2;
use Phing\Test\Support\IHCreatorFail3;
use Phing\Test\Support\IHProjectComponent;
use Phing\Type\FileSet;
use PHPUnit\Framework\TestCase;

/**
 * testcases for phing.IntrospectionHelper.
 *
 * @author Hans Lellelid <hans@xmpl.org> (Phing)
 * @author Stefan Bodewig <stefan.bodewig@epost.de> (Ant)
 *
 * @internal
 */
class IntrospectionHelperTest extends TestCase
{
    /** @var Project */
    private $p;

    public function setUp(): void
    {
        $this->p = new Project();
        $this->p->setBasedir(DIRECTORY_SEPARATOR);
    }

    /**
     * @throws BuildException
     */
    public function testAddText(): void
    {
        $ih = IntrospectionHelper::getHelper(Exception::class);

        try {
            $ih->addText($this->p, new Exception(), 'test');
            $this->fail("Exception doesn\\'t support addText");
        } catch (BuildException $be) {
        }

        $element = new IHProjectComponent();
        $ih = IntrospectionHelper::getHelper(IHProjectComponent::class);
        $ih->addText($this->p, $element, 'test');

        $this->assertSame('test', $element->text);
    }

    public function testSupportsCharactersAdders(): void
    {
        $ih = IntrospectionHelper::getHelper(Exception::class);
        $this->assertFalse($ih->supportsCharacters(), "String doesn\\'t support addText");
        $ih = IntrospectionHelper::getHelper(IHProjectComponent::class);
        $this->assertTrue($ih->supportsCharacters(), 'IHProjectComponent supports addText');
    }

    public function testElementCreators(): void
    {
        try {
            IntrospectionHelper::getHelper(IHCreatorFail1::class);
            $this->fail('create cannot take param');
        } catch (BuildException $be) {
        }

        try {
            IntrospectionHelper::getHelper(IHCreatorFail2::class);
            $this->fail('no class hint for add');
        } catch (BuildException $be) {
        }

        try {
            IntrospectionHelper::getHelper(IHCreatorFail3::class);
            $this->fail('no class hint for addconfigured');
        } catch (BuildException $be) {
        }

        $ih = IntrospectionHelper::getHelper(IHProjectComponent::class);
        $this->assertEquals('test', $ih->createElement($this->p, new IHProjectComponent(), 'one'));

        $fs = new FileSet();
        $fs->setProject($this->p);

        $this->assertEquals($fs, $ih->createElement($this->p, new IHProjectComponent(), 'FileSet'));
    }

    /**
     * @requires PHP >= 8
     * @return void
     */
    public function testUnionTypeOnSetterDoesNotCrashIH(): void
    {
        $clz = eval('return new class {
            public function setSomeAttribute(bool | string $attribute) {

            }
        };');

        $ih = IntrospectionHelper::getHelper(get_class($clz));
        $this->assertContains('someattribute', $ih->getAttributes());
    }

    /*
    public function testGetNestedElements()
    {
        Hashtable h = new Hashtable();
        h.put("six", java.lang.String.class);
        h.put("thirteen", java.lang.StringBuffer.class);
        h.put("fourteen", java.lang.StringBuffer.class);
        h.put("fifteen", java.lang.StringBuffer.class);
        IntrospectionHelper $ih = IntrospectionHelper::getHelper(get_class($this));
        Enumeration enum = ih.getNestedElements();
        while (enum.hasMoreElements()) {
            String name = (String) enum.nextElement();
            Class expect = (Class) h.get(name);
            assertNotNull("Support for "+name+" in IntrospectioNHelperTest?",
                          expect);
            $this->assertEquals("Return type of "+name, expect, ih.getElementType(name));
            h.remove(name);
        }
        $this->assertTrue("Found all", h.isEmpty());
    }

    public function createOne()
    {
        return "test";
    }
    /*
    public function testAttributeSetters()
    {
        $ih = IntrospectionHelper::getHelper(get_class($this));
        try {
            $ih->setAttribute($p, $this, "one", "test");
            $this->fail("setOne doesn't exist");
        } catch (BuildException $be) {
        }
        try {
            $ih->setAttribute($p, $this, "two", "test");
            $this->fail("setTwo returns non void");
        } catch (BuildException be) {
        }
        try {
            ih.setAttribute(p, this, "three", "test");
            $this->fail("setThree takes no args");
        } catch (BuildException be) {
        }
        try {
            ih.setAttribute(p, this, "four", "test");
            $this->fail("setFour takes two args");
        } catch (BuildException be) {
        }
        try {
            ih.setAttribute(p, this, "five", "test");
            $this->fail("setFive takes array arg");
        } catch (BuildException be) {
        }
        try {
            ih.setAttribute(p, this, "six", "test");
            $this->fail("Project doesn't have a String constructor");
        } catch (BuildException be) {
        }
        ih.setAttribute(p, this, "seven", "2");
        try {
            ih.setAttribute(p, this, "seven", "3");
            $this->fail("2 shouldn't be equals to three");
        } catch (BuildException be) {
            $this->assertTrue(be.getException() instanceof AssertionFailedError);
        }
        ih.setAttribute(p, this, "eight", "2");
        try {
            ih.setAttribute(p, this, "eight", "3");
            $this->fail("2 shouldn't be equals to three - as int");
        } catch (BuildException be) {
            $this->assertTrue(be.getException() instanceof AssertionFailedError);
        }
        ih.setAttribute(p, this, "nine", "2");
        try {
            ih.setAttribute(p, this, "nine", "3");
            $this->fail("2 shouldn't be equals to three - as Integer");
        } catch (BuildException be) {
            $this->assertTrue(be.getException() instanceof AssertionFailedError);
        }
        ih.setAttribute(p, this, "ten", "2");
        try {
            ih.setAttribute(p, this, "ten", "3");
            $this->fail(projectBasedir+"2 shouldn't be equals to "+projectBasedir+"3");
        } catch (BuildException be) {
            $this->assertTrue(be.getException() instanceof AssertionFailedError);
        }
        ih.setAttribute(p, this, "eleven", "2");
        try {
            ih.setAttribute(p, this, "eleven", "on");
            $this->fail("on shouldn't be false");
        } catch (BuildException be) {
            $this->assertTrue(be.getException() instanceof AssertionFailedError);
        }
        ih.setAttribute(p, this, "twelve", "2");
        try {
            ih.setAttribute(p, this, "twelve", "on");
            $this->fail("on shouldn't be false");
        } catch (BuildException be) {
            $this->assertTrue(be.getException() instanceof AssertionFailedError);
        }
        ih.setAttribute(p, this, "thirteen", "org.apache.tools.ant.Project");
        try {
            ih.setAttribute(p, this, "thirteen", "org.apache.tools.ant.ProjectHelper");
            $this->fail("org.apache.tools.ant.Project shouldn't be equal to org.apache.tools.ant.ProjectHelper");
        } catch (BuildException be) {
            $this->assertTrue(be.getException() instanceof AssertionFailedError);
        }
        try {
            ih.setAttribute(p, this, "thirteen", "org.apache.tools.ant.Project2");
            $this->fail("org.apache.tools.ant.Project2 doesn't exist");
        } catch (BuildException be) {
            $this->assertTrue(be.getException() instanceof ClassNotFoundException);
        }
        ih.setAttribute(p, this, "fourteen", "2");
        try {
            ih.setAttribute(p, this, "fourteen", "on");
            $this->fail("2 shouldn't be equals to three - as StringBuffer");
        } catch (BuildException be) {
            $this->assertTrue(be.getException() instanceof AssertionFailedError);
        }
        ih.setAttribute(p, this, "fifteen", "abcd");
        try {
            ih.setAttribute(p, this, "fifteen", "on");
            $this->fail("o shouldn't be equal to a");
        } catch (BuildException be) {
            $this->assertTrue(be.getException() instanceof AssertionFailedError);
        }
        ih.setAttribute(p, this, "sixteen", "abcd");
        try {
            ih.setAttribute(p, this, "sixteen", "on");
            $this->fail("o shouldn't be equal to a");
        } catch (BuildException be) {
            $this->assertTrue(be.getException() instanceof AssertionFailedError);
        }
        ih.setAttribute(p, this, "seventeen", "17");
        try {
            ih.setAttribute(p, this, "seventeen", "3");
            $this->fail("17 shouldn't be equals to three");
        } catch (BuildException be) {
            $this->assertTrue(be.getException() instanceof AssertionFailedError);
        }
        ih.setAttribute(p, this, "eightteen", "18");
        try {
            ih.setAttribute(p, this, "eightteen", "3");
            $this->fail("18 shouldn't be equals to three");
        } catch (BuildException be) {
            $this->assertTrue(be.getException() instanceof AssertionFailedError);
        }
        ih.setAttribute(p, this, "nineteen", "19");
        try {
            ih.setAttribute(p, this, "nineteen", "3");
            $this->fail("19 shouldn't be equals to three");
        } catch (BuildException be) {
            $this->assertTrue(be.getException() instanceof AssertionFailedError);
        }
    }

    public void testGetAttributes() {
        Hashtable h = new Hashtable();
        h.put("seven", java.lang.String.class);
        h.put("eight", java.lang.Integer.TYPE);
        h.put("nine", java.lang.Integer.class);
        h.put("ten", java.io.File.class);
        h.put("eleven", java.lang.Boolean.TYPE);
        h.put("twelve", java.lang.Boolean.class);
        h.put("thirteen", java.lang.Class.class);
        h.put("fourteen", java.lang.StringBuffer.class);
        h.put("fifteen", java.lang.Character.TYPE);
        h.put("sixteen", java.lang.Character.class);
        h.put("seventeen", java.lang.Byte.TYPE);
        h.put("eightteen", java.lang.Short.TYPE);
        h.put("nineteen", java.lang.Double.TYPE);

        h.put("name", java.lang.String.class);

        IntrospectionHelper $ih = IntrospectionHelper::getHelper(get_class($this));
        Enumeration enum = ih.getAttributes();
        while (enum.hasMoreElements()) {
            String name = (String) enum.nextElement();
            Class expect = (Class) h.get(name);
            assertNotNull("Support for "+name+" in IntrospectionHelperTest?",
                          expect);
            $this->assertEquals("Type of "+name, expect, ih.getAttributeType(name));
            h.remove(name);
        }
        h.remove("name");
        $this->assertTrue("Found all", h.isEmpty());
    }

    public function setTwo($s)
    {
        return 0;
    }

    public void setThree() {}

    public void setFour(String s1, String s2) {}

    public void setFive(String[] s) {}

    public void setSix(Project p) {}

    public void setSeven(String s) {
        $this->assertEquals("2", s);
    }

    public void setEight(int i) {
        $this->assertEquals(2, i);
    }

    public void setNine(Integer i) {
        $this->assertEquals(2, i.intValue());
    }

    public void setTen(File f) {
        if (Os.isFamily("unix")) {
            $this->assertEquals(projectBasedir+"2", f.getAbsolutePath());
        } elseif (Os.isFamily("netware")) {
            $this->assertEquals(projectBasedir+"2", f.getAbsolutePath().toLowerCase(Locale.US));
        } else {
            $this->assertEquals(":"+projectBasedir+"2", f.getAbsolutePath().toLowerCase(Locale.US).substring(1));
        }
    }

    public void setEleven(boolean b) {
        $this->assertTrue(!b);
    }

    public void setTwelve(Boolean b) {
        $this->assertTrue(!b.booleanValue());
    }

    public void setThirteen(Class c) {
        $this->assertEquals(Project.class, c);
    }

    public void setFourteen(StringBuffer sb) {
        $this->assertEquals("2", sb.toString());
    }

    public void setFifteen(char c) {
        $this->assertEquals(c, 'a');
    }

    public void setSixteen(Character c) {
        $this->assertEquals(c.charValue(), 'a');
    }

    public void setSeventeen(byte b) {
        $this->assertEquals(17, b);
    }

    public void setEightteen(short s) {
        $this->assertEquals(18, s);
    }

    public void setNineteen(double d) {
        $this->assertEquals(19, d, 1e-6);
    }
    */
}
