/*
 * Copyright 2007 nori090
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.coderepos.nori090.lib2chj;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;

/**
 * @author nori090
 * @version $Rev: 85 $ $Date: 2008-06-02 22:18:54 +0900 (Mon, 02 Jun 2008) $
 */
public class HttpHelper {

    /**
     * datファイル取得用http get
     * 
     * @param data
     * @return line reader
     * @throws IOException
     */
    public static BufferedReader http_get_for_dat( HTTP_DATA data )
        throws IOException {
        final String master = data.getUrl();
        String[] urls = make_kako_log_url( data.getUrl() );
        HttpURLConnection con = null;
        for ( String s : urls ) {
            /*
             * 最初はHTTP_DATAのURL通りにアクセスし、 次は、過去ログを見に行く。
             */
            con = createConnection( data );
            if ( 304 == con.getResponseCode() ) {
                System.out.println( "Not Modified" );
                con.disconnect();
                return null;
            }
            else if ( 404 == con.getResponseCode() || 302 == con.getResponseCode() ) {
                data.setContent_length( -1 );
                data.setEtag( null );
                data.setUrl( s );
            }
            else if ( 200 != con.getResponseCode() ) {
                System.err.println( "Not Support Status Code:" + con.getResponseCode() );
                con.disconnect();
                return null;
            }
        }
        if ( con.getResponseCode() != 200 ) {
            System.err.println( "データが取得できませんでした。" );
            data.setUrl( master );
            con.disconnect();
            return null;
        }
        {
            // TODO Cookie Set-Cookie
            Object tag = con.getHeaderField( "ETag" );
            if ( tag != null ) {
                data.setEtag( tag.toString() );
            }
            data.setContent_length( con.getContentLength() );
        }
        // charsetの選択
        String charset = selectCharset( con.getContentType() );
        BufferedReader br;
        if ( con.getContentEncoding() != null && "gzip".equals( con.getContentEncoding().toLowerCase() ) ) {
            br = new BufferedReader( new InputStreamReader( new GZIPInputStream( con.getInputStream() ), charset ) );
        }
        else {
            br = new BufferedReader( new InputStreamReader( con.getInputStream(), charset ) );
        }

        return br;
    }

    /**
     * ノーマルなアクセス
     * 
     * @param data
     * @return line reader
     * @throws IOException
     */
    public static BufferedReader http_get( HTTP_DATA data )
        throws IOException {
        final String master = data.getUrl();
        HttpURLConnection con = createConnection( data );
        if ( con.getResponseCode() != 200 ) {
            System.err.println( "データが取得できませんでした。" );
            data.setUrl( master );
            con.disconnect();
            return null;
        }
        {
            // TODO Cookie Set-Cookie
            Object tag = con.getHeaderField( "ETag" );
            if ( tag != null ) {
                data.setEtag( tag.toString() );
            }
            data.setContent_length( con.getContentLength() );
        }
        // charsetの選択
        String charset = selectCharset( con.getContentType() );
        BufferedReader br;
        if ( con.getContentEncoding() != null && "gzip".equals( con.getContentEncoding().toLowerCase() ) ) {
            br = new BufferedReader( new InputStreamReader( new GZIPInputStream( con.getInputStream() ), charset ) );
        }
        else {
            br = new BufferedReader( new InputStreamReader( con.getInputStream(), charset ) );
        }

        return br;
    }

    /**
     * urlから過去ログのurl listを作る。
     */
    static String[] make_kako_log_url( final String url ) {
        if ( !url.endsWith( ".dat" ) )
            throw new RuntimeException( "unknown url" );
        try {
            URL _url = new URL( url );
            String id = _split_thread_id_at_path( _url.getPath() );
            String ita = _split_ita_name_at_path( _url.getPath() );
            String[] urls = null;
            if ( id.length() == 10 ) {
                urls = _kako_log_id10( _url.getHost(), ita, id );
            }
            else if ( id.length() == 9 ) {
                urls = _kako_log_id9( _url.getHost(), ita, id );
            }
            return urls;
        }
        catch ( MalformedURLException e ) {
            e.printStackTrace();
            throw new RuntimeException( e );
        }
    }

