/*
 * Aipo is a groupware program developed by Aimluck,Inc.
 * Copyright (C) 2004-2008 Aimluck,Inc.
 * http://aipostyle.com/
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package com.aimluck.eip.common;

import java.util.ArrayList;
import java.util.List;
import java.util.jar.Attributes;

import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;
import org.apache.cayenne.query.SelectQuery;
import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
import org.apache.jetspeed.services.logging.JetspeedLogger;
import org.apache.turbine.services.TurbineServices;
import org.apache.turbine.util.RunData;
import org.apache.velocity.context.Context;

import com.aimluck.commons.field.ALStringField;
import com.aimluck.eip.modules.actions.common.ALAction;
import com.aimluck.eip.services.accessctl.ALAccessControlConstants;
import com.aimluck.eip.services.accessctl.ALAccessControlFactoryService;
import com.aimluck.eip.services.accessctl.ALAccessControlHandler;
import com.aimluck.eip.util.ALCommonUtils;
import com.aimluck.eip.util.ALEipUtils;

/**
 * 検索データを管理するための抽象クラスです。 <br />
 * 
 */
public abstract class ALAbstractSelectData implements ALData {

  /** logger */
  private static final JetspeedLogger logger = JetspeedLogFactoryService
      .getLogger(ALAbstractSelectData.class.getName());

  /** 表示行数 */
  private int rows_num = 10;

  /** 表示文字数 */
  private int strlen = 0;

  /** 開始位置 */
  private int start;

  /** 総件数 */
  private int count;

  /** 総ページ数 */
  private int pages_num = 1;

  /** 現在のページ */
  private int current_page = 1;

  /** 一覧データ */
  private ArrayList list;

  /** 詳細データ */
  private Object data;

  /** 現在のソート */
  private String current_sort;

  /** 現在のソートタイプ （asc:昇順、desc:降順） */
  private String current_sort_type;

  /** 現在のフィルタ */
  protected String current_filter;

  /** 現在のフィルタタイプ */
  protected String current_filter_type;

  /** アクセス権限の有無 */
  protected boolean hasAuthority;

  protected final String LIST_SORT_STR = new StringBuffer().append(
      this.getClass().getName()).append(ALEipConstants.LIST_SORT).toString();

  protected final String LIST_SORT_TYPE_STR = new StringBuffer().append(
      this.getClass().getName()).append(ALEipConstants.LIST_SORT_TYPE)
      .toString();

  protected final String LIST_FILTER_STR = new StringBuffer().append(
      this.getClass().getName()).append(ALEipConstants.LIST_FILTER).toString();

  protected final String LIST_FILTER_TYPE_STR = new StringBuffer().append(
      this.getClass().getName()).append(ALEipConstants.LIST_FILTER_TYPE)
      .toString();

  protected final String LIST_INDEX_STR = new StringBuffer().append(
      this.getClass().getName()).append(ALEipConstants.LIST_INDEX).toString();;

  /**
   * 
   * 
   * @see com.aimluck.eip.common.ALData#initField()
   */
  public void initField() {

  }

  /**
   * 初期化処理を行います。
   * 
   * @param action
   * @param rundata
   * @param context
   */
  public void init(ALAction action, RunData rundata, Context context)
      throws ALPageNotFoundException, ALDBErrorException {
    if (ALEipUtils.isMatch(rundata, context)) {
      // ENTITY ID をセッション変数に設定
      if (rundata.getParameters().containsKey(ALEipConstants.ENTITY_ID)) {
        ALEipUtils.setTemp(rundata, context, ALEipConstants.ENTITY_ID, rundata
            .getParameters().getString(ALEipConstants.ENTITY_ID));
      }

      if (rundata.getParameters().containsKey(ALEipConstants.LIST_SORT)) {
        ALEipUtils.setTemp(rundata, context, LIST_SORT_STR, rundata
            .getParameters().getString(ALEipConstants.LIST_SORT));
      }

      if (rundata.getParameters().containsKey(ALEipConstants.LIST_SORT_TYPE)) {
        ALEipUtils.setTemp(rundata, context, LIST_SORT_TYPE_STR, rundata
            .getParameters().getString(ALEipConstants.LIST_SORT_TYPE));
      }

      if (rundata.getParameters().containsKey(ALEipConstants.LIST_START)) {
        current_page = rundata.getParameters()
            .getInt(ALEipConstants.LIST_START);
      }

      if (rundata.getParameters().containsKey(ALEipConstants.LIST_FILTER)) {
        ALEipUtils.setTemp(rundata, context, LIST_FILTER_STR, rundata
            .getParameters().getString(ALEipConstants.LIST_FILTER));
      }

      if (rundata.getParameters().containsKey(ALEipConstants.LIST_FILTER_TYPE)) {
        ALEipUtils.setTemp(rundata, context, LIST_FILTER_TYPE_STR, rundata
            .getParameters().getString(ALEipConstants.LIST_FILTER_TYPE));
      }
    }
  }

