package org.maachang.comet.httpd.engine.script;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.script.ScriptContext;

import org.maachang.comet.conf.ServiceDef;
import org.maachang.comet.httpd.engine.script.dao.Dao;
import org.maachang.comet.httpd.engine.script.dao.DaoFactory;
import org.maachang.comet.httpd.engine.script.dao.MetaJson;
import org.maachang.comet.httpd.engine.script.image.ImageManager;
import org.maachang.comet.httpd.engine.script.image.ImageOp;
import org.maachang.comet.mdbm.SingleMDbm;
import org.maachang.dao.ExecutionDao;
import org.maachang.dao.MaachangDaoException;
import org.maachang.dao.dbms.Record;
import org.maachang.manager.GlobalManager;

/**
 * モデル実行処理格納用.
 * 
 * @version 2007/08/27
 * @author masahito suzuki
 * @since MaachangComet 1.00
 */
public class BaseModelImpl implements BaseModel {
    
    /**
     * SQL実行処理用.
     */
    private Record record = null ;
    
    /**
     * スクリプトコンテキスト.
     */
    private ScriptContext context = null ;
    
    /**
     * イメージ操作管理.
     */
    private ImageOp imageOp = null ;
    
    /**
     * 生成チェック用.
     */
    private boolean createFlag = false ;
    
    /**
     * コンストラクタ.
     */
    public BaseModelImpl() {
        
    }
    
    /**
     * デストラクタ.
     */
    protected void finalize() throws Exception {
        this.clear() ;
    }
    
    /**
     * 生成処理.
     * <BR><BR>
     * 条件を設定して生成します.
     * <BR>
     * @param mode [true]の場合、コンソール以外の実行モードです.
     * @param context 対象のスクリプトコンテキストを設定します.
     * @param record 対象のレコードオブジェクトを設定します.
     * @exception Exception 例外.
     */
    public synchronized void create( boolean mode,ScriptContext context,Record record )
        throws Exception {
        if( record == null ) {
            throw new IOException( "Recordは不正です" ) ;
        }
        this.record = record ;
        this.context = context ;
        if( mode == true ) {
            SingleMDbm.getInstance().createMDbm( false,record.getConnection().getAutoCommit() == false ) ;
        }
        else {
            SingleMDbm.getInstance().createMDbm( true,false ) ;
        }
        this.imageOp = ImageManager.getImageOp( SingleMDbm.getInstance().getMDbm() ) ;
        this.createFlag = true ;
    }
    
    /**
     * クリア処理.
     */
    public void clear() {
        if( imageOp != null ) {
            try {
                imageOp.commit() ;
            } catch( Exception e ) {
            }
        }
        if( record != null ) {
            try {
                record.commit() ;
            } catch( Exception e ) {
            }
            try {
                record.close() ;
            } catch( Exception e ) {
            }
        }
        record = null ;
        context = null ;
        imageOp = null ;
        createFlag = false ;
    }
    
    /**
     * コミット処理.
     * <BR><BR>
     * コミット処理を実施します.
     * <BR>
     * @exception Exception 例外.
     */
    public void commit() throws Exception {
        if( createFlag == true ) {
            checkCommit() ;
            if( imageOp != null ) {
                imageOp.commit() ;
            }
           record.commit() ;
        }
    }
    
    /**
     * ロールバック処理.
     * <BR><BR>
     * ロールバック処理を実施します.
     * <BR>
     * @exception Exception 例外.
     */
    public void rollback() throws Exception {
        if( createFlag == true ) {
            if( imageOp != null ) {
                try {
                    imageOp.rollback() ;
                } catch( Exception e ) {
                }
            }
            if( record != null ) {
                record.rollback() ;
            }
        }
    }
    
    /**
     * 生成チェック.
     * <BR><BR>
     * 生成チェック処理を行います.
     * <BR>
     * @return boolean [true]の場合生成されています.
     */
    public synchronized boolean isCreate() {
        return createFlag ;
    }
    
    /**
     * オブジェクトタイプを取得.
     * <BR><BR>
     * オブジェクトタイプを取得します.
     * <BR>
     * @return boolean [true]の場合、実行可能オブジェクトです.
     */
    public boolean isType() {
        return true ;
    }
    
    /**
     * スクリプトコンテキストを取得.
     * <BR><BR>
     * スクリプトコンテキストを取得します.
     * <BR>
     * @return ScriptContext 対象のスクリプトコンテキストが返されます.
     */
    public ScriptContext getScriptContext() {
        return context ;
    }
    
    /**
     * イメージ操作オブジェクトを取得.
     * <BR><BR>
     * イメージ操作オブジェクトを取得します.
     * <BR>
     * @return ImageOp イメージ操作オブジェクトが返されます.
     */
    public ImageOp getImageOp() {
        return imageOp ;
    }
    
