/*************************************************************************
 *
 * Copyright 2009 by bBreak Systems.
 *
 * ExCella Reports - Excelファイルを利用した帳票ツール
 *
 * $Id: ReportProcessorTest.java 16 2009-06-24 03:19:20Z tomo-shibata $
 * $Revision: 16 $
 *
 * This file is part of ExCella Reports.
 *
 * ExCella Reports is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * ExCella Reports is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the COPYING.LESSER file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with ExCella Reports.  If not, see
 * <http://www.gnu.org/licenses/lgpl-3.0-standalone.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/
package org.bbreak.excella.reports.processor;

import static org.junit.Assert.*;

import java.io.FileNotFoundException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.bbreak.excella.core.BookData;
import org.bbreak.excella.core.SheetData;
import org.bbreak.excella.core.SheetParser;
import org.bbreak.excella.core.exception.ExportException;
import org.bbreak.excella.core.exception.ParseException;
import org.bbreak.excella.reports.ReportsTestUtil;
import org.bbreak.excella.reports.exporter.ReportBookExporter;
import org.bbreak.excella.reports.listener.ReportProcessAdaptor;
import org.bbreak.excella.reports.listener.ReportProcessListener;
import org.bbreak.excella.reports.model.ConvertConfiguration;
import org.bbreak.excella.reports.model.ParsedReportInfo;
import org.bbreak.excella.reports.model.ReportBook;
import org.bbreak.excella.reports.model.ReportSheet;
import org.bbreak.excella.reports.tag.ReportsTagParser;
import org.junit.Test;

/**
 * {@link org.bbreak.excella.reports.processor.ReportProcessor} のためのテスト・クラス。
 * 
 * @since 1.0
 */
@SuppressWarnings("unchecked")
public class ReportProcessorTest extends ReportsWorkbookTest {

    /**
     * システム変数：テンプディレクトリ
     */
    private String tmpDirPath = ReportsTestUtil.getTestOutputDir();

    /**
     * プロセスログ
     */
    private List<String> processStrings = new ArrayList<String>();

    /**
     * コンストラクタ
     * @param version バージョン
     */
    public ReportProcessorTest( String version) {
        super( version);
    }

    /**
     * {@link org.bbreak.excella.reports.processor.ReportProcessor#ReportProcessor()} のためのテスト・メソッド。
     */
    @Test
    public void testReportProcessor() {
        ReportProcessor processor = new ReportProcessor();
        // デフォルトタグパーサー
        Map<String, ReportsTagParser<?>> actualParsers = ( Map<String, ReportsTagParser<?>>) getPrivateFiled( processor, "parsers");
        Map<String, ReportsTagParser<?>> expectedParsers = ReportCreateHelper.createDefaultParsers();

        assertEquals( expectedParsers.size(), actualParsers.size());

        for ( Entry<String, ReportsTagParser<?>> entry : expectedParsers.entrySet()) {
            ReportsTagParser<?> actualParser = actualParsers.get( entry.getKey());
            assertNotNull( actualParser);
            assertEquals( entry.getValue().getClass(), actualParser.getClass());
        }

        // デフォルトエクスポーター
        Map<String, ReportBookExporter> actualExporters = ( Map<String, ReportBookExporter>) getPrivateFiled( processor, "exporters");
        Map<String, ReportBookExporter> expectedExporters = ReportCreateHelper.createDefaultExporters();

        assertEquals( expectedExporters.size(), actualExporters.size());

        for ( Entry<String, ReportBookExporter> entry : expectedExporters.entrySet()) {
            ReportBookExporter actualExporter = actualExporters.get( entry.getKey());
            assertNotNull( actualExporter);
            assertEquals( entry.getValue().getClass(), actualExporter.getClass());
        }

    }

