/*
 * Copyright (c) 2009 OrangeSignal.com All rights reserved.
 * 
 * これは Apache ライセンス Version 2.0 (以下、このライセンスと記述) に
 * 従っています。このライセンスに準拠する場合以外、このファイルを使用
 * してはなりません。このライセンスのコピーは以下から入手できます。
 * 
 * http://www.apache.org/licenses/LICENSE-2.0.txt
 * 
 * 適用可能な法律がある、あるいは文書によって明記されている場合を除き、
 * このライセンスの下で配布されているソフトウェアは、明示的であるか暗黙の
 * うちであるかを問わず、「保証やあらゆる種類の条件を含んでおらず」、
 * 「あるがまま」の状態で提供されるものとします。
 * このライセンスが適用される特定の許諾と制限については、このライセンス
 * を参照してください。
 */

package jp.sf.orangesignal.ta.data;

import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;

import jp.sf.orangesignal.ta.data.annotation.AnnotationParser;
import jp.sf.orangesignal.ta.data.model.Margin;
import jp.sf.orangesignal.ta.data.model.Price;
import jp.sf.orangesignal.ta.data.model.PriceDataset;

import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * {@link DatasetItems} クラスの単体テストです。
 * 
 * @author 杉澤 浩二
 */
public class DatasetItemsTest {

	private Date[] date;
	private Number[] data;
	private PriceDataset dataset;

	// ------------------------------------------------------------------------

	@Before
	public void setUp() throws Exception {
		final SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd");
		date = new Date[]{
			df.parse("2009/01/03"),
			df.parse("2009/01/06"),
			df.parse("2009/01/05"),
			df.parse("2009/01/10")
		};
		data = new Number[]{ 300, 200, 400, 500, 700, null };

		final CsvPriceLoader loader = (CsvPriceLoader) new ClassPathXmlApplicationContext("applicationContext.xml").getBean("loader", CsvPriceLoader.class);
		dataset = PriceDataset.newInstance(loader.load("n225").toArray(new Price[0]));
	}

	// ------------------------------------------------------------------------

	@Test
	public void testDatasetItems() {
		// Act
		new DatasetItems();
	}

	// ------------------------------------------------------------------------

	@Test
	public void testGetUniqueDateEntry() throws ParseException {
		// Arrange
		final DatasetItems items = new DatasetItems();
		items.setDate("date", this.date);

		// Act
		final Map.Entry<String, Date[]> entry = items.getUniqueDateEntry();

		// Assert
		assertNotNull(entry);
		assertThat(entry.getKey(), is("date"));
		final Date[] date = entry.getValue();
		assertThat(date.length, is(4));
		final SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd");
		assertThat(date[0], is(df.parse("2009/01/03")));
		assertThat(date[1], is(df.parse("2009/01/06")));
		assertThat(date[2], is(df.parse("2009/01/05")));
		assertThat(date[3], is(df.parse("2009/01/10")));
	}

	@Test(expected = IllegalStateException.class)
	public void testGetUniqueDateEntryIllegalStateException1() {
		// Arrange
		final DatasetSource items = new DatasetItems();
		// Act
		items.getUniqueDateEntry();
	}

	@Test(expected = IllegalStateException.class)
	public void testGetUniqueDateEntryIllegalStateException2() {
		// Arrange
		final DatasetItems items = new DatasetItems();
		items.setDate("date1", date);
		items.setDate("date2", date);
		// Act
		items.getUniqueDateEntry();
	}

	@Test
	public void testGetDate() {
		// Arrange
		final DatasetSource items = new DatasetItems();
		// Act, Assert
		assertNull(items.getDate("date"));
	}

	@Test
	public void testGetDateMap() {
		// Arrange
		final DatasetSource items = new DatasetItems();
		// Act, Assert
		assertNotNull(items.getDateMap());
	}

	@Test
	public void testSetDate() throws ParseException {
		// Arrange
		final DatasetItems items = new DatasetItems();
		// Act
		items.setDate("date", date);
		// Assert
		final Date[] date = items.getDate("date");
		assertThat(date.length, is(4));
		final SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd");
		assertThat(date[0], is(df.parse("2009/01/03")));
		assertThat(date[1], is(df.parse("2009/01/06")));
		assertThat(date[2], is(df.parse("2009/01/05")));
		assertThat(date[3], is(df.parse("2009/01/10")));
	}