    /**
     * コネクションオブジェクトを取得.
     * <BR><BR>
     * コネクションオブジェクトを取得します.
     * <BR>
     * @return Record コネクションオブジェクトが返されます.
     */
    public Record getRecord() {
        return record ;
    }
    
    /**
     * 指定モデル名から、メタデータを取得.
     * <BR><BR>
     * 指定モデル名から、メタデータを取得します.
     * <BR>
     * @param model 対象のモデル名を設定します.
     * @return MetaJson 対象のメタデータが返されます.
     * @exception Exception 例外.
     */
    public MetaJson getMeta( String model )
        throws Exception {
        checkCreate() ;
        DaoFactory factory = getDaoFactory() ;
        Dao dao = factory.getDao( record,model ) ;
        if( dao == null ) {
            throw new MaachangDaoException( "対象のModel情報[" + model + "]は存在しません" ) ;
        }
        return dao.getMeta() ;
    }
    
    /**
     * メタデータをJSON形式に変換.
     * <BR><BR>
     * メタデータをJSON形式に変換します.
     * <BR>
     * @param model 対象のモデル名を設定します.
     * @return HashMap<String,Object> JSON形式のメタデータが返されます.
     * @exception Exception 例外.
     */
    public HashMap<String,Object> getMetaByJson( String model )
        throws Exception {
        checkCreate() ;
        MetaJson meta = getMeta( model ) ;
        return meta.getJson() ;
    }
    
    /**
     * データを保存.
     * <BR><BR>
     * スクリプトから渡されたデータを保存します.
     * <BR>
     * @param model 対象のモデル名を設定します.
     * @param args 対象のデータを設定します.
     * @return Long 更新されたIDが返されます.
     * @exception Exception 例外.
     */
    public Long save( String model,HashMap<String,Object> args )
        throws Exception {
        checkCreate() ;
        DaoFactory factory = getDaoFactory() ;
        Dao dao = factory.getDao( record,model ) ;
        if( dao.save( record,args ) == false ) {
            return new Long( -1L ) ;
        }
        Object ret = args.get( "id" ) ;
        if( ret != null && ret instanceof String ) {
            ret = new Long( ( String )ret ) ;
        }
        return ( Long )ret ;
    }
    
    /**
     * データをアップデート.
     * <BR><BR>
     * スクリプトから渡されたデータを更新します.
     * <BR>
     * @param model 対象のモデル名を設定します.
     * @param where 対象の条件を設定します.
     * @param args 対象のデータを設定します.
     * @exception Exception 例外.
     */
    public void update( String model,String where,ArrayList<Object> args )
        throws Exception {
        checkCreate() ;
        DaoFactory factory = getDaoFactory() ;
        Dao dao = factory.getDao( record,model ) ;
        dao.update( record,where,args ) ;
    }
    
    /**
     * データを削除.
     * <BR><BR>
     * スクリプトから渡されたデータを削除します.
     * <BR>
     * @param model 対象のモデル名を設定します.
     * @param im イメージマネージャを設定します.
     * @param delJoins 削除Joing群を設定します.
     * @param srcJoin 連結テーブル内容を設定します.
     * @param where 対象の条件を設定します.
     * @param args 対象のデータを設定します.
     * @exception Exception 例外.
     */
    public void remove( String model,ImageOp im,ArrayList<String> delJoins,
        Map<String,List<String>> srcJoin,String where,ArrayList<Object> args )
        throws Exception {
        checkCreate() ;
        DaoFactory factory = getDaoFactory() ;
        Dao dao = factory.getDao( record,model ) ;
        dao.remove( record,im,delJoins,srcJoin,where,args ) ;
    }
    
    /**
     * データを削除.
     * <BR><BR>
     * スクリプトから渡されたデータを削除します.
     * <BR>
     * @param model 対象のモデル名を設定します.
     * @param im イメージマネージャを設定します.
     * @param delJoins 削除Joing群を設定します.
     * @param srcJoin 連結テーブル内容を設定します.
     * @param args 対象のデータを設定します.
     * @exception Exception 例外.
     */
    public void removeData( String model,ImageOp im,ArrayList<String> delJoins,
        Map<String,List<String>> srcJoin,HashMap<String,Object> args )
        throws Exception {
        checkCreate() ;
        DaoFactory factory = getDaoFactory() ;
        Dao dao = factory.getDao( record,model ) ;
        dao.remove( record,im,delJoins,srcJoin,args ) ;
    }
    
    /**
     * データを取得.
     * <BR><BR>
     * データを取得します.
     * <BR>
     * @param model 対象のモデル名を設定します.
     * @param join 連結テーブル内容を設定します.
     * @param srcJoin 対象のsrcJoin条件を設定します.
     * @param where 対象の条件を設定します.
     * @param args 条件に対するパラメータを設定します.
     * @return List<Map<String,Object>> 処理結果が返されます.
     * @exception Exception 例外.
     */
    public List<Map<String,Object>> find( String model,Map<String,String> join,
        Map<String,List<String>> srcJoin,String where,ArrayList<Object> args )
        throws Exception {
        checkCreate() ;
        DaoFactory factory = getDaoFactory() ;
        Dao dao = factory.getDao( record,model ) ;
        return dao.find( record,join,srcJoin,where,args ) ;
    }
    