    /**
     * {@link org.bbreak.excella.reports.processor.ReportProcessor#process(org.bbreak.excella.reports.model.ReportData)} のためのテスト・メソッド。
     */
    @Test
    public void testProcess() {

        getWorkbook();

        ReportProcessor reportProcessor = new ReportProcessor();
        reportProcessor.addReportProcessListener( new CustomListener());

        reportProcessor.addReportsTagParser( new CustomTagParser( "$CUSTOM"));
        ReportBookExporter exporter = new CustomExporter();
        reportProcessor.addReportBookExporter( exporter);
        reportProcessor.addReportBookExporter( new ThrowExceptionExporter());

        // ------------------------------------
        // テンプレートコピーのない実行処理確認
        // ------------------------------------
        processStrings.clear();

        ConvertConfiguration configuration = new ConvertConfiguration( "CUSTOM");
        ConvertConfiguration[] configurations = new ConvertConfiguration[] {configuration, null};

        // ブック１
        ReportBook book1 = new ReportBook( getFilepath(), tmpDirPath + "workbookout1", configurations);
        // →シート１
        ReportSheet sheet1 = new ReportSheet( "Sheet1");
        book1.addReportSheet( sheet1);
        // →シート２
        ReportSheet sheet2 = new ReportSheet( "Sheet2");
        book1.addReportSheet( sheet2);

        // ブック２
        ReportBook book2 = new ReportBook( getFilepath(), tmpDirPath + "workbookout2", configurations);
        // →シート３
        ReportSheet sheet3 = new ReportSheet( "Sheet3");
        book2.addReportSheet( sheet3);

        book2.addReportSheet( null);


        try {
            reportProcessor.process( new ReportBook[]{book1, book2, null});
        } catch ( Exception e) {
            e.printStackTrace();
            fail( e.toString());
        }

        List<String> exceptedProccess = new ArrayList<String>();
        exceptedProccess.add( "ブック解析前処理 CustomListener#preBookParse( Workbook workbook, ReportBook reportBook)");
        exceptedProccess.add( "シート解析前処理 CustomListener#preParse( Sheet sheet, SheetParser sheetParser)");
        exceptedProccess.add( "タグ解析処理 CustomTagParser#parse( Sheet sheet, Cell tagCell, Object data)");
        exceptedProccess.add( "シート解析後処理 CustomListener#postParse( Sheet sheet, SheetParser sheetParser, SheetData sheetData)");
        exceptedProccess.add( "シート解析前処理 CustomListener#preParse( Sheet sheet, SheetParser sheetParser)");
        exceptedProccess.add( "タグ解析処理 CustomTagParser#parse( Sheet sheet, Cell tagCell, Object data)");
        exceptedProccess.add( "シート解析後処理 CustomListener#postParse( Sheet sheet, SheetParser sheetParser, SheetData sheetData)");
        exceptedProccess.add( "出力前処理 CustomExporter#setup()");
        exceptedProccess.add( "出力処理 CustomExporter#output( Workbook book, BookData bookdata, ConvertConfiguration configuration)");
        exceptedProccess.add( "出力後処理 CustomExporter#tearDown()");
        exceptedProccess.add( "ブック解析後処理 CustomListener#postBookParse( Workbook workbook, ReportBook reportBook)");
        exceptedProccess.add( "ブック処理結果:シート1=Sheet1,シート2=Sheet2,シート3=Sheet3,シート4=-コメント,");
        exceptedProccess.add( "ブック解析前処理 CustomListener#preBookParse( Workbook workbook, ReportBook reportBook)");
        exceptedProccess.add( "シート解析前処理 CustomListener#preParse( Sheet sheet, SheetParser sheetParser)");
        exceptedProccess.add( "タグ解析処理 CustomTagParser#parse( Sheet sheet, Cell tagCell, Object data)");
        exceptedProccess.add( "シート解析後処理 CustomListener#postParse( Sheet sheet, SheetParser sheetParser, SheetData sheetData)");
        exceptedProccess.add( "出力前処理 CustomExporter#setup()");
        exceptedProccess.add( "出力処理 CustomExporter#output( Workbook book, BookData bookdata, ConvertConfiguration configuration)");
        exceptedProccess.add( "出力後処理 CustomExporter#tearDown()");
        exceptedProccess.add( "ブック解析後処理 CustomListener#postBookParse( Workbook workbook, ReportBook reportBook)");
        exceptedProccess.add( "ブック処理結果:シート1=Sheet1,シート2=Sheet2,シート3=Sheet3,シート4=-コメント,");

        assertArrayEquals( exceptedProccess.toArray(), processStrings.toArray());
        
        //制御行の確認
        
        
        
        

        // ------------------------------------
        // テンプレートコピーのある実行処理確認
        // ------------------------------------
        processStrings.clear();

        // ブック１
        book1 = new ReportBook( getFilepath(), tmpDirPath + "workbookout1", configurations);
//        book1.setCopyTemplate( true);

        // →シート１
        ReportSheet sheetC1 = new ReportSheet( "Sheet1", "CSheet1");
        book1.addReportSheet( sheetC1);
        // →シート２
        ReportSheet sheetC2 = new ReportSheet( "Sheet2", "CSheet2");
        book1.addReportSheet( sheetC2);

        // ブック２
        book2 = new ReportBook( getFilepath(), tmpDirPath + "workbookout2", configurations);
        // →シート３
        ReportSheet sheetC3 = new ReportSheet( "Sheet3");
        book2.addReportSheet( sheetC3);

        book2.addReportSheet( null);


        try {
            reportProcessor.process( book1, book2, null);
        } catch ( Exception e) {
            e.printStackTrace();
            fail( e.toString());
        }

        exceptedProccess = new ArrayList<String>();
        exceptedProccess.add( "ブック解析前処理 CustomListener#preBookParse( Workbook workbook, ReportBook reportBook)");
        exceptedProccess.add( "シート解析前処理 CustomListener#preParse( Sheet sheet, SheetParser sheetParser)");
        exceptedProccess.add( "タグ解析処理 CustomTagParser#parse( Sheet sheet, Cell tagCell, Object data)");
        exceptedProccess.add( "シート解析後処理 CustomListener#postParse( Sheet sheet, SheetParser sheetParser, SheetData sheetData)");
        exceptedProccess.add( "シート解析前処理 CustomListener#preParse( Sheet sheet, SheetParser sheetParser)");
        exceptedProccess.add( "タグ解析処理 CustomTagParser#parse( Sheet sheet, Cell tagCell, Object data)");
        exceptedProccess.add( "シート解析後処理 CustomListener#postParse( Sheet sheet, SheetParser sheetParser, SheetData sheetData)");
        exceptedProccess.add( "出力前処理 CustomExporter#setup()");
        exceptedProccess.add( "出力処理 CustomExporter#output( Workbook book, BookData bookdata, ConvertConfiguration configuration)");
        exceptedProccess.add( "出力後処理 CustomExporter#tearDown()");
        exceptedProccess.add( "ブック解析後処理 CustomListener#postBookParse( Workbook workbook, ReportBook reportBook)");
        exceptedProccess.add( "ブック処理結果:シート1=Sheet3,シート2=-コメント,シート3=CSheet1,シート4=CSheet2,");
        exceptedProccess.add( "ブック解析前処理 CustomListener#preBookParse( Workbook workbook, ReportBook reportBook)");
        exceptedProccess.add( "シート解析前処理 CustomListener#preParse( Sheet sheet, SheetParser sheetParser)");
        exceptedProccess.add( "タグ解析処理 CustomTagParser#parse( Sheet sheet, Cell tagCell, Object data)");
        exceptedProccess.add( "シート解析後処理 CustomListener#postParse( Sheet sheet, SheetParser sheetParser, SheetData sheetData)");
        exceptedProccess.add( "出力前処理 CustomExporter#setup()");
        exceptedProccess.add( "出力処理 CustomExporter#output( Workbook book, BookData bookdata, ConvertConfiguration configuration)");
        exceptedProccess.add( "出力後処理 CustomExporter#tearDown()");
        exceptedProccess.add( "ブック解析後処理 CustomListener#postBookParse( Workbook workbook, ReportBook reportBook)");
        exceptedProccess.add( "ブック処理結果:シート1=Sheet1,シート2=Sheet2,シート3=Sheet3,シート4=-コメント,");

        
        assertArrayEquals( exceptedProccess.toArray(), processStrings.toArray());
        
        

        // --------------------------------------------------------------------------
        // テンプレートファイルがない場合
        // --------------------------------------------------------------------------
        processStrings.clear();

        // ブック１
        book1 = new ReportBook("test", tmpDirPath + "workbookout1", configurations);
//        book1.setCopyTemplate( true);

        // →シート１
        sheetC1 = new ReportSheet( "Sheet1", "CSheet1");
        book1.addReportSheet( sheetC1);

        try {
            reportProcessor.process( book1);
            fail();
        } catch ( FileNotFoundException e) {
            assertTrue( true);
        } catch ( Exception e) {
            e.printStackTrace();
            fail( e.toString());
        }
        assertTrue( processStrings.isEmpty());

        // --------------------------------------------------------------------------
        // Exportの出力処理時にエラー発生の場合の出力後処理の動作確認
        // --------------------------------------------------------------------------
        // 出力時のエラー後、出力後処理
        processStrings.clear();

        ConvertConfiguration configuration2 = new ConvertConfiguration( "E");
        configurations = new ConvertConfiguration[] {configuration, configuration2};

        // ブック１
        book1 = new ReportBook(  getFilepath(), tmpDirPath + "workbookout1", configurations);
//        book1.setCopyTemplate( true);

        // →シート１
        sheetC1 = new ReportSheet( "Sheet1", "CSheet1");
        book1.addReportSheet( sheetC1);

        try {
            reportProcessor.process( book1);
            fail();
        } catch ( Exception e) {
            assertTrue( true);
        }

        exceptedProccess = new ArrayList<String>();
        exceptedProccess.add( "ブック解析前処理 CustomListener#preBookParse( Workbook workbook, ReportBook reportBook)");
        exceptedProccess.add( "シート解析前処理 CustomListener#preParse( Sheet sheet, SheetParser sheetParser)");
        exceptedProccess.add( "タグ解析処理 CustomTagParser#parse( Sheet sheet, Cell tagCell, Object data)");
        exceptedProccess.add( "シート解析後処理 CustomListener#postParse( Sheet sheet, SheetParser sheetParser, SheetData sheetData)");
        exceptedProccess.add( "出力前処理 CustomExporter#setup()");
        exceptedProccess.add( "出力処理 CustomExporter#output( Workbook book, BookData bookdata, ConvertConfiguration configuration)");
        exceptedProccess.add( "出力後処理 CustomExporter#tearDown()");
        exceptedProccess.add( "出力前処理 ThrowExceptionExporter#setup()");
        exceptedProccess.add( "出力処理 ThrowExceptionExporter#output( Workbook book, BookData bookdata, ConvertConfiguration configuration)");
        exceptedProccess.add( "出力後処理 ThrowExceptionExporter#tearDown()");
        exceptedProccess.add( "ブック解析後処理 CustomListener#postBookParse( Workbook workbook, ReportBook reportBook)");
        exceptedProccess.add( "ブック処理結果:シート1=Sheet2,シート2=Sheet3,シート3=-コメント,シート4=CSheet1,");

        assertArrayEquals( exceptedProccess.toArray(), processStrings.toArray());

    }

