package jp.co.imtt.businesssystem.common;

import java.io.FileInputStream;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.jar.Attributes;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import org.apache.cayenne.DataRow;
import org.apache.cayenne.access.DataContext;
import org.apache.cayenne.query.SQLTemplate;
import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
import org.apache.jetspeed.services.logging.JetspeedLogger;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFDataFormat;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.turbine.util.RunData;
import org.apache.velocity.context.Context;

import com.aimluck.eip.cayenne.om.portlet.ImwTExtTimecardDetail;
import com.aimluck.eip.common.ALAbstractSelectData;
import com.aimluck.eip.common.ALDBErrorException;
import com.aimluck.eip.common.ALData;
import com.aimluck.eip.common.ALPageNotFoundException;
import com.aimluck.eip.orm.DatabaseOrmService;

public class IMXlsWriter {
  
  /** logger */
  private static final JetspeedLogger logger = JetspeedLogFactoryService
      .getLogger(IMXlsWriter.class.getName());  
  
  /** テンプレートファイル名 */
  private String template_name = "";
  /** 出力するシート名 */
  private String sheet_name = "";
  /** HTTPレスポンスに出力するファイル名 */
  private String file_name = "";
  /** 作成するエクセルファイルの実体 */
  protected HSSFWorkbook workbook = null;
  
  /**
   * エクセルファイルを作成し、帳票データを記述し、HTTPレスポンスに出力します。
   * @param rundata
   * @param context
   * @return true:成功 false:失敗
   */
  public boolean outputXls(RunData rundata, Context context){
    boolean res =  (create(rundata, context) != null)
      && write(rundata, context) && output(rundata, context);    
    return res;
  }
  
  
  /**
   * 帳票データを作成します。
   * @param rundata
   * @param context
   * @return 帳票データのリスト
   * @throws ALDBErrorException
   */
  protected List selectList(RunData rundata, Context context)
      throws ALDBErrorException{
    try {
      DataContext dataContext = DatabaseOrmService.getInstance()
          .getDataContext();
      
      String sql = getSelectQuery();
      if (sql==null || "".equals(sql)){
        return null;
      }
      SQLTemplate q = new SQLTemplate(ImwTExtTimecardDetail.class, sql, false);
      
      // 名前付きパラメータの設定
      String start_date = getStartDate(rundata);
      String end_date = getEndDate(rundata);
      String user_id = getUserId(rundata);

      HashMap map = new HashMap();
      if(start_date!=null && !"".equals(start_date)){
        map.put("ST", start_date);
      }
      if (start_date!=null && !"".equals(end_date)){
        map.put("ED", end_date);
      }
      if (user_id!=null && !"".equals(user_id)){
        map.put("UI", user_id);
      }
      if(map.size()>0){
        q.setParameters(map);
      }
      q.setFetchingDataRows(true);
      List list = dataContext.performQuery(q);
      return list;

    } catch (Exception e) {
      logger.error("Exception", e);
      return null;
    }
  }
  
  /**
   * 帳票データを作成するためのSQLクエリを記述します。
   * 出力期間の開始日は $ST 、終了日は $ED 、対象のユーザＩＤは $UI と記述すること
   * @return
   */
  protected String getSelectQuery(){
    return "";
  }
  
  /**
   * エクセルファイルを作成します。
   * @param rundata
   * @param context
   * @return 作成されたエクセルファイル
   */
  public HSSFWorkbook create(RunData rundata, Context context){
    try{
      String templateFilepath = rundata.getServletContext().getRealPath(
      "/ExcelTemplate");
      String tempfile = template_name + ".xls";
      // テンプレートファイルを元にHSSFWorkbookを作成
      FileInputStream excelFile = new FileInputStream(templateFilepath + "/"
          + tempfile);
      POIFSFileSystem fs = new POIFSFileSystem(excelFile);
      this.workbook = new HSSFWorkbook(fs);
      
      return this.workbook;
    } catch (Exception e){
      logger.error("Exception", e);
      return null;
    }
  }
  
  /**
   * エクセルに出力する項目名とフォーマットを記述します。
   * 例）項目「担当者」を標準フォーマットで、
   * 項目「作業時間」を数値フォーマットで、
   * 項目「更新日」を日付フォーマットで出力したい場合
   *   以下のように記述します。
   *   return {{"担当者","標準"},{"作業時間","数値"},{"更新日","日付"}};
   * @return
   */
  protected String[][] getXlsFormat(){
    return null;
  }
  