    /**
     * データを取得.
     * <BR><BR>
     * データを取得します.
     * <BR>
     * @param model 対象のモデル名を設定します.
     * @param join 連結テーブル内容を設定します.
     * @param srcJoin 対象のsrcJoin条件を設定します.
     * @param offset 対象のオフセット値を設定します.
     * @param limit 対象のリミット値を設定します.
     * @param where 対象の条件を設定します.
     * @param args 条件に対するパラメータを設定します.
     * @param options 条件に対するオプションを設定します.
     * @return List<Map<String,Object>> 処理結果が返されます.
     * @exception Exception 例外.
     */
    public List<Map<String,Object>> limit( String model,Map<String,String> join,
        Map<String,List<String>> srcJoin,int offset,int limit,String where,ArrayList<Object> args )
        throws Exception {
        checkCreate() ;
        DaoFactory factory = getDaoFactory() ;
        Dao dao = factory.getDao( record,model ) ;
        return dao.limit( record,join,srcJoin,offset,limit,where,args ) ;
    }
    
    /**
     * データを取得.
     * <BR><BR>
     * データを取得します.
     * <BR>
     * @param model 対象のモデル名を設定します.
     * @param join 連結テーブル内容を設定します.
     * @param srcJoin 対象のsrcJoin条件を設定します.
     * @param offset 対象のオフセット値を設定します.
     * @param limit 対象のリミット値を設定します.
     * @param where 対象の条件を設定します.
     * @param args 条件に対するパラメータを設定します.
     * @param options 条件に対するオプションを設定します.
     * @return List<Map<String,Object>> 処理結果が返されます.
     * @exception Exception 例外.
     */
    public List<Map<String,Object>> first( String model,Map<String,String> join,
        Map<String,List<String>> srcJoin,String where,ArrayList<Object> args )
        throws Exception {
        checkCreate() ;
        DaoFactory factory = getDaoFactory() ;
        Dao dao = factory.getDao( record,model ) ;
        return dao.first( record,join,srcJoin,where,args ) ;
    }
    
    /**
     * データ数を取得.
     * <BR><BR>
     * データ数を取得します.
     * <BR>
     * @param model 対象のモデル名を設定します.
     * @param where 対象の条件を設定します.
     * @param args 条件に対するパラメータを設定します.
     * @return int 処理結果が返されます.
     * @exception Exception 例外.
     */
    public int count( String model,String where,ArrayList<Object> args )
        throws Exception {
        checkCreate() ;
        DaoFactory factory = getDaoFactory() ;
        Dao dao = factory.getDao( record,model ) ;
        return dao.count( record,where,args ) ;
    }
    
    /**
     * SQL実行.
     * <BR><BR>
     * SQL文を実行します.
     * <BR>
     * @param sql 対象の更新系SQL文を設定します.
     * @return Object 処理結果が返されます.
     * @exception Exception 例外.
     */
    public Object sql( String sql ) throws Exception {
        return ExecutionDao.executionStatement( record,sql ) ;
    }
    
    /**
     * 更新系SQL実行.
     * <BR><BR>
     * 更新系のSQL文を実行します.
     * <BR>
     * @param sql 対象の更新系SQL文を設定します.
     * @return int 件数が返されます.
     * @exception Exception 例外.
     */
    public int executionUpdate( String sql ) throws Exception {
        checkCreate() ;
        return record.executeUpdate( sql ) ;
    }
    
    /**
     * デバッグモードを取得.
     * <BR>
     * @return [true]の場合、デバッグは有効です.
     */
    public boolean isDebug() {
        if( record != null ) {
            return record.isDebug() ;
        }
        return false ;
    }
    
    /**
     * DaoFactoryを取得.
     */
    private DaoFactory getDaoFactory() {
        return ( DaoFactory )GlobalManager.getInstance().get( ServiceDef.DAO_FACTORY ) ;
    }
    
    /**
     * DBが生成されているかチェック.
     */
    private void checkCreate() throws Exception {
        if( isCreate() == false ) {
            throw new IOException( "BaseModelは生成されていません" ) ;
        }
    }
    
    /**
     * コミットチェック.
     */
    private void checkCommit() throws Exception {
        if( imageOp != null ) {
            if( imageOp.isWrite() == false ) {
                throw new IOException( "イメージトランザクションエラー" ) ;
            }
        }
        if( record == null ) {
            throw new IOException( "レコードは利用できません" ) ;
        }
        if( record.isUse() == false ) {
            throw new IOException( "レコード処理エラー" ) ;
        }
    }
}