    /**
     * {@link org.bbreak.excella.reports.processor.ReportProcessor#addReportsTagParser(org.bbreak.excella.reports.tag.ReportsTagParser)} のためのテスト・メソッド。
     */
    @Test
    public void testAddReportsTagParser() {
        ReportProcessor processor = new ReportProcessor();

        ReportsTagParser<?> addparser = new CustomTagParser( "$CUSTOM");
        processor.addReportsTagParser( addparser);

        Map<String, ReportsTagParser<?>> actualParsers = ( Map<String, ReportsTagParser<?>>) getPrivateFiled( processor, "parsers");
        assertEquals( addparser, actualParsers.get( "$CUSTOM"));
    }

    /**
     * {@link org.bbreak.excella.reports.processor.ReportProcessor#removeReportsTagParser(java.lang.String)} のためのテスト・メソッド。
     */
    @Test
    public void testRemoveReportsTagParser() {

        ReportProcessor processor = new ReportProcessor();

        processor.removeReportsTagParser( "");
        Map<String, ReportsTagParser<?>> actualParsers = ( Map<String, ReportsTagParser<?>>) getPrivateFiled( processor, "parsers");

        assertNull( actualParsers.get( ""));
    }

    /**
     * {@link org.bbreak.excella.reports.processor.ReportProcessor#clearReportsTagParser()} のためのテスト・メソッド。
     */
    @Test
    public void testClearReportsTagParser() {
        ReportProcessor processor = new ReportProcessor();
        processor.clearReportsTagParser();
        Map<String, ReportsTagParser<?>> actualParsers = ( Map<String, ReportsTagParser<?>>) getPrivateFiled( processor, "parsers");

        assertTrue( actualParsers.isEmpty());
    }

