package org.itscool.commons.request;

import java.io.ByteArrayOutputStream;

import org.itscool.commons.util.ByteUtil;
import org.itscool.commons.util.URLDecodeUtil;

/**
 * マルチパート形式のリクエストパラメータを解析するクラスです
 *
 * @author Michiaki Kanou
 * @version 1.00A 2005/12/01 新規作成
 * @version 2.00A 2006/10/12 バイナリータイプのファイル転送対応を追加
 */
public class MultiHttpGeneralForm extends HttpGeneralForm{
    public static final String DISPOSTION="Content-Disposition";
    public static final String TYPE="Content-Type";
    
    /**
     * 以下のBeanのきもはparseData() methodなのですがこの中身を理解するためには
     * HTTPプロトコルの理解が必要です。
     * 今は部品として使っているだけです。
     * しかし既に出来ているものを使うのは”車輪の再発明”をするなという原則から
     * は、正しい方法と考えます。
     * 　parseData()中ではstartLineとfileLineの検出に13(x0D)と10(x0A)つまりCR,
     * LFを使っています。
     * HTTPのメッセージというのはCR, LFで”区切られて”いるということを意味し
     * ています。
     * さらにこのような方法でメッセージの区切り検出が出来るということはバイナリ
     * ファイルも文字コードに変換つまり冗長な形に変換して送信していると言うこと
     * で、変換されたストリーム中ではCR, LFの文字シーケンスは現れないようになっ
     * ていると言うことになりますね。
     * その割にはサーバ側で元のバイナリ形式に復元する処理は為されていないので、
     * ちょっと謎。
     * @param buffer
     */
    public void doParseData(byte[] buffer, String encode){
        int pos=0;
        int bufSize = buffer.length;
        
        for(pos=0; pos<=bufSize; ){
            byte[] header = ByteUtil.getLineCrLf(buffer, pos);
            pos += header.length + ByteUtil.LINE.length;
            byte[] dispostion = ByteUtil.getLineCrLf(buffer, pos);
            pos += dispostion.length + ByteUtil.LINE.length;
            byte[] type = ByteUtil.getLineCrLf(buffer, pos);
            pos += type.length + ByteUtil.LINE.length;
            
//			System.out.println("[HEADER]"+new String(header));
//			System.out.println("[DISPOS]"+new String(dispostion));
//			System.out.println("[TYPE  ]"+new String(type));
            
            String dispostionStr = new String(dispostion);
            if( dispostionStr==null || dispostionStr.trim().length() == 0){
                return;
            }
            byte[] data;
            if( (new String(type)).indexOf(TYPE) == 0 ){
                byte[] non = ByteUtil.getLineCrLf(buffer, pos);
                pos += non.length + ByteUtil.LINE.length;
                data = ByteUtil.copyByteData(buffer, pos, buffer.length-pos);
                //System.out.println(new String(data));
                data = getFormData(data, header);
//				pos += data.length + LINE.length;
                pos += data.length + 1;
                
                String key=getKey(dispostionStr);
                key = URLDecodeUtil.filter(key);
                String fileName = getFileName(dispostionStr);
                if(fileName==null || fileName.length()==0){
                    continue;
                }
                UploadFile file = new UploadFile();
                try{
                    file.setData(data);
                }catch(Exception e){
                    e.printStackTrace();
                }
                file.setFileName(fileName);
                file.setSize(data.length);
                addParameter(key, file);
            }else{
                data = ByteUtil.getLineCrLf(buffer, pos);
                pos += data.length + ByteUtil.LINE.length;
                //リクエストパラメータをセット
                String key=getKey(new String(dispostion));
                key = URLDecodeUtil.filter(key);
                String dataStr = URLDecodeUtil.filter(new String(data));
                addParameter(key, dataStr);
            }
            
//			System.out.println("[DATA  ]"+new String(data));
        }
    }
    
    
    /**
     * マルチパートデータを抽出します
     * @param buffer マルチパートデータの開始から始まるバイト配列
     * @param header 終了判定のためのヘッダ情報
     * @return マルチパートデータを返します
     */
    protected static byte[] getFormData(byte[] buffer, byte[] header){
        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        
        int endPos = ByteUtil.indexof(buffer, header);
        byte[] byteArray = ByteUtil.copyByteData(buffer, 0, endPos);
        
        return byteArray;
    }
    
    /**
     * 「Content-Disposition」記述からfilename属性を取得します
     * @param disposition 「Content-Disposition」記述
     * @return filename属性を返します
     */
    private String getFileName(String disposition){
        int idx=disposition.lastIndexOf(System.getProperty("file.separator"));
        if(idx == -1){
            //return null;
            idx = disposition.lastIndexOf("filename=");
            idx = idx + ("filename=").length();
        }
        String fileName=disposition.substring(idx+1,disposition.length()-1);
        
        return fileName;
    }
    
    /**
     * 「Content-Disposition」記述からname属性を取得する
     * @param disposition 「Content-Disposition」記述
     * @return name属性を返します
     */
    private String getKey(String disposition){
        int start = disposition.indexOf("name=\"");
        String stack = disposition.substring(start + ("name=\"").length(),
            disposition.length());
        int end = stack.indexOf("\"");
        
        String key = stack.substring(0, end);
        
        return key;
    }
    
}
