package org.maachang.report ;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.maachang.conf.Config;
import org.maachang.conf.ConvIniParam;
import org.maachang.util.StringUtil;

/**
 * 帳票エンジン.
 * 
 * @version 2008/10/01
 * @author masahito suzuki
 * @since MaachangReport 1.00
 */
public class ReportEngine {
    
    /**
     * 帳票コアオブジェクト.
     */
    private ReportCore core = null ;
    
    /**
     * セルスタイル挿入管理用Set.
     */
    private Set<String> csMan = null ;
    
    /**
     * 出力ファイル名.
     */
    private String outName = null ;
    
    /**
     * コンストラクタ.
     */
    private ReportEngine() {
        
    }
    
    /**
     * コンストラクタ.
     * @param name 対象のコンフィグファイル名を設定します.
     * @exception Exception 例外.
     */
    public ReportEngine( String name ) throws Exception {
        this( name,Config.read( name ) ) ;
    }
    
    /**
     * コンストラクタ.
     * @param name オープン対象のファイル名を設定します.
     * @param conf 対象のコンフィグ情報を設定します.
     * @exception Exception 例外.
     */
    public ReportEngine( String name,Config conf ) throws Exception {
        if( name == null || ( name = name.trim() ).length() <= 0 || conf == null ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        String excelFile = conf.get( "excel","name",0 ) ;
        String sheetName = conf.get( "excel","sheet",0 ) ;
        String outName = conf.get( "excel","outName",0 ) ;
        if( excelFile == null || ( excelFile = excelFile.trim() ).length() <= 0 ) {
            if( name.endsWith( ".conf" ) ) {
                excelFile = name.substring( 0,name.length()-".conf".length() ) + ".xls" ;
            }
            else {
                excelFile = null ;
            }
        }
        CellStyleBean[] cbList = null ;
        String[] keys = conf.getKeys( "cellStyle" ) ;
        if( keys != null && keys.length > 0 ) {
            ArrayList<CellStyleBean> lst = new ArrayList<CellStyleBean>() ;
            int len = keys.length ;
            for( int i = 0 ; i < len ; i ++ ) {
                String v = conf.get( "cellStyle",keys[ i ],0 ) ;
                if( v != null && v.length() > 0 ) {
                    ArrayList<String> csv = StringUtil.cutString( v,"," ) ;
                    if( csv.size() != 4 ) {
                        continue ;
                    }
                    if( StringUtil.checkType( StringUtil.CHECK_NUMBER,csv.get( 1 ) ) == false ||
                        StringUtil.checkType( StringUtil.CHECK_NUMBER,csv.get( 2 ) ) == false ||
                        StringUtil.checkType( StringUtil.CHECK_NUMBER,csv.get( 3 ) ) == false ) {
                        continue ;
                    }
                    if( StringUtil.checkType( StringUtil.CHECK_ALPHABET,csv.get( 0 ) ) ) {
                        CellStyleBean bean = new CellStyleBean( keys[ i ],
                            csv.get( 0 ),
                            ConvIniParam.getInt( csv.get( 1 ) ),
                            ConvIniParam.getInt( csv.get( 2 ) ),
                            ConvIniParam.getInt( csv.get( 3 ) ) ) ;
                        lst.add( bean ) ;
                    }
                    else if( StringUtil.checkType( StringUtil.CHECK_NUMBER,csv.get( 0 ) ) ) {
                        CellStyleBean bean = new CellStyleBean( keys[ i ],
                            ConvIniParam.getInt( csv.get( 0 ) ),
                            ConvIniParam.getInt( csv.get( 1 ) ),
                            ConvIniParam.getInt( csv.get( 2 ) ),
                            ConvIniParam.getInt( csv.get( 3 ) ) ) ;
                        lst.add( bean ) ;
                    }
                    else {
                        continue ;
                    }
                }
            }
            if( lst.size() != 0 ) {
                len = lst.size() ;
                cbList = new CellStyleBean[ len ] ;
                for( int i = 0 ; i < len ; i ++ ) {
                    cbList[ i ] = lst.get( i ) ;
                }
                lst.clear() ;
            }
            lst = null ;
        }
        this.createEngine( excelFile,sheetName,outName,cbList ) ;
    }
    
    /**
     * コンストラクタ.
     * @param excelFile テンプレート対象のExcelファイル名を設定します.
     * @param sheetName 対象のExcelシート名を設定します.
     * @param outName 対象の出力名を設定します.
     * @param cbList セルスタイルBean群を設定します.
     * @exception Exception 例外.
     */
    public ReportEngine( String excelFile,String sheetName,String outName,CellStyleBean[] cbList ) throws Exception {
        this.createEngine( excelFile,sheetName,outName,cbList ) ;
    }
    
    /** エンジン生成. **/
    private void createEngine( String excelFile,String sheetName,String outName,CellStyleBean[] cbList ) throws Exception {
        if( excelFile == null || ( excelFile = excelFile.trim() ).length() <= 0 ) {
            throw new IllegalArgumentException( "テンプレート対象のExcelファイル名は不正です" ) ;
        }
        if( sheetName == null || ( sheetName = sheetName.trim() ).length() <= 0 ) {
            sheetName = "Sheet1" ;
        }
        if( outName == null || ( outName = outName.trim() ).length() <= 0 ) {
            outName = "report.xls" ;
        }
        ReportCore pc = new ReportCore( excelFile,sheetName ) ;
        HSSFSheet hs = pc.getExcelSheet().getSheet() ;
        int len = 0 ;
        if( cbList != null ) {
            len = cbList.length ;
        }
        for( int i = 0 ; i < len ; i ++ ) {
            pc.addCellStyle( new CellStyle( cbList[ i ],hs ) ) ;
        }
        pc.fix() ;
        this.core = pc ;
        this.csMan = new HashSet<String>() ;
        this.outName = outName ;
    }
    
    /**
     * デストラクタ.
     */
    protected void finalize() throws Exception {
        destroy() ;
    }
    
    /**
     * オブジェクトを破棄.
     */
    public void destroy() {
        if( core != null ) {
            core.destroy() ;
        }
        core = null ;
        csMan = null ;
        outName = null ;
    }
    
    /**
     * 新しいリストを作成.
     * <p>※この処理は「セルスタイル」を定義していないと、意味を持ちません.</p>
     * <p>利用方法として、リスト条件に情報をセットした後、次のリストに対して
     * 情報をセットする時に、この処理を呼び出します.</p>
     * @exception Exception 例外.
     */
    public void nextList() throws Exception {
        Iterator it = csMan.iterator() ;
        while( it.hasNext() ) {
            String name = ( String )it.next() ;
            CellStyle cs = core.getCellStyleList().get( name ) ;
            if( cs != null ) {
                cs.insert() ;
            }
        }
        csMan.clear() ;
    }
    
    /**
     * 変数に対して、情報を追加.
     * @param name 設定名を設定します.
     * @param value 設定オブジェクトを設定します.
     * @exception Exception 例外.
     */
    public void putValue( String name,Object value )
        throws Exception {
        if( isUse() == false ) {
            throw new IOException( "オブジェクトは既に破棄されています" ) ;
        }
        if( core.isFix() == false ) {
            throw new IOException( "この処理はオブジェクトがフィックスしないと、利用できません" ) ;
        }
        if( name == null || ( name = name.trim() ).length() <= 0 ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( name.startsWith( "seq:" ) ) {
            // シーケンス項番の場合は、無視.
            return ;
        }
        if( value == null ) {
            value = "" ;
        }
        CellPositionList cpList = core.getCellPositionList() ;
        ExcelSheet element = core.getExcelSheet() ;
        CellPosition[] cp = cpList.get( name ) ;
        if( cp == null ) {
            throw new IllegalArgumentException( "指定名[" + name + "]の要素は存在しません" ) ;
        }
        int len = cp.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            if( cp[ i ].getCellStyleName() != null ) {
                String csName = cp[ i ].getCellStyleName() ;
                if( csName != null && csMan.contains( csName ) == false ) {
                    CellStyle cs = core.getCellStyleList().get( csName ) ;
                    if( cs != null && cs.isInsert() ) {
                        core.insertCellStyle( csName ) ;
                    }
                    core.pushSequence( csName ) ;
                    csMan.add( csName ) ;
                }
            }
            element.setCell( cp[ i ].getX(),cp[ i ].getY(),value ) ;
        }
    }
    
    /**
     * 指定名が利用可能かチェック.
     * @param name 対象の名前を設定します.
     * @return boolean [true]の場合、利用可能です.
     * @exception Exception 例外.
     */
    public boolean containsKey( String name )
        throws Exception {
        if( isUse() == false ) {
            throw new IOException( "オブジェクトは既に破棄されています" ) ;
        }
        if( core.isFix() == false ) {
            throw new IOException( "この処理はオブジェクトがフィックスしないと、利用できません" ) ;
        }
        return ( core.getCellPositionList().get( name ) != null ) ;
    }
    
    /**
     * 処理結果のExcel情報をバイナリで取得.
     * @return byte[] 処理結果のExcel情報が返されます.
     * @exception Exception 例外.
     */
    public byte[] getBytes() throws Exception {
        if( isUse() == false ) {
            throw new IOException( "オブジェクトは既に破棄されています" ) ;
        }
        return core.getExcelSheet().getBytes() ;
    }
    
    /**
     * 処理結果のExcel情報をファイル出力.
     * @param name 出力先のファイル名を設定します.
     * @exception Exception 例外.
     */
    public void output( String name ) throws Exception {
        if( isUse() == false ) {
            throw new IOException( "オブジェクトは既に破棄されています" ) ;
        }
        core.getExcelSheet().output( name ) ;
    }
    
    /**
     * ReportElementオブジェクトを取得.
     * <p>このメソッドは、オープン中のExcelSheetを直接操作したい場合に利用します.</p>
     * @return ReportElement ReportElementオブジェクトが返されます.
     * @exception Exception 例外.
     */
    public ReportElement getElement()
        throws Exception {
        if( isUse() == false ) {
            throw new IOException( "オブジェクトは既に破棄されています" ) ;
        }
        return new ReportElement( core.getExcelSheet() ) ;
    }
    
    /**
     * 出力名を取得.
     * @return 出力名が返されます.
     */
    public String getOutName() {
        return outName ;
    }
    
    /**
     * オブジェクトが有効かチェック.
     * @return boolean [true]の場合、有効です.
     */
    public boolean isUse() {
        return ( core != null ) ;
    }
}