    /**
     * {@link org.bbreak.excella.reports.processor.ReportProcessor#addReportBookExporter(org.bbreak.excella.reports.exporter.ReportBookExporter)} のためのテスト・メソッド。
     */
    @Test
    public void testAddReportBookExporter() {
        ReportProcessor processor = new ReportProcessor();

        ReportBookExporter bookExporter = new CustomExporter();
        processor.addReportBookExporter( bookExporter);

        Map<String, ReportBookExporter> actualExporters = ( Map<String, ReportBookExporter>) getPrivateFiled( processor, "exporters");

        assertEquals( bookExporter, actualExporters.get( "CUSTOM"));
    }

    /**
     * {@link org.bbreak.excella.reports.processor.ReportProcessor#removeReportBookExporter(java.lang.String)} のためのテスト・メソッド。
     */
    @Test
    public void testRemoveReportBookExporter() {
        ReportProcessor processor = new ReportProcessor();

        processor.removeReportBookExporter( "EXCEL");

        Map<String, ReportBookExporter> actualExporters = ( Map<String, ReportBookExporter>) getPrivateFiled( processor, "exporters");

        assertNull( actualExporters.get( "EXCEL"));
    }

    /**
     * {@link org.bbreak.excella.reports.processor.ReportProcessor#clearReportBookExporter()} のためのテスト・メソッド。
     */
    @Test
    public void testClearReportBookExporter() {
        ReportProcessor processor = new ReportProcessor();

        processor.clearReportBookExporter();

        Map<String, ReportBookExporter> actualExporters = ( Map<String, ReportBookExporter>) getPrivateFiled( processor, "exporters");

        assertTrue( actualExporters.isEmpty());
    }