	@Test
	public void testGetUniqueNumberEntry() {
		// Arrange
		final DatasetItems items = new DatasetItems();
		items.setNumber("close", data);
		// Act
		final Map.Entry<String, Number[]> entry = items.getUniqueNumberEntry();
		assertNotNull(entry);
		assertThat(entry.getKey(), is("close"));
		final Number[] data = entry.getValue();
		assertThat(data.length, is(6));
		assertThat(data[0].intValue(), is(300));
		assertThat(data[1].intValue(), is(200));
		assertThat(data[2].intValue(), is(400));
		assertThat(data[3].intValue(), is(500));
		assertThat(data[4].intValue(), is(700));
		assertNull(data[5]);
	}

	@Test(expected = IllegalStateException.class)
	public void testGetUniqueNumberEntryIllegalStateException1() {
		// Arrange
		final DatasetSource items = new DatasetItems();
		// Act
		items.getUniqueNumberEntry();
	}

	@Test(expected = IllegalStateException.class)
	public void testGetUniqueNumberEntryIllegalStateException2() {
		// Arrange
		final DatasetItems items = new DatasetItems();
		items.setNumber("close1", data);
		items.setNumber("close2", data);
		// Act
		items.getUniqueNumberEntry();
	}

	@Test
	public void testGetNumber() {
		// Arrange
		final DatasetSource items = new DatasetItems();
		// Act, Assert
		assertNull(items.getNumber("close"));
	}

	@Test
	public void testGetNumberMap() {
		// Arrange
		final DatasetSource items = new DatasetItems();
		// Act, Assert
		assertNotNull(items.getNumberMap());
	}

	@Test
	public void testSetNumber() {
		// Arrange
		final DatasetItems items = new DatasetItems();
		// Act
		items.setNumber("close", data);
		// Assert
		final Number[] data = items.getNumber("close");
		assertThat(data.length, is(6));
		assertThat(data[0].intValue(), is(300));
		assertThat(data[1].intValue(), is(200));
		assertThat(data[2].intValue(), is(400));
		assertThat(data[3].intValue(), is(500));
		assertThat(data[4].intValue(), is(700));
		assertNull(data[5]);
	}

	@Test
	public void testGetDateFormatConfig() {
		// Arrange
		final DatasetSource items = new DatasetItems();
		// Act, Assert
		assertNull(items.getDateFormatConfig("date"));
	}

	@Test
	public void testSetDateFormatConfig() {
		// Arrange
		final DatasetItems items = new DatasetItems();
		final DateFormatConfig config1 = new DateFormatConfig("yyyy/MM/dd", null, null);
		final DateFormatConfig config2 = new DateFormatConfig("yyyy/MM/dd", Locale.JAPAN, TimeZone.getTimeZone("Asia/Tokyo"));
		// Act
		items.setDateFormatConfig("date1", config1);
		items.setDateFormatConfig("date2", config2);
		// Assert
		final DateFormatConfig r1 = items.getDateFormatConfig("date1");
		assertNotNull(r1);
		assertThat(r1.getPattern(), is("yyyy/MM/dd"));
		assertNull(r1.getLocale());
		assertNull(r1.getTimeZone());
		final DateFormatConfig r2 = items.getDateFormatConfig("date2");
		assertNotNull(r2);
		assertThat(r2.getPattern(), is("yyyy/MM/dd"));
		assertThat(r2.getLocale(), is(Locale.JAPAN));
		assertThat(r2.getTimeZone(), is(TimeZone.getTimeZone("Asia/Tokyo")));
	}

	@Test
	public void testGetNumberFormatConfig() {
		// Arrange
		final DatasetSource items = new DatasetItems();
		// Act, Assert
		assertNull(items.getNumberFormatConfig("close"));
	}

	@Test
	public void testSetNumberFormatConfig() {
		// Arrange
		final DatasetItems items = new DatasetItems();
		final NumberFormatConfig config1 = new NumberFormatConfig("#,##0", null, null);
		final NumberFormatConfig config2 = new NumberFormatConfig("\u00A4\u00A4 #,##0", Locale.JAPAN, "JPY");
		// Act
		items.setNumberFormatConfig("close", config1);
		items.setNumberFormatConfig("jpy", config2);
		// Assert
		final NumberFormatConfig r1 = items.getNumberFormatConfig("close");
		assertNotNull(r1);
		assertThat(r1.getPattern(), is("#,##0"));
		assertNull(r1.getLocale());
		assertNull(r1.getCurrencyCode());
		final NumberFormatConfig r2 = items.getNumberFormatConfig("jpy");
		assertNotNull(r2);
		assertThat(r2.getPattern(), is("\u00A4\u00A4 #,##0"));
		assertThat(r2.getLocale(), is(Locale.JAPAN));
		assertThat(r2.getCurrencyCode(), is("JPY"));
	}

