/*
 * IRCClient.java
 * IRCCore
 *
 * Created by tarchan on Aug 07, 2006.
 * Copyright (c) 2006 tarchan. All rights reserved.
 */
package com.mac.tarchan.irc;

import java.util.HashMap;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;

import org.apache.log4j.Logger;

/**
 * IRCサーバから受け取ったメッセージをコマンド毎に振り分けます。 IRCメッセージを受け取る場合は、このクラスを継承します。
 * 
 * @author tarchan
 * @author nori090
 */
public class IRCClient {
    /** debug logger */
    private static Logger log = Logger.getLogger( IRCClient.class );

    /** IRCメッセージを受け取るターゲット */
    private Object target;

    /** IRCコマンドを処理するアクションのキャッシュ */
    private HashMap<String, Method> cache = new HashMap<String, Method>();

    /** アクション名の前置文字列 */
    private String prefix = "reply";

    /** デフォルトのアクション名 */
    private String altAction = "reply";

    /**
     * IRCメッセージを受けてアクションを振り分けします。
     * 
     * @param newTarget ターゲット
     */
    public IRCClient( Object newTarget ) {
        target = newTarget;
    }

    /**
     * IRCメッセージを受けてアクションを振り分けします。
     */
    public IRCClient() {
        target = this;
    }

    /**
     * IRCコマンドからアクション名を決定する命名規約を実装します。 デフォルトは "reply" + message.getCommand().toUpperCamelCase() を返します。
     * 
     * @param command コマンド名
     * @return アクション名
     */
    protected String forName( String command ) {
        String UpperCamelCase = Character.toUpperCase( command.charAt( 0 ) ) + command.substring( 1 ).toLowerCase();
        return prefix + UpperCamelCase;
    }

    /**
     * IRCコマンドに対応するアクションを登録します。
     * 
     * @param command コマンド名
     * @param action アクション名
     */
    @SuppressWarnings( "unchecked" )
    protected Method register( String command, String action ) {
        Method method = null;
        Class ref = target.getClass();
        try {
            // 指定したアクション名をメソッドとして登録
            method = ref.getMethod( action, IRCMessage.class );
        }
        catch ( NoSuchMethodException x ) {
            try {
                // デフォルトのアクション名をメソッドとして登録
                method = ref.getMethod( altAction, IRCMessage.class );
            }
            catch ( NoSuchMethodException y ) {
                throw new RuntimeException( y );
            }
        }
        cache.put( command, method );
        return method;
    }

    /**
     * コマンド名に対応するアクションを検索します。 未登録コマンドの場合は新規登録します。 ひとつも検索できない場合は null を返します。
     * 
     * @param command コマンド名
     * @return メソッド
     */
    protected Method select( String command ) {
        // コマンド名に対応するアクションを検索
        Method method = cache.get( command );

        // 未登録コマンドの場合は新規登録
        if ( method == null ) {
            String action = forName( command );
            try {
                method = register( command, action );
            }
            catch ( RuntimeException x ) {
                method = null;
            }
        }
        return method;
    }

    /**
     * IRCメッセージを対応メソッドに振り分けます。
     * 
     * @param message IRCメッセージ
     */
    public void listen( IRCMessage message ) {
        try {
            // コマンド名に対応するアクションを検索して実行
            Method selector = select( message.getCommand() );
            if ( selector != null ) {
                selector.invoke( target, message );
            }
        }
        catch ( IllegalAccessException x ) {
            throw new RuntimeException( x );
        }
        catch ( InvocationTargetException x ) {
            throw new RuntimeException( x );
        }
    }

    /**
     * デフォルトの応答アクションです。 すべてのパラメータとトレーラを" "で連結して表示します。
     * 
     * @param message IRCメッセージ
     */
    public void reply( IRCMessage message ) {
        if ( log.isDebugEnabled() ) {
            log.debug( message );
        }
    }
}