    /**
     * {@link org.bbreak.excella.reports.processor.ReportProcessor#addReportProcessListener(org.bbreak.excella.reports.listener.ReportProcessListener)} のためのテスト・メソッド。
     */
    @Test
    public void testAddReportProcessListener() {
        ReportProcessor processor = new ReportProcessor();
        CustomListener listener = new CustomListener();

        List<ReportProcessListener> listenerList = ( List<ReportProcessListener>) getPrivateFiled( processor, "listeners");

        assertTrue( listenerList.isEmpty());

        processor.addReportProcessListener( listener);

        assertEquals( 1, listenerList.size());
        assertEquals( listener, listenerList.iterator().next());

    }

    /**
     * {@link org.bbreak.excella.reports.processor.ReportProcessor#removeReportProcessListener(org.bbreak.excella.reports.listener.ReportProcessListener)} のためのテスト・メソッド。
     */
    @Test
    public void testRemoveReportProcessListener() {

        ReportProcessor processor = new ReportProcessor();
        CustomListener listener = new CustomListener();
        processor.addReportProcessListener( listener);

        List<ReportProcessListener> listenerList = ( List<ReportProcessListener>) getPrivateFiled( processor, "listeners");

        assertEquals( 1, listenerList.size());

        processor.removeReportProcessListener( listener);

        assertTrue( listenerList.isEmpty());

    }