  /**
   * エクセルファイルに帳票データを記述します。
   * @param rundata
   * @param context
   * @return true:成功 false:失敗
   */
  public boolean write(RunData rundata, Context context){
    return write(rundata, context, this.workbook);
  }
  
  /**
   * エクセルファイルに帳票データを記述します。
   * @param rundata
   * @param context
   * @param wb
   * @return true:成功 false:失敗
   */
  public boolean write(RunData rundata, Context context, HSSFWorkbook wb){
    try {
      HSSFCell cel;
      short baseRowNumberOfMatrix = 1;
      short baseColNumberOfMatrix;
      Object value;
      
      List<DataRow> list = selectList(rundata, context);
      if (list == null || list.size() == 0){
        return true; 
      }
      String[][] format = getXlsFormat();
      if (format == null || format.length == 0){
        return true;
      }
      
      HSSFSheet sheet = wb.getSheet(sheet_name);      
      if (sheet==null) {
        return true;
      }

      //標準フォーマット
      HSSFDataFormat cdf = wb.createDataFormat();
      HSSFCellStyle  ccss0 = wb.createCellStyle();
      ccss0.setDataFormat(cdf.getFormat("@"));
      ccss0.setWrapText(true);
      ccss0.setBorderTop(HSSFCellStyle.BORDER_THIN);
      ccss0.setBorderBottom(HSSFCellStyle.BORDER_THIN);
      ccss0.setBorderLeft(HSSFCellStyle.BORDER_THIN);
      ccss0.setBorderRight(HSSFCellStyle.BORDER_THIN);
      
      //数値フォーマット
      HSSFCellStyle  ccss1 = wb.createCellStyle();
      //Chg 20110118 KOHAMA Start
      ccss1.setDataFormat(cdf.getFormat("0.00_ "));
      //Chg 20110118 KOHAMA End
      ccss1.setAlignment(HSSFCellStyle.ALIGN_RIGHT);
      ccss1.setBorderTop(HSSFCellStyle.BORDER_THIN);
      ccss1.setBorderBottom(HSSFCellStyle.BORDER_THIN);
      ccss1.setBorderLeft(HSSFCellStyle.BORDER_THIN);
      ccss1.setBorderRight(HSSFCellStyle.BORDER_THIN);
      
      //日付フォーマット
      HSSFCellStyle  ccss2 = wb.createCellStyle();
      ccss2.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/d/yy"));
      ccss2.setAlignment(HSSFCellStyle.ALIGN_RIGHT);
      ccss2.setBorderTop(HSSFCellStyle.BORDER_THIN);
      ccss2.setBorderBottom(HSSFCellStyle.BORDER_THIN);
      ccss2.setBorderLeft(HSSFCellStyle.BORDER_THIN);
      ccss2.setBorderRight(HSSFCellStyle.BORDER_THIN);
      
      //Add 20110119 KOHAMA Start
      //日付フォーマット
      HSSFCellStyle  ccss4 = wb.createCellStyle();
      ccss4.setDataFormat(HSSFDataFormat.getBuiltinFormat("#,##0"));
      ccss4.setAlignment(HSSFCellStyle.ALIGN_RIGHT);
      ccss4.setBorderTop(HSSFCellStyle.BORDER_THIN);
      ccss4.setBorderBottom(HSSFCellStyle.BORDER_THIN);
      ccss4.setBorderLeft(HSSFCellStyle.BORDER_THIN);
      ccss4.setBorderRight(HSSFCellStyle.BORDER_THIN);
      //Add 20110119 KOHAMA Start

      //マーク用フォーマット
      HSSFCellStyle  ccss3 = wb.createCellStyle();
      
      for(DataRow drow : list){
        HSSFRow mRow = sheet.createRow(baseRowNumberOfMatrix ++);
        baseColNumberOfMatrix = 0;
        
        for (int i = 0; i < format.length; i++) {
          cel = mRow.createCell(baseColNumberOfMatrix++);
          cel.setEncoding(HSSFCell.ENCODING_UTF_16);
          if (format[i].length > 1) {
            value = drow.get(format[i][0]);
            if ("標準".equals(format[i][1])) {
              cel.setCellStyle(ccss0);
              if (value != null)
                cel.setCellValue(value.toString());
            } else if ("数値".equals(format[i][1])) {
              cel.setCellStyle(ccss1);
              if (value != null)
                //Chg 20110118 KOHAMA Start
                //cel.setCellValue(value.toString());
                cel.setCellValue(Double.parseDouble(value.toString()));
                //Chg 20110118 KOHAMA End
            } else if ("日付".equals(format[i][1])) {
              cel.setCellStyle(ccss2);
              if (value != null)
                cel.setCellValue((Date) value);
            //Add 20110119 KOHAMA Start
            } else if ("通貨".equals(format[i][1])) {
              cel.setCellStyle(ccss4);
              if (value != null)
                cel.setCellValue(Double.parseDouble(value.toString()));
            //Add 20110119 KOHAMA End
            }
          }
        }
      }
      return true;
    } catch (Exception e){
      logger.error("Exception", e);
      return false;
    }
  }
  
