<?php

namespace Geocoder\Tests\Result;

use Geocoder\Result\Geocoded;
use Geocoder\Tests\TestCase;

/**
 * @author William Durand <william.durand1@gmail.com>
 */
class GeocodedTest extends TestCase
{
    protected $geocoded;

    protected function setUp()
    {
        $this->geocoded = new Geocoded();
    }

    public function testFromArray()
    {
        $array = array(
            'latitude'  => 0.001,
            'longitude' => 1,
            'bounds'    => array(
                'south' => 1,
                'west'  => '2',
                'north' => 3,
                'east'  => 0.1
            ),
            'city'          => 'FOo CITY',
            'cityDistrict'  => 'fOo city District',
            'streetName'    => 'foo bar street',
            'zipcode'       => '65943',
            'region'        => 'FOO region',
            'county'        => 'foo county',
            'countyCode'    => 'foo',
            'regionCode'    => 'FOO',
            'country'       => 'FOO Country',
            'countryCode'   => 'foo',
            'timezone'      => 'FOO/Timezone'
        );

        $this->geocoded->fromArray($array);

        $coordinates = $this->geocoded->getCoordinates();
        $bounds = $this->geocoded->getBounds();

        $this->assertInternalType('array', $coordinates);
        $this->assertEquals(0.001, $coordinates[0]);
        $this->assertEquals(1, $coordinates[1]);
        $this->assertEquals(0.001, $this->geocoded->getLatitude());
        $this->assertEquals(1, $this->geocoded->getLongitude());
        $this->assertArrayHasKey('south', $bounds);
        $this->assertArrayHasKey('west', $bounds);
        $this->assertArrayHasKey('north', $bounds);
        $this->assertArrayHasKey('east', $bounds);
        $this->assertEquals(1, $bounds['south']);
        $this->assertEquals(2, $bounds['west']);
        $this->assertEquals(3, $bounds['north']);
        $this->assertEquals(0.1, $bounds['east']);
        $this->assertEquals('Foo City', $this->geocoded->getCity());
        $this->assertEquals('Foo City District', $this->geocoded->getCityDistrict());
        $this->assertEquals('Foo Bar Street', $this->geocoded->getStreetName());
        $this->assertEquals('65943', $this->geocoded->getZipcode());
        $this->assertEquals('Foo Region', $this->geocoded->getRegion());
        $this->assertEquals('Foo County', $this->geocoded->getCounty());
        $this->assertEquals('FOO', $this->geocoded->getCountyCode());
        $this->assertEquals('Foo Country', $this->geocoded->getCountry());
        $this->assertEquals('FOO', $this->geocoded->getCountryCode());
        $this->assertEquals('FOO/Timezone', $this->geocoded->getTimezone());
        $this->assertEquals('FOO', $this->geocoded->getRegionCode());
    }

    public function testToArray()
    {
        $expected = array(
            'latitude'  => 0.001,
            'longitude' => 1,
            'bounds'    => array(
                'south' => 1,
                'west'  => '2',
                'north' => 3,
                'east'  => 0.1
            ),
            'city'          => 'FOo CITY',
            'zipcode'       => '65943',
            'region'        => 'FOO region',
            'regionCode'    => 'Foo',
            'country'       => 'FOO Country',
            'timezone'      => 'FOO/Timezone'
        );

        $this->geocoded->fromArray($expected);
        $result = $this->geocoded->toArray();

        $this->assertEquals(0.001, $result['latitude']);
        $this->assertEquals(1, $result['longitude']);
        $this->assertArrayHasKey('south', $result['bounds']);
        $this->assertArrayHasKey('west', $result['bounds']);
        $this->assertArrayHasKey('north', $result['bounds']);
        $this->assertArrayHasKey('east', $result['bounds']);

        $bounds = $result['bounds'];
        $this->assertEquals(1, $bounds['south']);
        $this->assertEquals(2, $bounds['west']);
        $this->assertEquals(3, $bounds['north']);
        $this->assertEquals(0.1, $bounds['east']);

        $this->assertEquals('Foo City', $result['city']);
        $this->assertEquals('65943', $result['zipcode']);
        $this->assertEquals('Foo Region', $result['region']);
        $this->assertEquals('Foo Country', $result['country']);
        $this->assertEquals('FOO/Timezone', $result['timezone']);
        $this->assertEquals('FOO', $result['regionCode']);
    }

    public function testFromDataWithEmptyArray()
    {
        $this->geocoded->fromArray(array());

        $this->assertEquals(0, $this->geocoded->getLatitude());
        $this->assertEquals(0, $this->geocoded->getLongitude());
        $this->assertNull($this->geocoded->getBounds());
        $this->assertEquals('', $this->geocoded->getCity());
        $this->assertEquals('', $this->geocoded->getZipcode());
        $this->assertEquals('', $this->geocoded->getRegion());
        $this->assertEquals('', $this->geocoded->getCountry());
        $this->assertEquals('', $this->geocoded->getTimezone());
    }

    public function testFromDataWithNull()
    {
        $array = array(
            'latitude'  => 100,
            'longitude' => 1.2
        );

        $this->geocoded->fromArray($array);

        $this->assertEquals(100, $this->geocoded->getLatitude());
        $this->assertEquals(1.2, $this->geocoded->getLongitude());
        $this->assertNull($this->geocoded->getBounds());
        $this->assertEquals('', $this->geocoded->getCity());
        $this->assertEquals('', $this->geocoded->getZipcode());
        $this->assertEquals('', $this->geocoded->getRegion());
        $this->assertEquals('', $this->geocoded->getCountry());
        $this->assertEquals('', $this->geocoded->getTimezone());
    }