	@Test
	public void testGetCompressType() {
		// Arrange
		final DatasetSource items = new DatasetItems();
		// Act, Assert
		assertNull(items.getCompressType("close"));
	}

	@Test
	public void testGetCompressTypeMap() {
		// Arrange
		final DatasetSource items = new DatasetItems();
		// Act, Assert
		assertNotNull(items.getCompressTypeMap());
	}

	@Test
	public void testSetCompressType() {
		// Arrange
		final DatasetItems items = new DatasetItems();
		// Act
		items.setCompressType("close", CompressType.LAST);
		// Assert
		assertThat(items.getCompressType("close"), is(CompressType.LAST));
	}

	@Test
	public void testIsCompressable() {
		// Arrange
		final DatasetItems items = new DatasetItems();
		// Act, Assert
		assertFalse(items.isCompressable());
		// Act
		items.setCompressType("close", CompressType.LAST);
		// Assert
		assertTrue(items.isCompressable());
	}

	@Test
	public void testMergeDatasetItemsMergeMatchType() throws ParseException {
		final SimpleDateFormat ymd = new SimpleDateFormat("yyyy/MM/dd");

		// Arrange
		final int length = dataset.getDate().length;
		final List<Price> priceList = new ArrayList<Price>(length);
		for (int i = 0; i < length; i++) {
			final Price data = new Price();
			data.setDate(dataset.getDate()[i]);
			data.setOpen(dataset.getOpen()[i]);
			data.setHigh(dataset.getHigh()[i]);
			data.setLow(dataset.getLow()[i]);
			data.setClose(dataset.getClose()[i]);
			data.setVolume(dataset.getVolume()[i]);
			priceList.add(data);
		}
		final DatasetSource items = AnnotationParser.parse(priceList);

		final List<Margin> marginList = new ArrayList<Margin>();
		Margin margin = new Margin();
		margin.setDate(ymd.parse("1998/01/27"));
		margin.setSold(200);
		margin.setBought(null);
		marginList.add(margin);
		margin = new Margin();
		margin.setDate(ymd.parse("1998/02/17"));
		margin.setSold(500);
		margin.setBought(300);
		marginList.add(margin);
		margin = new Margin();
		margin.setDate(ymd.parse("1998/03/17"));
		margin.setSold(null);
		margin.setBought(null);
		marginList.add(margin);
		final DatasetSource marginItems = AnnotationParser.parse(marginList);

		// Act
		items.merge(marginItems, MergeMatchType.CURRENT);

		// Assert
		assertThat(items.getDate("date")[0], is(ymd.parse("1998/01/05")));
		assertThat(items.getNumber("open")[0].intValue(), is(15268));
		assertThat(items.getNumber("high")[0].intValue(), is(15307));
		assertThat(items.getNumber("low")[0].intValue(), is(14956));
		assertThat(items.getNumber("close")[0].intValue(), is(14956));
		assertThat(items.getNumber("volume")[0].intValue(), is(156617100));
		assertThat(items.getDate("date")[1], is(ymd.parse("1998/01/06")));
		assertThat(items.getNumber("open")[1].intValue(), is(15007));
		assertThat(items.getNumber("high")[1].intValue(), is(15066));
		assertThat(items.getNumber("low")[1].intValue(), is(14714));
		assertThat(items.getNumber("close")[1].intValue(), is(14896));
		assertThat(items.getNumber("volume")[1].intValue(), is(346720500));

		assertNull(items.getNumber("sold")[0]);
		assertThat(items.getNumber("sold")[15].intValue(), is(200));
		assertNull(items.getNumber("bought")[15]);
		assertNull(items.getNumber("sold")[16]);
		assertNull(items.getNumber("bought")[16]);
		assertThat(items.getNumber("sold")[29].intValue(), is(500));
		assertThat(items.getNumber("bought")[29].intValue(), is(300));
		assertNull(items.getNumber("sold")[49]);
		assertNull(items.getNumber("bought")[49]);
	}