  /**
   * 一覧表示します。
   * 
   * @param action
   * @param rundata
   * @param context
   * @return TRUE 成功 FASLE 失敗
   */
  public boolean doViewList(ALAction action, RunData rundata, Context context) {
    try {
      init(action, rundata, context);
      doCheckAclPermission(rundata, context,
          ALAccessControlConstants.VALUE_ACL_LIST);
      action.setMode(ALEipConstants.MODE_LIST);
      List aList = selectList(rundata, context);
      if (aList != null) {
        list = new ArrayList();
        Object obj = null;
        int size = aList.size();
        for (int i = 0; i < size; i++) {
          obj = getResultData(aList.get(i));
          if (obj != null) {
            list.add(obj);
          }
        }
      }
      action.setResultData(this);
      action.putData(rundata, context);
      ALEipUtils.removeTemp(rundata, context, ALEipConstants.ENTITY_ID);
      return (list != null);
    } catch (ALPermissionException e) {
      ALEipUtils.redirectPermissionError(rundata);
      return false;
    } catch (ALPageNotFoundException e) {
      ALEipUtils.redirectPageNotFound(rundata);
      return false;
    } catch (ALDBErrorException e) {
      ALEipUtils.redirectDBError(rundata);
      return false;
    }

  }

  /**
   * 一覧表示のためのデータを取得します。
   * 
   * @param action
   * @param rundata
   * @param context
   * @return TRUE 成功 FASLE 失敗
   */
  public boolean doSelectList(ALAction action, RunData rundata, Context context) {
    try {
      init(action, rundata, context);
      doCheckAclPermission(rundata, context,
          ALAccessControlConstants.VALUE_ACL_LIST);
      List aList = selectList(rundata, context);
      if (aList != null) {
        list = new ArrayList();
        int size = aList.size();
        for (int i = 0; i < size; i++) {
          list.add(getResultData(aList.get(i)));
        }
      }
      return (list != null);
    } catch (ALPermissionException e) {
      ALEipUtils.redirectPermissionError(rundata);
      return false;
    } catch (ALPageNotFoundException e) {
      ALEipUtils.redirectPageNotFound(rundata);
      return false;
    } catch (ALDBErrorException e) {
      ALEipUtils.redirectDBError(rundata);
      return false;
    }
  }

  /**
   * 詳細表示します。
   * 
   * @param action
   * @param rundata
   * @param context
   * @return TRUE 成功 FASLE 失敗
   */
  public boolean doViewDetail(ALAction action, RunData rundata, Context context) {
    try {
      init(action, rundata, context);
      doCheckAclPermission(rundata, context,
          ALAccessControlConstants.VALUE_ACL_DETAIL);
      action.setMode(ALEipConstants.MODE_DETAIL);
      Object obj = selectDetail(rundata, context);
      if (obj != null) {
        data = getResultDataDetail(obj);
      }
      action.setResultData(this);
      action.putData(rundata, context);
      return (data != null);
    } catch (ALPermissionException e) {
      ALEipUtils.redirectPermissionError(rundata);
      return false;
    } catch (ALPageNotFoundException e) {
      ALEipUtils.redirectPageNotFound(rundata);
      return false;
    } catch (ALDBErrorException e) {
      ALEipUtils.redirectDBError(rundata);
      return false;
    }
  }

  /**
   * ページング結果のリストを取得します。
   * 
   * @param records
   *            検索結果
   */
  protected void buildSelectQueryForListView(SelectQuery query) {
    query.setPageSize(getRowsNum());
  }

  /**
   * ページング結果のリストを取得します。
   * 
   * @param records
   *            検索結果
   */
  protected List buildPaginatedList(List records) {
    List list = new ArrayList();

    setPageParam(records.size());

    int size = records.size();
    int end = (start + rows_num <= size) ? start + rows_num : size;
    for (int i = start; i < end; i++) {
      list.add(records.get(i));
    }

    return list;
  }

  /**
   * 
   * @param cnt
   */
  protected void setPageParam(int cnt) {
    // 総件数
    count = cnt;
    // 総ページ数
    pages_num = ((int) (Math.ceil(count / (double) rows_num)));

    // 開始
    if ((pages_num > 0) && (pages_num < current_page)) {
      current_page = pages_num;
    }
    start = rows_num * (current_page - 1);
  }

  /**
   * ソート用の <code>SelectQuery</code> を構築します。
   * 
   * @param crt
   * @return
   */
  protected SelectQuery buildSelectQueryForListViewSort(SelectQuery query,
      RunData rundata, Context context) {
    String sort = ALEipUtils.getTemp(rundata, context, LIST_SORT_STR);
    String sort_type = ALEipUtils.getTemp(rundata, context, LIST_SORT_TYPE_STR);
    String crt_key = null;

    Attributes map = getColumnMap();
    if (sort == null)
      return query;
    crt_key = map.getValue(sort);
    if (crt_key == null)
      return query;
    if (sort_type != null
        && ALEipConstants.LIST_SORT_TYPE_DESC.equals(sort_type)) {
      query.addOrdering(crt_key, false);
    } else {
      query.addOrdering(crt_key, true);
      sort_type = ALEipConstants.LIST_SORT_TYPE_ASC;
    }
    current_sort = sort;
    current_sort_type = sort_type;
    return query;
  }

