/*
 * 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.peercast.proxy;

import static org.coderepos.nori090.peercast.proxy.YpHttpProxy.HTTP_ELEMENT_CHARSET;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;

import org.apache.commons.lang.ArrayUtils;
import org.coderepos.nori090.pcraw.PCRaw;

/**
 * @author nori090
 * @version $Rev: 136 $ $Date: 2008-07-27 20:23:03 +0900 (Sun, 27 Jul 2008) $
 */
public class YpResponse {

    private YpResponse() {
    }

    public static void getResponse( BufferedInputStream in, Content c ) {
        c.lastupdate = System.currentTimeMillis();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        StringBuilder header = new StringBuilder( "" );
        String data = "";
        int responseCode = 200;
        int byteCount = 0;
        int contentLength = 0;

        try {
            data = readLine( in );
            if ( data != null ) {
                header.append( data ).append( "\r\n" );
                int pos = data.indexOf( " " );
                if ( ( data.toLowerCase().startsWith( "http" ) ) && ( pos >= 0 ) &&
                    ( data.indexOf( " ", pos + 1 ) >= 0 ) ) {
                    String rcString = data.substring( pos + 1, data.indexOf( " ", pos + 1 ) );
                    try {
                        responseCode = Integer.parseInt( rcString );
                    }
                    catch ( Exception e ) {
                        e.printStackTrace();
                    }
                }
            }
            String encoding = null;
            while ( ( data = readLine( in ) ) != null ) {
                if ( data.length() == 0 )
                    break;
                header.append( data ).append( "\r\n" );
                int pos = data.toLowerCase().indexOf( "content-length:" );
                if ( pos >= 0 ) {
                    contentLength = Integer.parseInt( data.substring( pos + 15 ).trim() );
                }
                pos = data.toLowerCase().indexOf( "content-encoding:" );
                if ( pos >= 0 ) {
                    encoding = data.substring( pos + 17 ).trim().toLowerCase();
                }
            }

            header.append( "\r\n" );

            out.write( header.toString().getBytes( HTTP_ELEMENT_CHARSET ), 0, header.length() );

            if ( responseCode != 200 && contentLength == 0 ) {
                c.cache = out.toByteArray();
                return;
            }
            boolean waitForDisconnect = true;
            if ( contentLength > 0 ) {
                waitForDisconnect = false;
            }

            if ( contentLength > 0 || waitForDisconnect ) {
                ByteArrayOutputStream content = new ByteArrayOutputStream();
                byte[] buf = new byte[4096];
                int bytesIn = 0;
                while ( ( byteCount < contentLength || waitForDisconnect ) && ( bytesIn = in.read( buf ) ) >= 0 ) {
                    content.write( buf, 0, bytesIn );
                    byteCount += bytesIn;
                }
                buf = content.toByteArray();
                if ( buf.length > 0 ) {
                    PCRaw pcRaw = checkPCRaw( buf, "gzip".equals( encoding ) );
                    if ( pcRaw == null ) {
                        out.write( buf, 0, buf.length );
                        c.cache = out.toByteArray();
                    }
                    else {
                        c.pcRaw = pcRaw;
                        c.raw = true;
                        c.cache = pcRaw.getData();
                    }
                }
            }
        }
        catch ( Exception e ) {
            e.printStackTrace();
        }
    }

    private static final Pattern p = Pattern.compile( "^index(_[a-z]+)?\\.txt$" );

    private static PCRaw checkPCRaw( byte[] bs, boolean isGzip ) {
        try {
            ByteArrayInputStream bais = new ByteArrayInputStream( bs );
            BufferedReader br = null;
            if ( isGzip ) {
                br = new BufferedReader( new InputStreamReader( new GZIPInputStream( bais ), "UTF-8" ) );
            }
            else {
                br = new BufferedReader( new InputStreamReader( bais, "UTF-8" ) );
            }

            String s = null;
            String stream_url = null;
            while ( ( s = br.readLine() ) != null ) {
                String[] ss = s.split( "<>" );
                if ( ss.length == 19 ) {
                    if ( "RAW".equals( ss[9] ) && "".equals( ss[4] ) ) {
                        Matcher m = p.matcher( ss[0] );
                        if ( m.find() ) {
                            StringBuilder sb = new StringBuilder( "http://localhost:7144/stream/" );
                            sb.append( ss[1] );
                            sb.append( "?tip=" );
                            sb.append( ss[2] );
                            stream_url = sb.toString();
                            break;
                        }
                    }
                }
            }
            br.close();
            if ( stream_url != null ) {
                return new PCRaw( stream_url ) {
                    @Override
                    public byte[] getData() {
                        StringBuilder header = new StringBuilder( "" );
                        byte[] bs = super.getData();
                        header.append( "HTTP/1.1 200 OK\r\n" );
                        header.append( "Connection: close\r\n" );
                        header.append( "Content-Type: text/plain; charset=UTF-8\r\n" );
                        header.append( String.format( "Content-Length: %d\r\n", bs == null ? 0 : bs.length ) );
                        header.append( "\r\n" );
                        try {
                            return ArrayUtils.addAll( header.toString().getBytes( HTTP_ELEMENT_CHARSET ), bs );
                        }
                        catch ( UnsupportedEncodingException e ) {
                            e.printStackTrace();
                        }
                        return null;
                    }
                };
            }
        }
        catch ( Exception e ) {
            e.printStackTrace();
        }
        return null;
    }

    private static String readLine( InputStream in ) {
        StringBuilder data = new StringBuilder( "" );

        try {
            in.mark( 1 );
            if ( in.read() == -1 ) {
                return null;
            }
            else {
                in.reset();
            }

            int c = -1;
            while ( ( c = in.read() ) >= 0 ) {
                if ( c == 0 || c == 10 || c == 13 ) {
                    break;
                }
                else {
                    data.append( (char) c );
                }
            }
            if ( c == 13 ) {
                in.mark( 1 );
                if ( in.read() != 10 ) {
                    in.reset();
                }
            }
        }
        catch ( Exception e ) {
            e.printStackTrace();
        }
        return data.toString();
    }

}
