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

import java.io.PrintWriter;
import java.io.Writer;
import java.util.HashMap;

/**
 * IRC向けに調整した出力バッファです。改行コードは常にCRLFを出力します。一定時間のうちにしきい値以上のバイト数を出力しません。
 * 
 * @author tarchan
 */
public class IRCWriter
    extends PrintWriter {
    /** PASS <password> */
    public static final String PASS = "PASS %s";

    /** NICK <nickname> */
    public static final String NICK = "NICK %s";

    /** USER <user> <mode> <unused> <realname> */
    public static final String USER = "USER %s %d * :%s";

    /** QUIT [<quit message>] */
    public static final String QUIT = "QUIT :%s";

    /** JOIN (<channel> *(","<channel>) [<key> *(","<key>)]) / "0" */
    public static final String JOIN = "JOIN %s %s";

    /** PART <channel> *(","<channel>) [:<part message>] */
    public static final String PART = "PART %s :%s";

    /** TOPIC <channel> [:<topic>] */
    public static final String TOPIC = "TOPIC %s :%s";

    /** INVITE <nickname> <channel> */
    public static final String INVITE = "INVITE %s %s";

    /** PRIVMSG <msgtarget> <text to be sent> */
    public static final String PRIVMSG = "PRIVMSG %s :%s";

    /** NOTICE <msgtarget> <text to be sent> */
    public static final String NOTICE = "NOTICE %s :%s";

    /** PONG <server1> [<server2>] */
    public static final String PONG = "PONG %s";

    public IRCWriter( Writer out ) {
        super( out, true );

        // フォーマット登録
        setFormat( "PASS", PASS );
        setFormat( "NICK", NICK );
        setFormat( "USER", USER );
        setFormat( "QUIT", QUIT );
        setFormat( "JOIN", JOIN );
        setFormat( "PART", PART );
        setFormat( "INVITE", INVITE );
        setFormat( "PRIVMSG", PRIVMSG );
        setFormat( "NOTICE", NOTICE );
        setFormat( "PONG", PONG );
    }

    /** コマンドフォーマット */
    private HashMap<String, String> formatMap = new HashMap<String, String>();

    public IRCWriter setFormat( String command, String format ) {
        formatMap.put( command.toUpperCase(), format );
        return this;
    }

    private String getFormat( String format ) {
        String command = format.toUpperCase();
        return formatMap.containsKey( command ) ? formatMap.get( command ) : format;
    }

    /** IRC行区切り */
    private static final String CRLF = "\r\n";

    public PrintWriter format( String format, Object... args ) {
        format = getFormat( format );
        super.format( format + CRLF, args );
        return this;
    }

    public void flush() {
        // penalty wait
        // 制限時間に達したら一回休み
        if ( isLimitTime() ) {
            try {
                Thread.sleep( PENALTY_TIME );
            }
            catch ( InterruptedException x ) {
                x.printStackTrace();
            }
        }
        addPenaltyTime();

        // super flush
        super.flush();
    }

    /** 最大持ち時間 */
    private static final int LIMIT_TIME = 10 * 1000;

    /** 単位ペナルティ時間 */
    private static final int PENALTY_TIME = 2 * 1000;

    /** 合計ペナルティ時間 */
    private long penaltyTime;

    protected boolean isLimitTime() {
        // get current time
        final long currentTime = System.currentTimeMillis();

        // message timer >= current time, always
        if ( penaltyTime < currentTime ) {
            penaltyTime = currentTime;
        }

        // message timer - current time < limit time, always
        long limitTime = penaltyTime - currentTime;
        if ( limitTime > LIMIT_TIME )
            limitTime = LIMIT_TIME;
        final int limitPercent = 100 * (int) limitTime / LIMIT_TIME;

        // System.out.println("penalty: " + (penaltyTime - currentTime) + " ms,"
        // + limitPercent + " %");

        return limitPercent >= 100;
    }

    protected void addPenaltyTime() {
        penaltyTime += PENALTY_TIME;
    }
}