    /**
     * スレキーが10桁の場合 /板キー/kako/スレキーの上位4桁/スレキーの上位5桁/スレキー.dat.gz
     */
    static String[] _kako_log_id10( String serv, String ita, String id ) {
        String s4 = id.substring( 0, 4 );
        String s5 = id.substring( 0, 5 );
        String prefix = "http://" + serv + "/" + ita + "/kako/";
        String p1 = prefix + s4 + "/" + s5 + "/" + id + ".dat.gz";
        String p2 = prefix + s4 + "/" + s5 + "/" + id + ".dat";
        return new String[] { p1, p2 };
    }

    /**
     * スレキーが9桁の場合 /板キー/kako/スレキーの上位3桁/スレキー.dat.gz
     */
    static String[] _kako_log_id9( String serv, String ita, String id ) {
        String s3 = id.substring( 0, 3 );
        String prefix = "http://" + serv + "/" + ita + "/kako/";
        String p1 = prefix + s3 + "/" + id + ".dat.gz";
        String p2 = prefix + s3 + "/" + id + ".dat";
        return new String[] { p1, p2 };
    }

    /**
     * /hogehoge/dat/1234567890.datから、板名であるhogehogeを取り出す。
     */
    static String _split_ita_name_at_path( String path ) {
        if ( path.startsWith( "/" ) )
            return path.split( "/" )[1];
        else
            return path.split( "/" )[0];
    }

    /**
     * /hogehoge/dat/1234567890.datから、IDである1234567890を取り出す。
     */
    static String _split_thread_id_at_path( String path ) {
        if ( path.endsWith( ".dat" ) ) {
            String s = null;
            if ( path.startsWith( "/" ) )
                s = path.split( "/" )[3];
            else
                s = path.split( "/" )[2];
            return s.substring( 0, s.length() - ".dat".length() );
        }
        return null;
    }

    /**
     * コネクションを作成
     */
    public static HttpURLConnection createConnection( HTTP_DATA data )
        throws IOException {
        URL url = new URL( data.getUrl() );
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        con.setConnectTimeout( data.getConnectTimeout() >= 0 ? data.getConnectTimeout() : 10000 );
        con.setReadTimeout( data.getReadTimeout() >= 0 ? data.getReadTimeout() : 10000 );
        con.setRequestProperty( "Host", url.getHost() );
        con.setRequestProperty( "Accept", "*/*" );
        con.setRequestProperty( "User-Agent", data.getUser_Agent() != null ? data.getUser_Agent()
                        : "Monazilla/1.00 (lib2chj/0.1)" );
        con.setRequestProperty( "Accept-Encoding", "gzip" );
        // TODO Cookie Set-Cookie
        if ( data.getEtag() != null && !"".equals( data.getEtag() ) ) {
            con.setRequestProperty( "If-None-Match", data.getEtag() );
        }
        if ( 0 < data.getContent_length() ) {
            con.setRequestProperty( "Range", "bytes=" + data.getContent_length() + "-" );
        }
        return con;
    }

    /**
     * charsetの選択.defaultはjisautodetect.
     */
    public static String selectCharset( String contentType ) {
        String charset = "JISAutoDetect";
        if ( null != contentType && -1 < contentType.indexOf( "charset=" ) ) {
            charset = contentType;
            Pattern p = Pattern.compile( "charset=[a-zA-Z0-9_-]+" );
            Matcher match = p.matcher( charset );
            match.find();
            charset = match.group( 0 );
            charset = charset.substring( "charset=".length() );
            String tmp = charset.toLowerCase();
            if ( "sjis".equals( tmp ) || "shift_jis".equals( tmp ) ) {
                charset = "MS932";
            }
        }
        return charset;
    }

    /*
     * debug print http header
     */
    public static void debugPringHttpHeader( HttpURLConnection con ) {
        Map<String, List<String>> m = con.getHeaderFields();
        for ( Iterator<String> ite = con.getHeaderFields().keySet().iterator(); ite.hasNext(); ) {
            String key = ite.next();
            System.out.printf( "key=%s\n", key );
            List<String> l = m.get( key );
            for ( String v : l ) {
                System.out.printf( " value=%s\n", v );
            }
        }
    }

}