    private Object getPrivateFiled( Object object, String fieldName) {

        Field field = null;
        Object res = null;
        try {
            field = object.getClass().getDeclaredField( fieldName);
            field.setAccessible( true);
            res = field.get( object);
        } catch ( Exception e) {
            e.printStackTrace();
            fail();
        }
        return res;

    }

    class CustomTagParser extends ReportsTagParser<Object> {

        public CustomTagParser( String tag) {
            super( tag);
        }

        @Override
        public ParsedReportInfo parse( Sheet sheet, Cell tagCell, Object data) throws ParseException {
            processStrings.add( "タグ解析処理 CustomTagParser#parse( Sheet sheet, Cell tagCell, Object data)");
            return null;
        }

        @Override
        public boolean useControlRow() {
            return false;
        }

    }

    class CustomExporter extends ReportBookExporter {

        @Override
        public String getExtention() {
            return ".custom";
        }

        @Override
        public String getFormatType() {
            return "CUSTOM";
        }

        @Override
        public void output( Workbook book, BookData bookdata, ConvertConfiguration configuration) throws ExportException {
            processStrings.add( "出力処理 CustomExporter#output( Workbook book, BookData bookdata, ConvertConfiguration configuration)");
        }

        @Override
        public void setup() {
            processStrings.add( "出力前処理 CustomExporter#setup()");
            super.setup();
        }

        @Override
        public void tearDown() {
            processStrings.add( "出力後処理 CustomExporter#tearDown()");
            super.tearDown();
        }

    }

    class ThrowExceptionExporter extends ReportBookExporter {

        @Override
        public String getExtention() {
            return ".e";
        }

        @Override
        public String getFormatType() {
            return "E";
        }

        @Override
        public void output( Workbook book, BookData bookdata, ConvertConfiguration configuration) throws ExportException {
            processStrings.add( "出力処理 ThrowExceptionExporter#output( Workbook book, BookData bookdata, ConvertConfiguration configuration)");
            throw new ExportException( new Exception());
        }

        @Override
        public void setup() {
            processStrings.add( "出力前処理 ThrowExceptionExporter#setup()");
            super.setup();
        }

        @Override
        public void tearDown() {
            processStrings.add( "出力後処理 ThrowExceptionExporter#tearDown()");
            super.tearDown();
        }

    }

    class CustomListener extends ReportProcessAdaptor {

        @Override
        public void postBookParse( Workbook workbook, ReportBook reportBook) {
            processStrings.add( "ブック解析後処理 CustomListener#postBookParse( Workbook workbook, ReportBook reportBook)");

            StringBuffer buffer = new StringBuffer( "ブック処理結果:");
            for ( int i = 0; i < workbook.getNumberOfSheets(); i++) {
                buffer.append( "シート" + (i + 1) + "=" + workbook.getSheetName( i) + ",");
            }
            processStrings.add( buffer.toString());
            System.out.println( buffer.toString());

            // System.out.println("workbook=" + workbook + ",reportBook=" + reportBook);
            super.postBookParse( workbook, reportBook);
        }

        @Override
        public void postParse( Sheet sheet, SheetParser sheetParser, SheetData sheetData) throws ParseException {
            processStrings.add( "シート解析後処理 CustomListener#postParse( Sheet sheet, SheetParser sheetParser, SheetData sheetData)");
            // System.out.println("sheet=" + sheet + ",sheetParser=" + sheetParser+ ",sheetData=" + sheetData);
            super.postParse( sheet, sheetParser, sheetData);
        }

        @Override
        public void preBookParse( Workbook workbook, ReportBook reportBook) {
            processStrings.add( "ブック解析前処理 CustomListener#preBookParse( Workbook workbook, ReportBook reportBook)");
            // System.out.println("workbook=" + workbook +",reportBook=" + reportBook);
            super.preBookParse( workbook, reportBook);
        }

        @Override
        public void preParse( Sheet sheet, SheetParser sheetParser) throws ParseException {
            processStrings.add( "シート解析前処理 CustomListener#preParse( Sheet sheet, SheetParser sheetParser)");
            // System.out.println("sheet=" + sheet +",SheetParser=" + sheetParser);
            super.preParse( sheet, sheetParser);
        }

    }

}
