package org.coderepos.nori090.peercast;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.coderepos.nori090.peercast.EventCollector.EventData;
import org.coderepos.nori090.peercast.EventCollector.EventDate;
import org.coderepos.nori090.peercast.EventCollector.EventDetail;

import com.mac.tarchan.irc.IRCClient;
import com.mac.tarchan.irc.IRCMessage;
import com.mac.tarchan.irc.IRCNetwork;
import com.mac.tarchan.irc.IRCWriter;

public class PeerCastChecker
    extends IRCClient {

    private static final Log log = LogFactory.getLog( PeerCastChecker.class );

    // public static final String CH = "#nnntest";
    // public static final String ADDR = "irc://irc.media.kyoto-u.ac.jp" + CH;

    public static final String CH = "#[Arekuma]";

    public static final String ADDR = "irc://irc.friend.td.nu" + CH;

    private final static String DEFAULT_ENCODING = "ISO-2022-JP";

    private final IRCNetwork net = IRCNetwork.getNetwork( "friend" );

    private static String nick = "nori_bot";

    private static String pass = "";

    public final static String[] YPs = { "http://kp.podzone.net/index.txt",
    // "http://extremeyp.ath.cx/index.txt",
        // "http://uppeercast.dyndns.org/index.txt",
        "http://cp.webhop.net/index.txt" };

    public PeerCastChecker() {
        super();
        net.addClient( (IRCClient) this );
        net.login( ADDR, nick, pass );
        this.startCollector();
        Thread t = new Thread( this.new Checker(), "PeerCastCheckerThread" );
        t.start();
        Thread t2 = new Thread( this.new ConnectionRetry(), "ConnectionRetryThread" );
        t2.start();
    }

    private void startCollector() {
        ContactUrlCollector url_collector = new ContactUrlCollector();
        EventCollector event_collector = new EventCollector();
        ShutdownThread shutdownThread = new ShutdownThread();
        shutdownThread.addListener( url_collector );
        shutdownThread.addListener( event_collector );
        url_collector.start();
        event_collector.start();
        Runtime.getRuntime().addShutdownHook( new Thread( shutdownThread ) );
    }

    public void replyPrivmsg( IRCMessage message ) {
        String s = message.getTrailing( DEFAULT_ENCODING );
        IRCNetwork net = message.getNetwork();
        String ch = message.getTarget();
        parse( net, ch, s );
    }

    private static void NOTICE( IRCWriter writer, String ch, String comment ) {
        if ( writer == null ) {
            log.error( "NOTICE write error" );
        }
        else {
            writer.format( IRCWriter.CMD.NOTICE, ch, comment );
            writer.flush();
        }
    }

    private void parse( IRCNetwork net, String ch, String s ) {
        if ( s == null ) {
            return;
        }
        if ( s.startsWith( "check(" ) && s.endsWith( ")" ) ) {
            String reg = s.substring( 6, s.length() - 1 );
            new Thread( new CheckCmd( net, ch, reg ), "CheckCmd" ).start();
        }
        else if ( s.startsWith( "sure(" ) && s.endsWith( ")" ) ) {
            String param = s.substring( 5, s.length() - 1 );
            new Thread( new SureCmd( net, ch, param ), "SureCmd" ).start();
        }
        else if ( s.startsWith( "stream(" ) && s.endsWith( ")" ) ) {
            String param = s.substring( 7, s.length() - 1 );
            new Thread( new StreamCmd( net, ch, param ), "StreamCmd" ).start();
        }
        else if ( s.startsWith( "event(" ) && s.endsWith( ")" ) ) {
            String param = s.substring( 6, s.length() - 1 );
            new Thread( new EventCmd( net, ch, param ), "EventCmd" ).start();
        }
    }

    private class CheckCmd
        implements Runnable {
        String param;

        String ch;

        IRCNetwork net;

        private CheckCmd( IRCNetwork net, String ch, String param ) {
            this.param = param;
            this.ch = ch;
            this.net = net;
        }

        @Override
        public void run() {
            Pattern p = Pattern.compile( param );
            ArrayList<String> ret = new ArrayList<String>();
            for ( String yp : PeerCastChecker.YPs ) {
                buildComment( IndexTxtUtil.getIndexTxtAtProxy( yp ), p, ret );
            }
            if ( ret.isEmpty() ) {
                ret.add( "見当たらないよ！" );
            }
            if ( ret.size() > 10 ) {
                ret.clear();
                ret.add( "検索結果が10件以上だから、もう少し絞り込んで！" );
            }
            IRCWriter out = net.writer( DEFAULT_ENCODING );
            for ( String v : ret ) {
                NOTICE( out, ch, v );
            }
        }

        private void buildComment( List<String> index_txt, Pattern p, List<String> result ) {
            if ( index_txt == null )
                return;
            for ( String v : index_txt ) {
                Matcher m = p.matcher( v );
                if ( m.find() ) {
                    String[] vs = v.split( "<>" );
                    StringBuilder sb =
                        new StringBuilder().append( vs[0] ).append( " / " ).append( vs[4] ).append( " / " ).append(
                                                                                                                    vs[5] );
                    result.add( sb.toString() );
                }
            }
        }
    }

    private class SureCmd
        implements Runnable {
        String param;

        String ch;

        IRCNetwork net;

        private SureCmd( IRCNetwork net, String ch, String param ) {
            this.param = param;
            this.ch = ch;
            this.net = net;
        }

        @Override
        public void run() {
            List<String> l = ContactUrlCollector.searchContactUrl( param );
            ArrayList<String> ret = new ArrayList<String>();
            if ( l.isEmpty() ) {
                ret.add( "見当たらないよ！" );
            }
            for ( String w : l ) {
                String[] ss = w.split( "<>" );
                ret.add( ss[0] + " [" + ss[1] + "]" );
            }
            if ( ret.size() > 10 ) {
                ret.clear();
                ret.add( "検索結果が10件以上だから、もう少し絞り込んで！" );
            }
            IRCWriter out = net.writer( DEFAULT_ENCODING );
            for ( String v : ret ) {
                NOTICE( out, ch, v );
            }
        }
    }

    private class EventCmd
        implements Runnable {
        String param;

        String ch;

        IRCNetwork net;

        private EventCmd( IRCNetwork net, String ch, String param ) {
            this.param = param;
            this.ch = ch;
            this.net = net;
        }

        @Override
        public void run() {
            if ( param.length() == 0 ) {
                param = new SimpleDateFormat( "yyyy/MM/dd" ).format( new Date() );
            }
            String[] p = param.split( "[ ,/]+" );
            if ( p.length != 3 ) {
                IRCWriter out = net.writer( DEFAULT_ENCODING );
                NOTICE( out, ch, "event(yyyy/mm/dd)みたいに使うます。(日付を指定する)" );
                return;
            }
            if ( !StringUtils.isNumeric( p[0] ) || !StringUtils.isNumeric( p[1] ) || !StringUtils.isNumeric( p[2] ) ) {
                IRCWriter out = net.writer( DEFAULT_ENCODING );
                NOTICE( out, ch, "event(yyyy/mm/dd)みたいに使うます。(日付を指定する)" );
                return;
            }
            Map<EventDate, EventDetail> m = EventCollector.getMap();
            EventDate e = new EventDate( Integer.parseInt( p[0] ), Integer.parseInt( p[1] ), Integer.parseInt( p[2] ) );
            if ( m.containsKey( e ) ) {
                EventDetail detail = m.get( e );
                IRCWriter out = net.writer( DEFAULT_ENCODING );
                if ( detail.size() > 10 ) {
                    NOTICE( out, ch, "検索結果が10件以上だから、もう少し絞り込んで！" );
                    return;
                }
                for ( EventData data : detail ) {
                    NOTICE( out, ch, EventCollectorUtil.unicode2sjis0201( data.author ) + "さんが、" +
                        EventCollectorUtil.unicode2sjis0201( data.eventName ) + "をするみたいだよ。" );
                }
                return;
            }
            else {
                IRCWriter out = net.writer( DEFAULT_ENCODING );
                NOTICE( out, ch, "見当たらないよ！" );
                return;
            }
        }
    }

    private class StreamCmd
        implements Runnable {
        String param;

        String ch;

        IRCNetwork net;

        public StreamCmd( IRCNetwork net, String ch, String param ) {
            this.net = net;
            this.ch = ch;
            this.param = param;
        }

        @Override
        public void run() {
            Pattern p = Pattern.compile( param );
            ArrayList<String> ret = new ArrayList<String>();
            for ( String yp : PeerCastChecker.YPs ) {
                buildComment( IndexTxtUtil.getIndexTxtAtProxy( yp ), p, ret );
            }
            if ( ret.isEmpty() ) {
                ret.add( "見当たらないよ！" );
            }
            if ( ret.size() > 10 ) {
                ret.clear();
                ret.add( "検索結果が10件以上だから、もう少し絞り込んで！" );
            }
            IRCWriter out = net.writer( DEFAULT_ENCODING );
            for ( String v : ret ) {
                NOTICE( out, ch, v );
            }
        }

        private void buildComment( List<String> index_txt, Pattern p, List<String> result ) {
            if ( index_txt == null )
                return;
            for ( String v : index_txt ) {
                Matcher m = p.matcher( v );
                if ( m.find() ) {
                    String[] vs = v.split( "<>" );
                    StringBuilder sb = new StringBuilder( "http://localhost:7144/pls/" );
                    sb.append( vs[1] );
                    sb.append( "?tip=" );
                    sb.append( vs[2] );
                    result.add( sb.toString() );
                }
            }
        }
    }

    private class ConnectionRetry
        implements Runnable {
        @Override
        public void run() {
            int count = 0;
            try {
                while ( true ) {
                    Thread.sleep( 1000 * 60 );
                    if ( !net.isConnected() ) {
                        if ( count > 10 ) {
                            break;
                        }
                        count++;
                        net.login( ADDR, nick, pass );
                    }
                    else if ( count > 0 ) {
                        // reset
                        count = 0;
                    }
                }
            }
            catch ( Exception e ) {
                e.printStackTrace();
            }
            finally {
                System.err.println( "connection retry err!" );
            }
        }
    }

    private class Checker
        implements Runnable {
        ConcurrentHashMap<String, Map<String, String>> m = new ConcurrentHashMap<String, Map<String, String>>();

        @Override
        public void run() {
            try {
                Thread.sleep( 60000 );
                IRCWriter out = net.writer( DEFAULT_ENCODING );
                for ( String yp : PeerCastChecker.YPs ) {
                    Map<String, String> yp_map = search( IndexTxtUtil.getIndexTxtAtProxy( yp ) );
                    for ( String k : yp_map.keySet() ) {
                        ConcurrentHashMap<String, String> value = new ConcurrentHashMap<String, String>();
                        value.put( "value", yp_map.get( k ) );
                        value.put( "count", "1" );
                        m.put( k, value );
                        NOTICE( out, CH, "[" + yp_map.get( k ) + "]が配信を始めたよ！" );
                    }
                }

                while ( true ) {
                    Thread.sleep( 60000 * 5 );
                    for ( String yp : PeerCastChecker.YPs ) {
                        Map<String, String> yp_map = search( IndexTxtUtil.getIndexTxtAtProxy( yp ) );
                        compare( m, yp_map );
                    }
                    searchResult( m, out );
                    clearCount( m );
                }
            }
            catch ( InterruptedException e ) {
                e.printStackTrace();
            }
            catch ( Exception e ) {
                System.err.println( "別スレッドで異常。終了します。" );
                e.printStackTrace();
            }
        }

        private void clearCount( ConcurrentHashMap<String, Map<String, String>> m2 ) {
            for ( String k : m2.keySet() ) {
                m2.get( k ).put( "count", "1" );
            }
        }

        private void searchResult( ConcurrentHashMap<String, Map<String, String>> m2, IRCWriter out ) {
            for ( String k : m2.keySet() ) {
                Map<String, String> m = m2.get( k );
                String count = m.get( "count" );
                if ( "0".equals( count ) ) {
                    NOTICE( out, CH, "[" + m.get( "value" ) + "]が配信を始めたよ！" );
                }
                else if ( "1".equals( count ) ) {
                    NOTICE( out, CH, "[" + m.get( "value" ) + "]が配信終了したみたい。。。" );
                    m2.remove( k );
                }
            }
        }

        private void compare( Map<String, Map<String, String>> master, Map<String, String> yp ) {
            for ( String k : yp.keySet() ) {
                Map<String, String> m = master.get( k );
                if ( m == null ) {
                    ConcurrentHashMap<String, String> value = new ConcurrentHashMap<String, String>();
                    value.put( "value", yp.get( k ) );
                    value.put( "count", "0" );
                    master.put( k, value );
                }
                else {
                    m.put( "count", "2" );
                }
            }
        }

        private Map<String, String> search( List<String> l ) {
            Pattern p = Pattern.compile( "プログラミング|[Pp]rogramming|あれくま" );
            ConcurrentHashMap<String, String> user = new ConcurrentHashMap<String, String>();
            if ( l == null )
                return user;
            for ( String v : l ) {
                Matcher m = p.matcher( v );
                if ( m.find() ) {
                    String[] vs = v.split( "<>" );
                    StringBuilder sb =
                        new StringBuilder().append( vs[0] ).append( " / " ).append( vs[4] ).append( " / " ).append(
                                                                                                                    vs[5] );
                    user.put( vs[1], sb.toString() );
                }
            }
            return user;
        }
    }
}
