/**
 * Copyright (c) 2003-2004 System Integrator Corporation.
 *                 All Rights Reserved.
 */
package jp.co.sint.tools;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import javax.servlet.http.HttpServletResponse;

import jp.co.sint.config.SICSVConf;
import jp.co.sint.config.SIConfig;
import jp.co.sint.database.SIDBUtil;

import org.apache.log4j.Category;

/**
 * @version $Id: SICSVWrite.java,v 1.0 2003/12/19 Exp $
 * @author  Jinwang Chen
 * <br>Description:
 * <p>History</p>
 * <p>Author&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Date&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Reason</p>
 *  ============&nbsp;&nbsp;&nbsp;==========&nbsp;&nbsp;===========================<br>
 * Jinwang Chen   2003/12/19 9:35:50  Original
 */
public class SICSVWrite {
  //ログ用のインスタンスの生成
  private static Category log=Category.getInstance(SIConfig.SILOG4J_WEBSHOP_CATEGORY_NAME);
  private static final String CONTENT_TYPE = "text/html;charset=MS932"; // 7.4.0 ST2060 修正

  //出力先のファイルオブジェクト
  private File file;
  //分割文字
  private String token=SICSVConf.SICSV_TOKEN;
  //出力タイトル
  private String[][] csvTitleAndField=null;
  //タイプ
  private String[] fieldType;
  //出力データセット
  private Collection bodyColl=new ArrayList();
  //ヘッダのデータ
  private Collection headerColl=new ArrayList();
  //フッタのデータ
  private Collection footerColl=new ArrayList();
  //出力するSQL 文
  private String sqlStatement="";

  /**
   * <b>SICSVWrite</b>
   * コンストラクタ
   * @param  なし
   * @return なし
   * @throws なし
   */
  public SICSVWrite(){}

  /**
   * <b>SICSVWrite</b>
   * コンストラクタ
   * @param  lFileName 出力先のファイル名
   * @return なし
   * @throws なし
   */
  public SICSVWrite(String lFileName){
    this.file=new File(lFileName);
  }

  /**
   * <b>SICSVWrite</b>
   * コンストラクタ
   * @param  lFile 出力先のファイルへのオブジェクト
   * @return なし
   * @throws なし
   */
  public SICSVWrite(File lFile){
    this.file =lFile;
  }

  /**
   * <b>setFileName</b>
   * 出力先のファイル名を設定します。
   * @param  lFileName 出力先のパス付けのファイル名(絶対パス)
   * @return なし
   * @throws なし
   */
  public void setFileName(String lFileName){
    this.file=new File(lFileName);
  }

  /**
   * <b>setFile</b>
   * 出力先のファイル名を設定します。
   * @param  lFile 出力先のファイルへのオブジェクト
   * @return なし
   * @throws なし
   */
  public void setFile(File lFile){
    this.file=lFile;
  }

  /**
   * <b>setCsvTitle</b>
   * 出力先のCSVタイトルとFieldの文字の配列のデータを設定します。
   * @param  lCsvTitle タイトル配列データ
   * @return なし
   * @throws なし
   */
  public void setCsvTitleAndField(String[][] lCsvTitleAndField){
    if (lCsvTitleAndField==null) return;
    this.csvTitleAndField =lCsvTitleAndField;
  }

  public void setFieldType(String[] lFieldType){
    this.fieldType =lFieldType;
  }

  /**
   * <b>setSqlStatement</b>
   * CSVデータレコードセットを出力するために、SQL 文を設定します。
   * @param  lSqlStatement 実行するSQL文
   * @return なし
   * @throws なし
   */
  public void setSqlStatement(String lSqlStatement){
    this.sqlStatement =lSqlStatement;
  }

  /**
   * <b>setBodyColl</b>
   * CSVファイルに出力するデータセットを設定します。
   * @param  lBodyColl データセット
   * @return なし
   * @throws なし
   */
  public void setBodyColl(Collection lBodyColl){
    this.bodyColl =lBodyColl;
  }

  /**
   * <b>addHeader</b>
   * 一つ文字配列のレコードをヘッダの部分に追加します。
   * @param  lRec 文字配列
   * @return なし
   * @throws なし
   */
  public void addHeader(String[] lRec){
    this.headerColl.add(lRec);
  }

