package jp.sourceforge.glad.calendar.joda.chrono;

import static jp.sourceforge.glad.calendar.CalendarConsts.*;
import jp.sourceforge.glad.calendar.joda.format.DateTimeFormat;
import junit.framework.TestCase;

import org.joda.time.DateTime;
import org.joda.time.DateTimeUtils;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormatter;

/**
 * {@link JapaneseChronology} の単体テスト。
 * 
 * @author GLAD!!
 */
public class JapaneseChronologyTest extends TestCase {

    static final DateTimeZone TOKYO = DateTimeZone.forID("Asia/Tokyo");

    JapaneseChronology chrono;
    DateTimeFormatter jisFmt;

    DateTimeZone defaultZone;
    DateTime fixedDateTime;

    protected void setUp() throws Exception {
        defaultZone = DateTimeZone.getDefault();
        DateTimeZone.setDefault(TOKYO);
        fixedDateTime = new DateTime(2008, 1, 23, 1, 12, 23, 123);
        DateTimeUtils.setCurrentMillisFixed(fixedDateTime.getMillis());
        chrono = JapaneseChronology.getInstance();
        jisFmt = DateTimeFormat.forPattern("GYY.MM.dd");
    }

    protected void tearDown() throws Exception {
        chrono = null;
        DateTimeZone.setDefault(defaultZone);
        defaultZone = null;
        DateTimeUtils.setCurrentMillisSystem();
        fixedDateTime = null;
    }

    public void testInstanceUTC() {
        JapaneseChronology chrono = JapaneseChronology.getInstanceUTC();
        assertSame(JapaneseChronology.INSTANCE_UTC, chrono);
        assertSame(JapaneseChronology.INSTANCE_UTC, chrono.withUTC());
        assertSame(JapaneseChronology.INSTANCE_JST, chrono.withJST());
        assertEquals("JapaneseChronology[UTC]", chrono.toString());
    }

    public void testInstanceJST() {
        JapaneseChronology chrono = JapaneseChronology.getInstanceJST();
        assertSame(JapaneseChronology.INSTANCE_JST, chrono);
        assertSame(JapaneseChronology.INSTANCE_UTC, chrono.withUTC());
        assertSame(JapaneseChronology.INSTANCE_JST, chrono.withJST());
        assertEquals("JapaneseChronology[Asia/Tokyo]", chrono.toString());
    }

    public void testInstance() {
        JapaneseChronology chrono = JapaneseChronology.getInstance();
        assertEquals(DateTimeZone.getDefault(), chrono.getZone());
        assertSame(JapaneseChronology.INSTANCE_UTC, chrono.withUTC());
        assertSame(JapaneseChronology.INSTANCE_JST, chrono.withJST());
        assertEquals("JapaneseChronology[Asia/Tokyo]", chrono.toString());
    }

    public void testInstance_withZone() {
        DateTimeZone zone = DateTimeZone.forOffsetHours(9);
        JapaneseChronology chrono = JapaneseChronology.getInstance(zone);
        assertEquals(zone, chrono.getZone());
        assertSame(JapaneseChronology.INSTANCE_UTC, chrono.withUTC());
        assertSame(JapaneseChronology.INSTANCE_JST, chrono.withJST());
        assertEquals("JapaneseChronology[+09:00]", chrono.toString());
    }

    public void testFields() {
        DateTime dateTime = new DateTime(2008, 1, 23, 1, 12, 23, 123, chrono);
        assertEquals(2008, dateTime.getYear());
        assertEquals(HEISEI, dateTime.getEra());
        assertEquals(20, dateTime.getYearOfEra());
        assertEquals(21, dateTime.getCenturyOfEra());
        assertEquals( 8, dateTime.getYearOfCentury());
        assertEquals( 1, dateTime.getMonthOfYear());
        assertEquals(23, dateTime.getDayOfYear());
        assertEquals(23, dateTime.getDayOfMonth());
        assertEquals(WEDNESDAY, dateTime.getDayOfWeek());
        assertEquals( 1, dateTime.getHourOfDay());
        assertEquals(12, dateTime.getMinuteOfHour());
        assertEquals(23, dateTime.getSecondOfMinute());
        assertEquals(123, dateTime.getMillisOfSecond());
    }

    public void testBeforeMeiji() {
        DateTime dt18671231 = new DateTime(1867, 12, 31, 23, 59, 59, 999, chrono);
        assertEquals(BEFORE_MEIJI, dt18671231.getEra());
        assertEquals(1867, dt18671231.getYearOfEra());
        assertEquals("1867.12.31", jisFmt.print(dt18671231));
    }

