package org.maachang.report ;

import java.io.IOException;
import java.util.Iterator;

/**
 * 帳票コアオブジェクト.
 * 
 * @version 2008/09/29
 * @author masahito suzuki
 * @since MaachangReport 1.00
 */
class ReportCore {
    
    /**
     * 帳票要素.
     */
    private ExcelSheet element = null ;
    
    /**
     * セルスタイルリスト.
     */
    private CellStyleList csList = null ;
    
    /**
     * セルポジションリスト.
     */
    private CellPositionList cpList = null ;
    
    /**
     * オブジェクトフィックスフラグ.
     */
    private volatile boolean fixFlag = false ;
    
    /**
     * コンストラクタ.
     */
    private ReportCore() {
        
    }
    
    /**
     * コンストラクタ.
     * @param name Excelファイル名を設定します.
     * @param sheetName 利用対象のExcelシート名を設定します.
     * @exception Exception 例外.
     */
    public ReportCore( String name,String sheetName ) throws Exception {
        this.element = new ExcelSheet( name,sheetName ) ;
        this.csList = new CellStyleList() ;
        this.cpList = null ;
    }
    
    protected void finalize() throws Exception {
        destroy() ;
    }
    
    /**
     * オブジェクトを破棄.
     */
    public void destroy() {
        if( element != null ) {
            element.destroy() ;
        }
        element = null ;
        csList = null ;
        cpList = null ;
        fixFlag = true ;
    }
    
    /**
     * オブジェクト有効チェック.
     * @return boolean [true]の場合、有効です.
     */
    public boolean isUse() {
        return ( element != null && element.isUse() ) ;
    }
    
    /**
     * オブジェクトフィックスチェック.
     * @return boolean [true]の場合、オブジェクトはフィックスしています.
     */
    public boolean isFix() {
        return fixFlag ;
    }
    
    /**
     * 帳票要素を取得.
     * @return ExcelSheet 帳票要素が返されます.
     * @exception Exception 例外.
     */
    public ExcelSheet getExcelSheet()
        throws Exception {
        if( isUse() == false ) {
            throw new IOException( "オブジェクトは既に破棄されています" ) ;
        }
        return element ;
    }
    
    /**
     * セルスタイルリストを取得.
     * @return CellStyleList セルスタイルリストが返されます.
     * @exception Exception 例外.
     */
    public CellStyleList getCellStyleList()
        throws Exception {
        if( isUse() == false ) {
            throw new IOException( "オブジェクトは既に破棄されています" ) ;
        }
        return csList ;
    }
    
    /**
     * セルポジションリストを取得.
     * @return CellStyleList セルポジションリストが返されます.
     * @exception Exception 例外.
     */
    public CellPositionList getCellPositionList()
        throws Exception {
        if( isUse() == false ) {
            throw new IOException( "オブジェクトは既に破棄されています" ) ;
        }
        return cpList ;
    }
    
    /**
     * オブジェクトフィックス.
     * <p>セルスタイル群の設定が完了した後に呼び出します.</p>
     * <p>この処理により、読み込まれたExcel内のデータ割り当て変数名を取得します.</p>
     * <p>また、この処理を呼び出した後、セルスタイル群の変更や修正はできません.</p>
     * @exception Exception 例外.
     */
    public void fix() throws Exception {
        if( isUse() == false ) {
            throw new IOException( "オブジェクトは既に破棄されています" ) ;
        }
        if( fixFlag ) {
            //throw new IOException( "既にオブジェクトはフィックスしています" ) ;
            return ;
        }
        cpList = element.getCellPositionList( csList ) ;
        if( cpList.size() <= 0 ) {
            throw new IOException( "データ設定対象の変数情報が存在しないので、処理を中断します" ) ;
        }
        fixFlag = true ;
    }
    
    /**
     * セルスタイルを追加.
     * @param name セルスタイル名を設定します.
     * @param startXPos コピー開始位置Xを設定します.
     * @param startYPos コピー開始位置Yを設定します.
     * @param width Width値を設定します.
     * @param height Height値を設定します.
     * @exception Exception 例外.
     */
    public void addCellStyle( String name,int startXPos,int startYPos,int width,int height )
        throws Exception {
        if( isUse() == false ) {
            throw new IOException( "オブジェクトは既に破棄されています" ) ;
        }
        if( fixFlag ) {
            throw new IOException( "既にオブジェクトはフィックスしています" ) ;
        }
        CellStyle cs = new CellStyle( name,element.getSheet(),startXPos,startYPos,width,height ) ;
        csList.put( cs ) ;
    }
    