  /**
   * フィルタ用の <code>SelectQuery</code> を構築します。
   * 
   * @param crt
   * @param rundata
   * @param context
   * @return
   */
  protected SelectQuery buildSelectQueryForFilter(SelectQuery query,
      RunData rundata, Context context) {
    String filter = ALEipUtils.getTemp(rundata, context, LIST_FILTER_STR);
    String filter_type = ALEipUtils.getTemp(rundata, context,
        LIST_FILTER_TYPE_STR);
    String crt_key = null;
    Attributes map = getColumnMap();
    if (filter == null || filter_type == null || filter.equals(""))
      return query;
    crt_key = map.getValue(filter_type);
    if (crt_key == null)
      return query;

    Expression exp = ExpressionFactory.matchDbExp(crt_key, filter);
    query.andQualifier(exp);
    current_filter = filter;
    current_filter_type = filter_type;
    return query;
  }

  /**
   * 表示する項目数を設定します。
   * 
   * @param num
   */
  public void setRowsNum(int num) {
    if (num >= 1) {
      rows_num = num;
    }
  }

  /**
   * 表示文字数を設定します。
   * 
   * @param num
   */
  public void setStrLength(int num) {
    if (num >= 0) {
      strlen = num;
    }
  }

  /**
   * 表示文字数を取得します。
   * 
   * @return
   */
  public int getStrLength() {
    return strlen;
  }

  /**
   * 表示する項目数を取得します。
   * 
   * @return
   */
  public int getRowsNum() {
    return rows_num;
  }

  /**
   * 総件数を取得します。
   * 
   * @return
   */
  public int getCount() {
    return count;
  }

  /**
   * 総ページ数を取得します。
   * 
   * @return
   */
  public int getPagesNum() {
    return pages_num;
  }

  /**
   * 現在表示されているページを取得します。
   * 
   * @return
   */
  public int getCurrentPage() {
    return current_page;
  }

  /**
   * 一覧データを取得します。
   * 
   * @return
   */
  public ArrayList getList() {
    return list;
  }

  /**
   * 詳細データを取得します。
   * 
   * @return
   */
  public Object getDetail() {
    return data;
  }

  /**
   * 
   * @return
   */
  public String getCurrentSort() {
    return current_sort;
  }

  /**
   * 
   * @return
   */
  public String getCurrentSortType() {
    return current_sort_type;
  }

  /**
   * 
   * @return
   */
  public String getCurrentFilter() {
    return current_filter;
  }

  /**
   * 
   * @return
   */
  public String getCurrentFilterType() {
    return current_filter_type;
  }

  /**
   * 一覧データを取得する抽象メソッドです。
   * 
   * @param rundata
   * @param context
   * @return
   */
  protected abstract List selectList(RunData rundata, Context context)
      throws ALPageNotFoundException, ALDBErrorException;

  /**
   * 詳細データを取得する抽象メソッドです。
   * 
   * @param rundata
   * @param context
   * @return
   */
  protected abstract Object selectDetail(RunData rundata, Context context)
      throws ALPageNotFoundException, ALDBErrorException;

  /**
   * ResultDataを取得する抽象メソッドです。（一覧データ）
   * 
   * @param obj
   * @return
   */
  protected abstract Object getResultData(Object obj)
      throws ALPageNotFoundException, ALDBErrorException;

  /**
   * ResultDataを取得する抽象メソッドです。（詳細データ）
   * 
   * @param obj
   * @return
   */
  protected abstract Object getResultDataDetail(Object obj)
      throws ALPageNotFoundException, ALDBErrorException;

  /**
   * 
   * @return
   */
  protected abstract Attributes getColumnMap();

  /**
   * アクセス権限をチェックします。
   * 
   * @return
   */
  protected boolean doCheckAclPermission(RunData rundata, Context context,
      int defineAclType) throws ALPermissionException {

    if (defineAclType == 0) {
      return true;
    }

    String pfeature = getAclPortletFeature();
    if (pfeature == null || "".equals(pfeature)) {
      return true;
    }

    ALAccessControlFactoryService aclservice = (ALAccessControlFactoryService) ((TurbineServices) TurbineServices
        .getInstance()).getService(ALAccessControlFactoryService.SERVICE_NAME);
    ALAccessControlHandler aclhandler = aclservice.getAccessControlHandler();

    hasAuthority = aclhandler.hasAuthority(ALEipUtils.getUserId(rundata),
        pfeature, defineAclType);

    if (!hasAuthority) {
      throw new ALPermissionException();
    }

    return true;
  }

  /**
   * アクセス権限用メソッド。<br />
   * アクセス権限の有無を返します。
   * 
   * @return
   */
  public boolean hasAuthority() {
    return hasAuthority;
  }

  /**
   * アクセス権限チェック用メソッド。<br />
   * アクセス権限の機能名を返します。
   * 
   * @return
   */
  public String getAclPortletFeature() {
    return null;
  }

  /**
   * @return
   */
  public int getStart() {
    return start;
  }

  public String getStringCR(ALStringField field) {
    return ALCommonUtils.replaceToAutoCR(field.toString());
  }

}