	@Test
	public void testMergeDatasetItemsMergeMatchTypeMergeGapFillTypeNumber() throws ParseException {
		final SimpleDateFormat ymd = new SimpleDateFormat("yyyy/MM/dd");

		// Arrange
		final int length = dataset.getDate().length;
		final List<Price> priceList = new ArrayList<Price>(length);
		for (int i = 0; i < length; i++) {
			final Price data = new Price();
			data.setDate(dataset.getDate()[i]);
			data.setOpen(dataset.getOpen()[i]);
			data.setHigh(dataset.getHigh()[i]);
			data.setLow(dataset.getLow()[i]);
			data.setClose(dataset.getClose()[i]);
			data.setVolume(dataset.getVolume()[i]);
			priceList.add(data);
		}
		final DatasetSource items = AnnotationParser.parse(priceList);

		final List<Margin> marginList = new ArrayList<Margin>();
		Margin margin = new Margin();
		margin.setDate(ymd.parse("1998/01/27"));
		margin.setSold(200);
		margin.setBought(null);
		marginList.add(margin);
		margin = new Margin();
		margin.setDate(ymd.parse("1998/02/17"));
		margin.setSold(500);
		margin.setBought(300);
		marginList.add(margin);
		margin = new Margin();
		margin.setDate(ymd.parse("1998/03/17"));
		margin.setSold(null);
		margin.setBought(null);
		marginList.add(margin);
		final DatasetSource marginItems = AnnotationParser.parse(marginList);

		// Act
		items.merge(marginItems, MergeMatchType.CURRENT, MergeGapFillType.PREVIOUS, null);

		// Assert
		assertThat(items.getDate("date")[0], is(ymd.parse("1998/01/05")));
		assertThat(items.getNumber("open")[0].intValue(), is(15268));
		assertThat(items.getNumber("high")[0].intValue(), is(15307));
		assertThat(items.getNumber("low")[0].intValue(), is(14956));
		assertThat(items.getNumber("close")[0].intValue(), is(14956));
		assertThat(items.getNumber("volume")[0].intValue(), is(156617100));
		assertThat(items.getDate("date")[1], is(ymd.parse("1998/01/06")));
		assertThat(items.getNumber("open")[1].intValue(), is(15007));
		assertThat(items.getNumber("high")[1].intValue(), is(15066));
		assertThat(items.getNumber("low")[1].intValue(), is(14714));
		assertThat(items.getNumber("close")[1].intValue(), is(14896));
		assertThat(items.getNumber("volume")[1].intValue(), is(346720500));

		assertNull(items.getNumber("sold")[0]);
		assertThat(items.getNumber("sold")[15].intValue(), is(200));
		assertNull(items.getNumber("bought")[15]);
		assertThat(items.getNumber("sold")[16].intValue(), is(200));
		assertNull(items.getNumber("bought")[16]);
		assertThat(items.getNumber("sold")[29].intValue(), is(500));
		assertThat(items.getNumber("bought")[29].intValue(), is(300));
		assertNull(items.getNumber("sold")[49]);
		assertNull(items.getNumber("bought")[49]);
	}

	@Test
	public void testCompressInt() throws ParseException {
		// Arrange
		final int length = dataset.getDate().length;
		final List<Price> priceList = new ArrayList<Price>(length);
		for (int i = 0; i < length; i++) {
			final Price data = new Price();
			data.setDate(dataset.getDate()[i]);
			data.setOpen(dataset.getOpen()[i]);
			data.setHigh(dataset.getHigh()[i]);
			data.setLow(dataset.getLow()[i]);
			data.setClose(dataset.getClose()[i]);
			data.setVolume(dataset.getVolume()[i]);
			priceList.add(data);
		}
		final DatasetSource items = AnnotationParser.parse(priceList);

		// Act
		items.compress(Calendar.DATE);

		// Assert
		final SimpleDateFormat ym = new SimpleDateFormat("yyyy/MM");
		assertThat(items.getDate("date")[0], is(ym.parse("1998/01")));
		assertThat(items.getNumber("open")[0].intValue(), is(15268));
		assertThat(items.getNumber("high")[0].intValue(), is(17352));
		assertThat(items.getNumber("low")[0].intValue(), is(14546));
		assertThat(items.getNumber("close")[0].intValue(), is(16628));
		assertThat(items.getNumber("volume")[0].longValue(), is(11177692700L));
		assertThat(items.getDate("date")[1], is(ym.parse("1998/02")));
		assertThat(items.getNumber("open")[1].intValue(), is(16685));
		assertThat(items.getNumber("high")[1].intValue(), is(17256));
		assertThat(items.getNumber("low")[1].intValue(), is(15932));
		assertThat(items.getNumber("close")[1].intValue(), is(16831));
		assertThat(items.getNumber("volume")[1].longValue(), is(9237806300L));
	}