    /**
     * セルスタイルを追加.
     * @param cs 対象のセルスタイルを設定します.
     * @exception Exception 例外.
     */
    public void addCellStyle( CellStyle cs ) throws Exception {
        if( isUse() == false ) {
            throw new IOException( "オブジェクトは既に破棄されています" ) ;
        }
        if( fixFlag ) {
            throw new IOException( "既にオブジェクトはフィックスしています" ) ;
        }
        if( cs == null || cs.isUse() == false ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        csList.put( cs ) ;
    }
    
    /** 指定セルスタイルより下に存在する、変数位置をセルスタイルHeight分移動させる **/
    private void moveUnderCellStyle( CellStyle cs ) {
        int addHeight = cs.getHeight() ;
        // 変数位置を移動.
        Iterator it = cpList.iterator() ;
        while( it.hasNext() ) {
            CellPosition[] pos = cpList.get( ( String )it.next() ) ;
            int len = pos.length ;
            for( int i = 0 ; i < len ; i ++ ) {
                if( cs.isUnderRow( pos[ i ].getY() ) ) {
                    pos[ i ].setY( pos[ i ].getY() + addHeight ) ;
                }
            }
        }
        // セルスタイル位置を移動.
        it = csList.iterator() ;
        while( it.hasNext() ) {
            CellStyle target = csList.get( ( String )it.next() ) ;
            if( target != cs && cs.isUnderRow( target.getStartY() ) ) {
                target.addStartY( addHeight ) ;
            }
        }
    }
    
    /** 現在のセルスタイルに属する変数位置を、セルスタイルHeight分移動させる **/
    private void moveThisCellStyle( CellStyle cs ) {
        int addHeight = cs.getHeight() ;
        String csName = cs.getName() ;
        Iterator it = cpList.iterator() ;
        while( it.hasNext() ) {
            CellPosition[] pos = cpList.get( ( String )it.next() ) ;
            int len = pos.length ;
            for( int i = 0 ; i < len ; i ++ ) {
                if( csName.equals( pos[ i ].getCellStyleName() ) ) {
                    pos[ i ].setY( pos[ i ].getY() + addHeight ) ;
                }
            }
        }
    }
    
    /**
     * 指定セルスタイル内容を１インクリメント.
     * @param name 追加対象のセルスタイル名を設定します.
     * @exception Exception 例外.
     */
    public void insertCellStyle( String name )
        throws Exception {
        if( isUse() == false ) {
            throw new IOException( "オブジェクトは既に破棄されています" ) ;
        }
        if( fixFlag == false ) {
            throw new IOException( "この処理はオブジェクトがフィックスしないと、利用できません" ) ;
        }
        if( name == null || ( name = name.trim() ).length() <= 0 ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        CellStyle cs = csList.get( name ) ;
        if( cs == null ) {
            throw new IllegalArgumentException( "指定名[" + name + "]のセルスタイルは存在しません" ) ;
        }
        // 指定セルスタイル以下の座標を持つ変数位置を移動させる.
        moveUnderCellStyle( cs ) ;
        // 指定セルスタイル以下の内容を、セルスタイルHeight分挿入.
        element.pushRow( cs.getNowRow(),cs.getHeight() ) ;
        // 対象位置に対して、セルスタイルをコピー.
        cs.copy( element.getSheet(),cs.getStartX(),cs.getNowRow() ) ;
        // セルスタイルに属する変数位置を移動させる.
        moveThisCellStyle( cs ) ;
        // セルスタイルの現在位置を、１インクリメント.
        cs.addNowRow() ;
    }
    
    /**
     * 指定セルスタイル範囲内のシーケンス条件に対して、シーケンスIDを付加.
     * @param name セルスタイル名を設定します.
     * @exception Exception 例外.
     */
    public void pushSequence( String name )
        throws Exception {
        if( isUse() == false ) {
            throw new IOException( "オブジェクトは既に破棄されています" ) ;
        }
        if( fixFlag == false ) {
            throw new IOException( "この処理はオブジェクトがフィックスしないと、利用できません" ) ;
        }
        if( name == null || ( name = name.trim() ).length() <= 0 ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        CellStyle cs = csList.get( name ) ;
        if( cs == null ) {
            throw new IllegalArgumentException( "指定名[" + name + "]のセルスタイルは存在しません" ) ;
        }
        // ${seq:*}変数が存在する内容に対して、シーケンスIDを付加.
        CellPosition p = cpList.getSequence( name ) ;
        if( p != null ) {
            element.setCell( p.getX(),p.getY(),new Integer( cs.getNowPos()+1 ) ) ;
        }
    }
}