    public function testArrayInterface()
    {
        $array = array(
            'latitude'  => 0.001,
            'longitude' => 1,
            'bounds'    => array(
                'south' => 1,
                'west'  => '2',
                'north' => 3,
                'east'  => 0.1
            ),
            'city'      => 'FOo CITY',
            'zipcode'   => '65943',
            'region'    => 'FOO region',
            'country'   => 'FOO Country',
            'timezone'  => 'Foo/Timezone',
            'countryCode' => 'XX'
        );

        $this->geocoded->fromArray($array);

        // array access
        $this->assertEquals(0.001, $this->geocoded['latitude']);
        $this->assertEquals(1, $this->geocoded['longitude']);
        $this->assertInternalType('array', $this->geocoded['bounds']);
        $this->assertEquals('Foo City', $this->geocoded['city']);
        $this->assertEquals('65943', $this->geocoded['zipcode']);
        $this->assertEquals('Foo Region', $this->geocoded['region']);
        $this->assertEquals('Foo Country', $this->geocoded['country']);
        $this->assertEquals('Foo/Timezone', $this->geocoded['timezone']);

        $this->assertEquals('XX', $this->geocoded['countryCode']);

        // isset
        $this->assertEquals(true, isset($this->geocoded['latitude']));
        $this->assertEquals(true, isset($this->geocoded['longitude']));
        $this->assertEquals(true, isset($this->geocoded['bounds']));
        $this->assertEquals(true, isset($this->geocoded['city']));
        $this->assertEquals(true, isset($this->geocoded['zipcode']));
        $this->assertEquals(true, isset($this->geocoded['region']));
        $this->assertEquals(true, isset($this->geocoded['country']));
        $this->assertEquals(true, isset($this->geocoded['timezone']));
        $this->assertEquals(false, isset($this->geocoded['other']));

        // set
        $this->geocoded['latitude'] = 0.123456;
        $this->assertEquals(0.123456, $this->geocoded['latitude']);

        // unset
        unset($this->geocoded['latitude']);
        $this->assertEquals(false, isset($this->geocoded['latitude']));
    }

    public function testFromArrayWithAccents()
    {
        $array = array(
            'latitude'  => 0.001,
            'longitude' => 1,
            'bounds'    => array(
                'south' => 1,
                'west'  => '2',
                'north' => 3,
                'east'  => 0.1
            ),
            'city'      => 'Toulouse',
            'zipcode'   => '31000',
            'region'    => 'Midi-Pyrénées',
            'country'   => 'France',
            'timezone'  => 'Europe/Paris'
        );

        $this->geocoded->fromArray($array);

        $bounds = $this->geocoded->getBounds();

        $this->assertEquals(0.001, $this->geocoded->getLatitude());
        $this->assertEquals(1, $this->geocoded->getLongitude());
        $this->assertArrayHasKey('south', $bounds);
        $this->assertArrayHasKey('west', $bounds);
        $this->assertArrayHasKey('north', $bounds);
        $this->assertArrayHasKey('east', $bounds);
        $this->assertEquals(1, $bounds['south']);
        $this->assertEquals(2, $bounds['west']);
        $this->assertEquals(3, $bounds['north']);
        $this->assertEquals(0.1, $bounds['east']);
        $this->assertEquals('Toulouse', $this->geocoded->getCity());
        $this->assertEquals('31000', $this->geocoded->getZipcode());
        $this->assertEquals('Midi-Pyrénées', $this->geocoded->getRegion());
        $this->assertEquals('France', $this->geocoded->getCountry());
        $this->assertEquals('Europe/Paris', $this->geocoded->getTimezone());
    }

    public function testFromArrayWithLettersInStreetNumber()
    {
        $array = array(
            'streetNumber'   => '1A'
        );

        $this->geocoded->fromArray($array);
        $this->assertEquals('1A', $this->geocoded->getStreetNumber());
    }

    public function testLowerizeViaReflection()
    {
        $method = new \ReflectionMethod(
            $this->geocoded, 'lowerize'
        );
        $method->setAccessible(true);

        $this->assertEquals('foo', $method->invoke($this->geocoded, 'FOO'));
    }

    public function testUpperizeViaReflection()
    {
        $method = new \ReflectionMethod(
            $this->geocoded, 'upperize'
        );
        $method->setAccessible(true);

        $this->assertEquals('FOO', $method->invoke($this->geocoded, 'foo'));
    }

    public function testResultFormattingWithLeadingNumeral()
    {
        if (version_compare(phpversion(), '5.5.16', '<')) {
            $this->markTestSkipped("Character property matching for mb_ereg doesn't work for PHP < 5.5");
        }
        // MB_TITLE_CASE Will turn this into 1St so let's test to ensure we are correcting that
        // We do not want to "correct" 5C, however, as it is part of the original string
        $array = array(
            'streetName' => '1st ave 1A',
        );

        $this->geocoded->fromArray($array);
        $this->assertEquals('1st Ave 1A', $this->geocoded->getStreetName());
    }
}