    /**
     * 明治に関するテスト。
     */
    public void testMeiji() {
        // 1873-01-01 までは旧暦なので、正確には日付が異なるが、
        // Java SE 6.0 の実装に合わせた。
        DateTime dt18680101 = new DateTime(1868, 1, 1, 0, 0, 0, 0, chrono);
        assertEquals(MEIJI, dt18680101.getEra());
        assertEquals(1, dt18680101.getYearOfEra());
        assertEquals("M01.01.01", jisFmt.print(dt18680101));
        
        // 1873-01-01 から新暦 (グレゴリオ暦採用) を採用。
        DateTime dt18730101 = new DateTime(1873, 1, 1, 0, 0, 0, 0, chrono);
        assertEquals(MEIJI, dt18730101.getEra());
        assertEquals(6, dt18730101.getYearOfEra());
        assertEquals("M06.01.01", jisFmt.print(dt18730101));
        
        // 明治最後の日。
        DateTime dt19120729 = new DateTime(1912, 7, 29, 23, 59, 59, 999, chrono);
        assertEquals(MEIJI, dt19120729.getEra());
        assertEquals(45, dt19120729.getYearOfEra());
        assertEquals("M45.07.29", jisFmt.print(dt19120729));
        
        // 和暦から西暦へ。
        try {
            getGregorianYear(MEIJI, 0);
            fail();
        } catch (IllegalArgumentException e) {}
        assertEquals(1868, getGregorianYear(MEIJI, 1));
        assertEquals(1873, getGregorianYear(MEIJI, 6));
        assertEquals(1912, getGregorianYear(MEIJI, 45));
        assertEquals(1913, getGregorianYear(MEIJI, 46));
    }

    /**
     * 大正に関するテスト。
     */
    public void testTaisho() {
        // 大正最初の日。
        DateTime dt19120730 = new DateTime(1912, 7, 30, 0, 0, 0, 0, chrono);
        assertEquals(TAISHO, dt19120730.getEra());
        assertEquals(1, dt19120730.getYearOfEra());
        assertEquals("T01.07.30", jisFmt.print(dt19120730));
        
        // 大正最後の日。
        DateTime dt19261224 = new DateTime(1926, 12, 24, 23, 59, 59, 999, chrono);
        assertEquals(TAISHO, dt19261224.getEra());
        assertEquals(15, dt19261224.getYearOfEra());
        assertEquals("T15.12.24", jisFmt.print(dt19261224));
        
        // 和暦から西暦へ。
        try {
            getGregorianYear(TAISHO, 0);
            fail();
        } catch (IllegalArgumentException e) {}
        assertEquals(1912, getGregorianYear(TAISHO, 1));
        assertEquals(1926, getGregorianYear(TAISHO, 15));
        assertEquals(1927, getGregorianYear(TAISHO, 16));
    }

    /**
     * 昭和に関するテスト。
     */
    public void testShowa() {
        // 昭和最初の日。
        DateTime dt19261225 = new DateTime(1926, 12, 25, 0, 0, 0, 0, chrono);
        assertEquals(SHOWA, dt19261225.getEra());
        assertEquals(1, dt19261225.getYearOfEra());
        assertEquals("S01.12.25", jisFmt.print(dt19261225));
        
        // 昭和最後の日。
        DateTime dt19890107 = new DateTime(1989, 1, 7, 23, 59, 59, 999, chrono);
        assertEquals(SHOWA, dt19890107.getEra());
        assertEquals(64, dt19890107.getYearOfEra());
        assertEquals("S64.01.07", jisFmt.print(dt19890107));
        
        // 和暦から西暦へ。
        try {
            getGregorianYear(SHOWA, 0);
            fail();
        } catch (IllegalArgumentException e) {}
        assertEquals(1926, getGregorianYear(SHOWA, 1));
        assertEquals(1989, getGregorianYear(SHOWA, 64));
        assertEquals(1990, getGregorianYear(SHOWA, 65));
    }

    /**
     * 平成に関するテスト。
     */
    public void testHeisei() {
        // 平成最初の日。
        DateTime dt19890108 = new DateTime(1989, 1, 8, 0, 0, 0, 0, chrono);
        assertEquals(HEISEI, dt19890108.getEra());
        assertEquals(1, dt19890108.getYearOfEra());
        assertEquals("H01.01.08", jisFmt.print(dt19890108));
        
        DateTime dt20080123 = new DateTime(2008, 1, 23, 1, 12, 23, 123, chrono);
        assertEquals(HEISEI, dt20080123.getEra());
        assertEquals(20, dt20080123.getYearOfEra());
        assertEquals("H20.01.23", jisFmt.print(dt20080123));
        
        // 和暦から西暦へ。
        try {
            getGregorianYear(HEISEI, 0);
            fail();
        } catch (IllegalArgumentException e) {}
        assertEquals(1989, getGregorianYear(HEISEI, 1));
        assertEquals(2008, getGregorianYear(HEISEI, 20));
    }

    int getGregorianYear(int era, int yearOfEra) {
        return new DateTime(chrono)
                .withEra(era)
                .withYearOfEra(yearOfEra)
                .getYear();
    }

}