	@Test
	public void testCompressIntCalendar() throws ParseException {
		// Arrange
		final int length = dataset.getDate().length;
		final List<Price> priceList = new ArrayList<Price>(length);
		for (int i = 0; i < length; i++) {
			final Price data = new Price();
			data.setDate(dataset.getDate()[i]);
			data.setOpen(dataset.getOpen()[i]);
			data.setHigh(dataset.getHigh()[i]);
			data.setLow(dataset.getLow()[i]);
			data.setClose(dataset.getClose()[i]);
			data.setVolume(dataset.getVolume()[i]);
			priceList.add(data);
		}
		final DatasetSource items = AnnotationParser.parse(priceList);

		// Act
		items.compress(Calendar.DATE, Calendar.getInstance());

		// Assert
		final SimpleDateFormat ym = new SimpleDateFormat("yyyy/MM");
		assertThat(items.getDate("date")[0], is(ym.parse("1998/01")));
		assertThat(items.getNumber("open")[0].intValue(), is(15268));
		assertThat(items.getNumber("high")[0].intValue(), is(17352));
		assertThat(items.getNumber("low")[0].intValue(), is(14546));
		assertThat(items.getNumber("close")[0].intValue(), is(16628));
		assertThat(items.getNumber("volume")[0].longValue(), is(11177692700L));
		assertThat(items.getDate("date")[1], is(ym.parse("1998/02")));
		assertThat(items.getNumber("open")[1].intValue(), is(16685));
		assertThat(items.getNumber("high")[1].intValue(), is(17256));
		assertThat(items.getNumber("low")[1].intValue(), is(15932));
		assertThat(items.getNumber("close")[1].intValue(), is(16831));
		assertThat(items.getNumber("volume")[1].longValue(), is(9237806300L));
	}

	@Test
	public void testCompressIntCalendarDateTruncater() throws ParseException {
		// Arrange
		final int length = dataset.getDate().length;
		final List<Price> priceList = new ArrayList<Price>(length);
		for (int i = 0; i < length; i++) {
			final Price data = new Price();
			data.setDate(dataset.getDate()[i]);
			data.setOpen(dataset.getOpen()[i]);
			data.setHigh(dataset.getHigh()[i]);
			data.setLow(dataset.getLow()[i]);
			data.setClose(dataset.getClose()[i]);
			data.setVolume(dataset.getVolume()[i]);
			priceList.add(data);
		}
		final DatasetSource items = AnnotationParser.parse(priceList);

		// Act
		items.compress(Calendar.DATE, Calendar.getInstance(), new DateTruncater());

		// Assert
		final SimpleDateFormat ym = new SimpleDateFormat("yyyy/MM");
		assertThat(items.getDate("date")[0], is(ym.parse("1998/01")));
		assertThat(items.getNumber("open")[0].intValue(), is(15268));
		assertThat(items.getNumber("high")[0].intValue(), is(17352));
		assertThat(items.getNumber("low")[0].intValue(), is(14546));
		assertThat(items.getNumber("close")[0].intValue(), is(16628));
		assertThat(items.getNumber("volume")[0].longValue(), is(11177692700L));
		assertThat(items.getDate("date")[1], is(ym.parse("1998/02")));
		assertThat(items.getNumber("open")[1].intValue(), is(16685));
		assertThat(items.getNumber("high")[1].intValue(), is(17256));
		assertThat(items.getNumber("low")[1].intValue(), is(15932));
		assertThat(items.getNumber("close")[1].intValue(), is(16831));
		assertThat(items.getNumber("volume")[1].longValue(), is(9237806300L));
	}
/*
	@Test
	public void testSplit() {
		// Arrange
		final int length = dataset.getLength();
		final List<Price> prices = new ArrayList<Price>(length);
		for (int i = 0; i < length; i++) {
			final Price price = new Price();
			price.setDate(dataset.getDate(i));
			price.setOpen(dataset.getOpen(i));
			price.setHigh(dataset.getHigh(i));
			price.setLow(dataset.getLow(i));
			price.setClose(dataset.getClose(i));
			price.setVolume(dataset.getVolume(i));
			prices.add(price);
		}
		final DatasetSource items = AnnotationParser.parse(prices);

		// Act
		items.split("");
	}
*/

/*
	@Test
	public void testNewDatasetBuilder() {
		assertNotNull(new DatasetItems().newDatasetBuilder(Object.class));
	}
*/
}