  /**
   * <b>setHeaderColl</b>
   * ヘッダデータを設定します。
   * @param  lHeaderColl ヘッダセット
   * @return なし
   * @throws なし
   */
  public void setHeaderColl(Collection lHeaderColl){
    this.headerColl =lHeaderColl;
  }

  /**
   * <b>addFooter</b>
   * 一つ文字配列のレコードをフッタの部分に追加します。
   * @param  lRec 文字配列
   * @return なし
   * @throws なし
   */
  public void addFooter(String[] lRec){
    this.footerColl.add(lRec);
  }

  /**
   * <b>setFooterColl</b>
   * フッタセットを設定します。
   * @param  lFooterColl フッタセット
   * @return なし
   * @throws なし
   */
  public void setFooterColl(Collection lFooterColl){
    this.footerColl =lFooterColl;
  }

  /**
   * <b>getFileName</b>
   * CSVファイルを取得します
   * @param  なし
   * @return CSVファイル名
   * @throws なし
   */
  public String getFileName(){
    return this.file.getAbsolutePath();
  }

  /**
   * <b>getFile</b>
   * CSVファイルを取得します
   * @param  なし
   * @return CSVファイル名
   * @throws なし
   */
  public File getFile(){
    return this.file;
  }

  /**
   * <b>getCsvTitle</b>
   * CSVファイルにタイトルとFieldデータの文字配列を取得します。
   * @param  なし
   * @return タイトル配列
   * @throws なし
   */
  public String[][] getCsvTitleAndField(){
    return this.csvTitleAndField;
  }

  public String[] getFieldType(){
    return this.fieldType;
  }

  /**
   * <b>getSqlStatement</b>
   * 結果セットを生成するSQL文を取得します。
   * @param  なし
   * @return SQL文
   * @throws なし
   */
  public String getSqlStatement(){
    return this.sqlStatement;
  }

  /**
   * <b>getBodyColl</b>
   * CSVファイルに結果データセットを取得します。
   * @param  なし
   * @return データセット
   * @throws なし
   */
  public Collection getBodyColl(){
    return this.bodyColl;
  }

  /**
   * <b>constructOneLine</b>
   * 一行のレコードのデータを基づいて、一行のCSVレコードのデータを生成します。
   * @param  lLineData 一行の配列のデータ
   * @return 変換後のデータ
   * @throws なし
   */
  private String constructOneLine(String[] lLineData){
    if (lLineData==null) return "";
    StringBuffer lResultBuf=new StringBuffer();
    for (int ii=0;ii<lLineData.length;ii++){
      lResultBuf.append(this.token+constructItemData(lLineData[ii]));
    }
    if (lResultBuf.length()>0) return lResultBuf.toString().substring(this.token.length())+SICSVConf.SINEW_LINE;
    return "\"\"";
  }

  /**
   * <b>constructItemData</b>
   * 一列のデータを作成します。
   * @param  lItemData 一列のデータ
   * @return 置き換えたデータ
   * @throws なし
   */
  private String constructItemData(String lItemData){
    if (SIUtil.isNull(lItemData))return "\"\"";
    lItemData=SIStringUtil.replace(lItemData,"\r\n","");
    return "\""+SIStringUtil.replace(lItemData,"\"","\"\"") +"\"";
  }

