/**
 * Copyright (c) 2008, SnakeYAML
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package org.yaml.snakeyaml;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import junit.framework.TestCase;
import org.yaml.snakeyaml.constructor.AbstractConstruct;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.ScalarNode;
import org.yaml.snakeyaml.nodes.Tag;

/**
 * Test Chapter 2.4 from the YAML specification
 */
public class Chapter2_4Test extends TestCase {

  @SuppressWarnings("unchecked")
  public void testExample_2_19() {
    YamlDocument document = new YamlDocument("example2_19.yaml");
    Map<String, Object> map = (Map<String, Object>) document.getNativeData();
    assertEquals(5, map.size());
    assertEquals("Expect 12345 to be an Integer.", Integer.class, map.get("canonical").getClass());
    assertEquals(Integer.valueOf(12345), map.get("canonical"));
    assertEquals(Integer.valueOf(12345), map.get("decimal"));
    assertEquals(Integer.valueOf(3 * 3600 + 25 * 60 + 45), map.get("sexagesimal"));
    assertEquals(Integer.valueOf(014), map.get("octal"));
    assertEquals(Integer.valueOf(0xC), map.get("hexadecimal"));
  }

  @SuppressWarnings("unchecked")
  public void testExample_2_20() {
    YamlDocument document = new YamlDocument("example2_20.yaml");
    Map<String, Object> map = (Map<String, Object>) document.getNativeData();
    assertEquals(6, map.size());
    assertEquals("Expect '1.23015e+3' to be a Double.", Double.class,
        map.get("canonical").getClass());
    assertEquals(Double.valueOf(1230.15), map.get("canonical"));
    assertEquals(Double.valueOf(12.3015e+02), map.get("exponential"));
    assertEquals(Double.valueOf(20 * 60 + 30.15), map.get("sexagesimal"));
    assertEquals(Double.valueOf(1230.15), map.get("fixed"));
    assertEquals(Double.NEGATIVE_INFINITY, map.get("negative infinity"));
    assertEquals(Double.NaN, map.get("not a number"));
  }

  @SuppressWarnings("unchecked")
  public void testExample_2_21() {
    YamlDocument document = new YamlDocument("example2_21.yaml");
    Map<String, Object> map = (Map<String, Object>) document.getNativeData();
    assertEquals(4, map.size());
    assertNull("'~' must be parsed as 'null': " + map.get(null), map.get(null));
    assertTrue((Boolean) map.get(Boolean.TRUE));
    assertFalse((Boolean) map.get(Boolean.FALSE));
    assertEquals("12345", map.get("string"));
  }

  @SuppressWarnings("unchecked")
  public void testExample_2_22() {
    YamlDocument document = new YamlDocument("example2_22.yaml");
    Map<String, Object> map = (Map<String, Object>) document.getNativeData();
    assertEquals(4, map.size());
    assertEquals("Expect '2001-12-15T02:59:43.1Z' to be a Date.", Date.class,
        map.get("canonical").getClass());
    Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
    cal.clear();
    cal.set(Calendar.YEAR, 2001);
    cal.set(Calendar.MONTH, 11); // Java's months are zero-based...
    cal.set(Calendar.DAY_OF_MONTH, 15);
    cal.set(Calendar.HOUR_OF_DAY, 2);
    cal.set(Calendar.MINUTE, 59);
    cal.set(Calendar.SECOND, 43);
    cal.set(Calendar.MILLISECOND, 100);
    Date date = cal.getTime();
    assertEquals(date, map.get("canonical"));
    assertEquals("Expect '2001-12-14t21:59:43.10-05:00' to be a Date.", Date.class,
        map.get("iso8601").getClass());
    assertEquals("Expect '2001-12-14 21:59:43.10 -5' to be a Date.", Date.class,
        map.get("spaced").getClass());
    assertEquals("Expect '2002-12-14' to be a Date.", Date.class, map.get("date").getClass());
  }

  @SuppressWarnings("unchecked")
  public void testExample_2_23_non_date() {
    try {
      YamlDocument document = new YamlDocument("example2_23_non_date.yaml");
      Map<String, Object> map = (Map<String, Object>) document.getNativeData();
      assertEquals(1, map.size());
      assertEquals("2002-04-28", map.get("not-date"));
    } catch (RuntimeException e) {
      fail("Cannot parse '!!str': 'not-date: !!str 2002-04-28'");
    }
  }

  @SuppressWarnings("unchecked")
  public void testExample_2_23_picture() {
    YamlDocument document = new YamlDocument("example2_23_picture.yaml", false);
    Map<String, Object> map = (Map<String, Object>) document.getNativeData();
    assertEquals(1, map.size());
    byte[] picture = (byte[]) map.get("picture");
    assertEquals((byte) 'G', picture[0]);
    assertEquals((byte) 'I', picture[1]);
    assertEquals((byte) 'F', picture[2]);
  }

  class SomethingConstructor extends Constructor {

    public SomethingConstructor() {
      super(new LoaderOptions());
      this.yamlConstructors.put(new Tag("!something"), new ConstructSomething());
    }

    private class ConstructSomething extends AbstractConstruct {

      public Object construct(Node node) {
        // convert to upper case
        String val = constructScalar((ScalarNode) node);
        return val.toUpperCase().replace('\n', ' ').trim();
      }
    }
  }

  @SuppressWarnings("unchecked")
  public void testExample_2_23() {
    YamlDocument document = new YamlDocument("example2_23.yaml", false, new SomethingConstructor());
    Map<String, Object> map = (Map<String, Object>) document.getNativeData();
    assertEquals(3, map.size());
    String special = (String) map.get("application specific tag");
    assertEquals("THE SEMANTICS OF THE TAG ABOVE MAY BE DIFFERENT FOR DIFFERENT DOCUMENTS.",
        special);
  }

  @SuppressWarnings("unchecked")
  public void testExample_2_25() {
    YamlDocument document = new YamlDocument("example2_25.yaml");
    Set<String> set = (Set<String>) document.getNativeData();
    assertEquals(3, set.size());
    assertTrue(set.contains("Mark McGwire"));
    assertTrue(set.contains("Sammy Sosa"));
    assertTrue(set.contains("Ken Griff"));
  }

  @SuppressWarnings("unchecked")
  public void testExample_2_26() {
    YamlDocument document = new YamlDocument("example2_26.yaml");
    Map<String, String> map = (Map<String, String>) document.getNativeData();
    assertEquals(3, map.size());
    assertTrue(map instanceof LinkedHashMap);
    assertEquals(Integer.valueOf(65), map.get("Mark McGwire"));
    assertEquals(Integer.valueOf(63), map.get("Sammy Sosa"));
    assertEquals(Integer.valueOf(58), map.get("Ken Griffy"));
    List<String> list = new ArrayList<String>();
    for (String key : map.keySet()) {
      list.add(key);
    }
    assertEquals("Mark McGwire", list.get(0));
    assertEquals("Sammy Sosa", list.get(1));
    assertEquals("Ken Griffy", list.get(2));
  }
}