  /**
   * HTTPレスポンスにエクセルファイルを出力します。
   * @param rundata
   * @param context
   * @return true:成功 false:失敗
   */
  public boolean output(RunData rundata, Context context){
    return output(rundata, context, this.workbook);
  }

  /**
   * HTTPレスポンスにエクセルファイルを出力します。
   * @param rundata
   * @param context
   * @param wb
   * @return true:成功 false:失敗
   */
  public boolean output(RunData rundata, Context context, HSSFWorkbook wb) {
    try {
      ServletOutputStream out = null;
      HttpServletResponse response = rundata.getResponse();

      String fileName_full = URLEncoder.encode(file_name + ".xls",
          "utf-8");
      response.setHeader("Content-disposition", "attachment; filename=\""
          + fileName_full + "\"");
      response.setContentType("application/msexcel");

      response.setHeader("Cache-Control", "private");
      response.setHeader("Pragma", "private");
      response.setHeader("Expires", "-1");

      // ファイル内容を直接ストリームへ出力
      out = response.getOutputStream();
      wb.write(out);

      out.flush();
      return true;
    } catch (Exception e) {
      logger.error("Exception", e);
      return false;
    }
  }
  
  /**
   * 帳票出力期間の開始日を返します。
   * rundata中に以下のパラメータを渡すこと
   * start_year = 帳票出力期間の開始年, start_month = 帳票出力期間の開始月 
   * @param rundata
   * @return 帳票出力期間の開始日
   */
  protected String getStartDate(RunData rundata){
    try{
      String start_year = rundata.getParameters().get("start_year");
      String start_month = rundata.getParameters().get("start_month");
      if(start_year == null || start_month == null
          || "".equals(start_year) || "".equals(start_month)){
        return "";
      }
      int year = Integer.valueOf(start_year);
      int month = Integer.valueOf(start_month);
    
      Calendar start_date = Calendar.getInstance(); 
      // 取得日をセット
      start_date.set(year, month - 1, 1);
      Date date = start_date.getTime();
      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
      return sdf.format(date);
    } catch (Exception e){
      return "";
    }  
  }
  
  /**
   * 帳票出力期間の終了日を返します。
   * rundata中に以下のパラメータを渡すこと
   * end_year = 帳票出力期間の終了年, end_month = 帳票出力期間の終了月
   * @param rundata
   * @return 帳票出力期間の終了日
   */
  protected String getEndDate(RunData rundata){
    try{
      String end_year = rundata.getParameters().get("end_year");
      String end_month = rundata.getParameters().get("end_month");
      if(end_year == null || end_month == null 
          || "".equals(end_year) || "".equals(end_month)){
        return "";
      }
      int year = Integer.valueOf(end_year);
      int month = Integer.valueOf(end_month);
    
      Calendar end_date = Calendar.getInstance(); 
      // 取得日の来月をセット
      end_date.set(year, month + 1 - 1, 1);
      Date date = end_date.getTime();
      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
      
      return sdf.format(date);
    } catch (Exception e){
      return "";
    }  
  }
  
  /**
   * 対象のユーザＩＤを取得します。
   * @param rundata
   * @return　対象のユーザＩＤ
   */
  protected String getUserId(RunData rundata){
    return rundata.getParameters().get("target_user_id");
  }
  
  /**
   * テンプレートファイル名をセットします。
   * @param name
   */
  public void setTemplateName(String name){
    template_name = name;
  }
  
  /**
   * シート名をセットします。
   * @param name
   */
  public void setSheetName(String name){
    sheet_name = name;
  }
  
  /**
   * 出力ファイル名をセットします。
   * @param name
   */
  public void setFileName(String name){
    file_name = name;
  }

  /**
   * シート名を取得します。
   * @return
   */
  public String getSheetName(){
    return sheet_name;
  }
    
  /**
   * テンプレートファイル名を取得します。
   * @return
   */
  public String getTemplateName(){
    return template_name;
  }  

  
  /**
   * 出力ファイル名を取得します。
   * @return
   */
  public String getFileName(){
    return file_name;
  }
 
  
}