  /**
   * <b>execute</b>
   * テーブルからデータレコードを取得して、結果セットを作製します。
   * @param  lConnection データベースへのコネンクション
   * @param  lResponse  クライアントへ
   * @return なし
   * @throws なし
   */
  public void execute(Connection lConnection,HttpServletResponse lResponse){
    execute(lConnection, lResponse, false);
  }
  public void execute(Connection lConnection,HttpServletResponse lResponse,boolean nohead){
    Statement lStatement=null;
    ResultSet lResultSet=null;
    int lColumnCount=0;
    String[] lLineData=null;
    String lFieldType="";
    log.debug("execute:sqlStatement="+sqlStatement);

    try {
      log.debug("execute:sqlStatement="+sqlStatement);
      //結果レコードセットの作製
      lStatement=lConnection.createStatement();
      lResultSet=lStatement.executeQuery(sqlStatement);
      //項目数の取得

      //タイトルのチェック
      if (csvTitleAndField==null){
        lColumnCount=lResultSet.getMetaData().getColumnCount();
        this.csvTitleAndField=new String[2][lColumnCount];
        for (int ii=0;ii<lColumnCount;ii++){
          this.csvTitleAndField[0][ii]=lResultSet.getMetaData().getColumnName(ii+1);
          this.csvTitleAndField[1][ii]=lResultSet.getMetaData().getColumnName(ii+1);
        }
      }

      this.bodyColl.clear();
      //データレコードの作製
      while (lResultSet.next()){
        //一行のデータの生成
        lLineData=new String[csvTitleAndField[1].length];
        for (int ii=0;ii<csvTitleAndField[1].length;ii++){
          if (getFieldType()!=null && getFieldType().length>ii){
            lFieldType=getFieldType()[ii];
          }else{
            lFieldType="";
          }

          if (SIUtil.isNull(lFieldType)){
            lLineData[ii]=SIUtil.CP932ToJIS(lResultSet.getString(csvTitleAndField[1][ii]));
          }else if (lFieldType.equals(SICSVConf.SICSV_DATE_TYPE)){
            lLineData[ii]=SIDBUtil.getDate(lResultSet.getTimestamp(csvTitleAndField[1][ii]));
          }else if (lFieldType.equals(SICSVConf.SICSV_DATE_TIME_TYPE)){
            lLineData[ii]=SIDBUtil.getDateTime(lResultSet.getTimestamp(csvTitleAndField[1][ii]));
          }else{
            lLineData[ii]=SIUtil.CP932ToJIS(lResultSet.getString(csvTitleAndField[1][ii]));
          }
        }
        //ファイルに一レコードのデータを書き込む
        this.bodyColl.add(lLineData);
      }
    }catch(SQLException sqle){
      sqle.printStackTrace();
    }finally{
      SIDBUtil.close(lStatement,lResultSet);
    }
    if (nohead) csvTitleAndField = null;
    //結果レコードセットをCSVファイルに出力します。
    this.executeCollection(lResponse);
  }

  /**
   * <b>executeCollection</b>
   * 指定データセットからCSVファイルに出力します。
   * @param  lResponse クライアントンへのresponse
   * @return なし
   * @throws なし
   */
  public void executeCollection(HttpServletResponse lResponse){
    executeCollection(bodyColl,lResponse);
  }

  /**
   * <b>executeCollection</b>
   * 指定データセットからCSVファイルに出力します。
   * @param  lCollection 指定するデータレコードのセット
   * @param  lResponse クライアントンへのresponse
   * @return なし
   * @throws なし
   */
  public void executeCollection(Collection lCollection,HttpServletResponse lResponse){

    if (lCollection.size()==0) return;

    //出力先のファイルの作成
    String lFileName="";
    if (file==null || SIUtil.isNull(file.getName()))lFileName=new SIDateTime().getDateTimeString()+"."+SICSVConf.SICSV_EXTENSION_NAME;
    else lFileName=file.getName();

    lResponse.setContentType(CONTENT_TYPE);
    lResponse.setContentType("text/csv");
    lResponse.setHeader("Content-Disposition","attachment; filename=\""+lFileName+"\"");

    PrintWriter lOut = null;
    try {lOut = lResponse.getWriter();}catch (IOException ex) {ex.printStackTrace();}

    //1.ヘッダのデータの出力
    int lColumnCount=headerColl.size();
    Iterator lIta=headerColl.iterator();
    if(lIta.hasNext()){
        while (lIta.hasNext()) lOut.write(constructOneLine((String[])lIta.next()));
        lOut.write(SICSVConf.SINEW_LINE);
    }

    //2.中身のデータの出力
    lColumnCount=lCollection.size();
    lIta=lCollection.iterator();

    //タイトルを設定すれば、出力します
    if (csvTitleAndField!=null)lOut.write(constructOneLine(csvTitleAndField[0]));
    while (lIta.hasNext()) lOut.write(constructOneLine((String[])lIta.next()));

    //3.フッタのデータの出力
    lColumnCount=footerColl.size();
    lIta=footerColl.iterator();
    if(lIta.hasNext()){
        lOut.write(SICSVConf.SINEW_LINE);
        while (lIta.hasNext()) lOut.write(constructOneLine((String[])lIta.next()));
    }


    lOut.close();
  }
}
