/*
 * Copyright (C) 2003-2007 eXo Platform SAS.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Affero 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 org.exoplatform.ecm.webui.component.explorer.search;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.ResourceBundle;

import org.exoplatform.commons.utils.ISO8601;
import org.exoplatform.ecm.jcr.model.Preference;
import org.exoplatform.ecm.webui.component.explorer.UIJCRExplorer;
import org.exoplatform.ecm.webui.form.UIFormInputSetWithAction;
import org.exoplatform.ecm.webui.selector.UISelectable;
import org.exoplatform.web.application.RequestContext;
import org.exoplatform.webui.config.annotation.ComponentConfig;
import org.exoplatform.webui.core.UIPopupWindow;
import org.exoplatform.webui.core.model.SelectItemOption;
import org.exoplatform.webui.form.UIFormDateTimeInput;
import org.exoplatform.webui.form.UIFormSelectBox;
import org.exoplatform.webui.form.UIFormStringInput;
import org.exoplatform.webui.form.input.UICheckBoxInput;

@ComponentConfig(
    template =  "app:/groovy/webui/component/explorer/search/UIConstraintsForm.gtmpl"
)
public class UIConstraintsForm extends UIFormInputSetWithAction implements UISelectable{

  final static public String OPERATOR = "operator" ;
  final static public String TIME_OPTION = "timeOpt" ;
  final static public String PROPERTY1 = "property1" ;
  final static public String PROPERTY2 = "property2" ;
  final static public String PROPERTY3 = "property3" ;
  final static public String CONTAIN_EXACTLY = "containExactly" ;
  final static public String CONTAIN = "contain" ;
  final static public String NOT_CONTAIN = "notContain" ;
  final static public String START_TIME = "startTime" ;
  final static public String END_TIME = "endTime" ;
  final static public String DOC_TYPE = "docType" ;
  final static public String CATEGORY_TYPE = "categoryType" ;
  final static public String AND_OPERATION = "and" ;
  final static public String OR_OPERATION = "or" ;
  final static public String CREATED_DATE = "CREATED" ;
  final static public String MODIFIED_DATE = "MODIFIED" ;
  final static public String EXACTLY_PROPERTY = "exactlyPro" ;
  final static public String CONTAIN_PROPERTY = "containPro" ;
  final static public String NOT_CONTAIN_PROPERTY = "notContainPro" ;
  final static public String DATE_PROPERTY = "datePro" ;
  final static public String NODETYPE_PROPERTY = "nodetypePro" ;
  final static public String CATEGORY_PROPERTY = "categoryPro" ;
  final static public String SPLIT_REGEX = "/|\\s+|:" ;
  final static public String DATETIME_REGEX =
    "^(\\d{1,2}\\/\\d{1,2}\\/\\d{1,4})\\s*(\\s+\\d{1,2}:\\d{1,2}:\\d{1,2})?$" ;

  private String              virtualDateQuery_;

  private String              _CREATED_DATE;

  private String              _MODIFIED_DATE;

  private String              _AND_OPERATION;

  private String              _OR_OPERATION;

  public UIConstraintsForm(String name) throws Exception {
    super(name);
    RequestContext context = RequestContext.getCurrentInstance();
    ResourceBundle res = context.getApplicationResourceBundle();
    _AND_OPERATION = res.getString("UIConstraintForm.label.and");
    _OR_OPERATION = res.getString("UIConstraintForm.label.or");
    _CREATED_DATE = res.getString("UIConstraintForm.label.created");
    _MODIFIED_DATE = res.getString("UIConstraintForm.label.modified");
    setActions(new String[] {"Add", "Cancel"}, null) ;
    List<SelectItemOption<String>> typeOperation = new ArrayList<SelectItemOption<String>>() ;
    typeOperation.add(new SelectItemOption<String>(_AND_OPERATION, AND_OPERATION));
    typeOperation.add(new SelectItemOption<String>(_OR_OPERATION, OR_OPERATION));
    addUIFormInput(new UIFormSelectBox(OPERATOR, OPERATOR, typeOperation)) ;

    addUIFormInput(new UICheckBoxInput(EXACTLY_PROPERTY, EXACTLY_PROPERTY, null)) ;
    addUIFormInput(new UIFormStringInput(PROPERTY1, PROPERTY1, null)) ;
    addUIFormInput(new UIFormStringInput(CONTAIN_EXACTLY, CONTAIN_EXACTLY, null)) ;

    addUIFormInput(new UICheckBoxInput(CONTAIN_PROPERTY, CONTAIN_PROPERTY, null)) ;
    addUIFormInput(new UIFormStringInput(PROPERTY2, PROPERTY2, null)) ;
    addUIFormInput(new UIFormStringInput(CONTAIN, CONTAIN, null)) ;

    addUIFormInput(new UICheckBoxInput(NOT_CONTAIN_PROPERTY, NOT_CONTAIN_PROPERTY, null)) ;
    addUIFormInput(new UIFormStringInput(PROPERTY3, PROPERTY3, null)) ;
    addUIFormInput(new UIFormStringInput(NOT_CONTAIN, NOT_CONTAIN, null)) ;


    addUIFormInput(new UICheckBoxInput(DATE_PROPERTY, DATE_PROPERTY, null)) ;
    List<SelectItemOption<String>> dateOperation = new ArrayList<SelectItemOption<String>>() ;
    dateOperation.add(new SelectItemOption<String>(_CREATED_DATE, CREATED_DATE));
    dateOperation.add(new SelectItemOption<String>(_MODIFIED_DATE, MODIFIED_DATE));
    addUIFormInput(new UIFormSelectBox(TIME_OPTION, TIME_OPTION, dateOperation)) ;
    UIFormDateTimeInput uiFromDate = new UIFormDateTimeInput(START_TIME, START_TIME, null) ;
    uiFromDate.setDisplayTime(true) ;
    addUIFormInput(uiFromDate) ;
    UIFormDateTimeInput uiToDate = new UIFormDateTimeInput(END_TIME, END_TIME, null) ;
    uiToDate.setDisplayTime(true) ;
    addUIFormInput(uiToDate) ;
    addUIFormInput(new UICheckBoxInput(NODETYPE_PROPERTY, NODETYPE_PROPERTY, null)) ;
    addUIFormInput(new UIFormStringInput(DOC_TYPE, DOC_TYPE, null)) ;
    addUIFormInput(new UICheckBoxInput(CATEGORY_PROPERTY, CATEGORY_PROPERTY, null)) ;
    addUIFormInput(new UIFormStringInput(CATEGORY_TYPE, CATEGORY_TYPE, null)) ;
  }

  private String getContainQueryString(String property, String type, boolean isContain) {
    String value = getUIStringInput(type).getValue() ;
    if(value == null) return "" ;
    if(value.trim().length() > 0) {
      if(isContain) return " jcr:contains(@" + property.trim() + ", '"+ value.trim() + "')" ;
      return " fn:not(jcr:contains(@" + property.trim() + ", '"+ value.trim() + "'))" ;
    }
    return "" ;
  }

  private String getContainSQLQueryString(String property, String type, boolean isContain) {
    String value = getUIStringInput(type).getValue();
    if(value == null) return "";
    if(value.trim().length() > 0) {
      if(isContain) return " CONTAINS(" + property.trim() + ", '"+ value.trim() + "')";
      return " NOT CONTAINS(" + property.trim() + ", '"+ value.trim() + "')";
    }
    return "";
  }

  private String getDateTimeQueryString(String beforeDate, String afterDate, String type) {
    Calendar bfDate = getUIFormDateTimeInput(START_TIME).getCalendar() ;
    if(bfDate==null) return "";
    if (afterDate != null && afterDate.trim().length() > 0) {
      Calendar afDate = getUIFormDateTimeInput(END_TIME).getCalendar();
      if (type.equals(CREATED_DATE)) {
        virtualDateQuery_ = "(documents created from '" + beforeDate
            + "') and (documents created to '" + afterDate + "')";
        return "@exo:dateCreated >= xs:dateTime('" + ISO8601.format(bfDate)
            + "') and @exo:dateCreated <= xs:dateTime('" + ISO8601.format(afDate) + "')";
      } else if (type.equals(MODIFIED_DATE)) {
        virtualDateQuery_ = "(documents modified from '" + beforeDate
            + "') and (documents modified to '" + afterDate + "')";
        return "@exo:dateModified >= xs:dateTime('" + ISO8601.format(bfDate)
            + "') and @exo:dateModified <= xs:dateTime('" + ISO8601.format(afDate) + "')";
      }
    } else {
      if(type.equals(CREATED_DATE)) {
        virtualDateQuery_ = "(documents created from '"+beforeDate+"')" ;
        return "@exo:dateCreated >= xs:dateTime('"+ISO8601.format(bfDate)+"')" ;
      } else if(type.equals(MODIFIED_DATE)) {
        virtualDateQuery_ = "(documents modified from '"+beforeDate+"')" ;
        return "@exo:dateModified >= xs:dateTime('"+ISO8601.format(bfDate)+"')" ;
      }
    }
    return "" ;
  }

  private String getDateTimeSQLQueryString(String beforeDate, String afterDate, String type) {
    Calendar bfDate = getUIFormDateTimeInput(START_TIME).getCalendar();
    if(bfDate==null) return "";
    if (afterDate != null && afterDate.trim().length() > 0) {
      Calendar afDate = getUIFormDateTimeInput(END_TIME).getCalendar();
      if (type.equals(CREATED_DATE)) {
        virtualDateQuery_ = "(documents created from '" + beforeDate
            + "') and (documents created to '" + afterDate + "')";
        return "exo:dateCreated >= TIMESTAMP '" + ISO8601.format(bfDate)
            + "' and exo:dateCreated <= TIMESTAMP '" + ISO8601.format(afDate) + "'";
      } else if (type.equals(MODIFIED_DATE)) {
        virtualDateQuery_ = "(documents modified from '" + beforeDate
            + "') and (documents modified to '" + afterDate + "')";
        return "exo:dateModified >= TIMESTAMP '" + ISO8601.format(bfDate)
            + "' and exo:dateModified <= TIMESTAMP '" + ISO8601.format(afDate) + "'";
      }
    } else {
      if(type.equals(CREATED_DATE)) {
        virtualDateQuery_ = "(documents created from '"+beforeDate+"')";
        return "exo:dateCreated >= TIMESTAMP '"+ISO8601.format(bfDate)+"'";
      } else if(type.equals(MODIFIED_DATE)) {
        virtualDateQuery_ = "(documents modified from '"+beforeDate+"')";
        return "exo:dateModified >= TIMESTAMP '"+ISO8601.format(bfDate)+"'";
      }
    }
    return "" ;
  }

  private String getNodeTypeQueryString(String nodeTypes) {
    StringBuffer advanceQuery = new StringBuffer();
    String[] arrNodeTypes = {};
    if (nodeTypes.indexOf(",") > -1)
      arrNodeTypes = nodeTypes.split(",");
    if (arrNodeTypes.length > 0) {
      for (String nodeType : arrNodeTypes) {
        if (advanceQuery.length() == 0)
          advanceQuery.append("@jcr:primaryType = '").append(nodeType).append("'");
        else
          advanceQuery.append(" ")
                      .append(OR_OPERATION)
                      .append(" ")
                      .append("@jcr:primaryType = '")
                      .append(nodeType)
                      .append("'");
      }
    } else {
      advanceQuery.append("@jcr:primaryType = '").append(nodeTypes).append("'");
    }
    return advanceQuery.toString();
  }

  private String getNodeTypeSQLQueryString(String nodeTypes) {
    StringBuffer advanceQuery = new StringBuffer();
    String[] arrNodeTypes = {};
    if (nodeTypes.indexOf(",") > -1)
      arrNodeTypes = nodeTypes.split(",");
    if (arrNodeTypes.length > 0) {
      for (String nodeType : arrNodeTypes) {
        if (advanceQuery.length() == 0)
          advanceQuery.append("jcr:primaryType = '").append(nodeType).append("'");
        else
          advanceQuery.append(" ")
                      .append(OR_OPERATION)
                      .append(" ")
                      .append("jcr:primaryType = '")
                      .append(nodeType)
                      .append("'");
      }
    } else {
      advanceQuery.append("jcr:primaryType = '").append(nodeTypes).append("'");
    }
    return advanceQuery.toString();
  }

  /**
   * Create query string for category
   * @param categoryPath
   * @return
   */
  private String getCategoryQueryString(String categoryPath) {
    if (categoryPath == null || categoryPath.length() == 0) return "";
    return ("@exo:category = '" + categoryPath + "'");
  }

  private String getCategorySQLQueryString(String categoryPath) {
    if (categoryPath == null || categoryPath.length() == 0) return "";
    return ("exo:category = '" + categoryPath + "'");
  }

  void addConstraint(int opt) throws Exception {
    String advanceQuery = "" ;
    String property ;
    virtualDateQuery_ = null ;
    UISimpleSearch uiSimpleSearch = this.getParent();
    UIJCRExplorer uiExplorer = uiSimpleSearch.getAncestorOfType(UIJCRExplorer.class);
    Preference pref = uiExplorer.getPreference();
    String queryType = pref.getQueryType();
    switch (opt) {
      case 0:
        property = getUIStringInput(PROPERTY1).getValue() ;
        String value = getUIStringInput(CONTAIN_EXACTLY).getValue() ;
        if (queryType.equals(Preference.XPATH_QUERY))
          advanceQuery = "@" + property + " = '" + value.trim() + "'" ;
        else
          advanceQuery = " CONTAINS(" + property + ", '" + value.trim() + "')" ;
        break;
      case 1:
        property = getUIStringInput(PROPERTY2).getValue() ;
        if (queryType.equals(Preference.XPATH_QUERY))
          advanceQuery = getContainQueryString(property, CONTAIN, true);
        else
          advanceQuery = getContainSQLQueryString(property, CONTAIN, true);
        break;
      case 2:
        property = getUIStringInput(PROPERTY3).getValue();
        if (queryType.equals(Preference.XPATH_QUERY))
          advanceQuery = getContainQueryString(property, NOT_CONTAIN, false);
        else
          advanceQuery = getContainSQLQueryString(property, NOT_CONTAIN, false);
        break;
      case 3:
        String fromDate = getUIFormDateTimeInput(START_TIME).getValue() ;
        String toDate = getUIFormDateTimeInput(END_TIME).getValue() ;
        String type = getUIFormSelectBox(TIME_OPTION).getValue() ;
        if (queryType.equals(Preference.XPATH_QUERY))
          advanceQuery = getDateTimeQueryString(fromDate, toDate, type);
        else
          advanceQuery = getDateTimeSQLQueryString(fromDate, toDate, type);
        break ;
      case 4:
        property = getUIStringInput(DOC_TYPE).getValue();
        if (queryType.equals(Preference.XPATH_QUERY))
          advanceQuery = getNodeTypeQueryString(property);
        else
          advanceQuery = getNodeTypeSQLQueryString(property);
        break;
      case 5:
        property = getUIStringInput(CATEGORY_TYPE).getValue();
        if (queryType.equals(Preference.XPATH_QUERY))
          advanceQuery = getCategoryQueryString(property);
        else
          advanceQuery = getCategorySQLQueryString(property);
        String firstOperator = uiSimpleSearch.getUIStringInput(UISimpleSearch.FIRST_OPERATOR).getValue();
        if (!uiSimpleSearch.getCategoryPathList().contains(property) && firstOperator.equals("and"))
          uiSimpleSearch.getCategoryPathList().add(property);
        break;
      default:
        break;
    }
    uiSimpleSearch.updateAdvanceConstraint(advanceQuery,
                                           getUIFormSelectBox(OPERATOR).getValue(),
                                           virtualDateQuery_);
  }

  void resetConstraintForm() {
    reset();
    getUICheckBoxInput(EXACTLY_PROPERTY).setChecked(false);
    getUICheckBoxInput(CONTAIN_PROPERTY).setChecked(false);
    getUICheckBoxInput(NOT_CONTAIN_PROPERTY).setChecked(false);
    getUICheckBoxInput(DATE_PROPERTY).setChecked(false);
    getUICheckBoxInput(NODETYPE_PROPERTY).setChecked(false);
    getUICheckBoxInput(CATEGORY_PROPERTY).setChecked(false);
  }

  boolean isValidDateTime(String dateTime) {
    String[] arr = dateTime.split(SPLIT_REGEX, 7) ;
    int valid = Integer.parseInt(arr[0]) ;
    if(valid < 1 || valid > 12) return false;
    Calendar date = new GregorianCalendar(Integer.parseInt(arr[2]), valid - 1, 1) ;
    if(Integer.parseInt(arr[1]) > date.getActualMaximum(Calendar.DAY_OF_MONTH)) return false;
    if (arr.length > 3
        && (Integer.parseInt(arr[3]) > 23 || Integer.parseInt(arr[4]) > 59 || Integer.parseInt(arr[5]) > 59))
      return false;
    return true;
  }

  /**
   * Set category to text box and closeof choose category popup window
   * @param selectField: name of text field input
   * @param value: value of chosen category
   * @throws Exception
   */
  public void doSelect(String selectField, Object value) throws Exception {
    /* Set value to textbox */
    if (value==null) {
      getUIStringInput(selectField).setValue("");
      getUICheckBoxInput(UIConstraintsForm.CATEGORY_PROPERTY).setChecked(false);
    }else {
      getUIStringInput(selectField).setValue(value.toString());
      /* Set value for checkBox is checked */
      getUICheckBoxInput(UIConstraintsForm.CATEGORY_PROPERTY).setChecked(true);
    }
    UISearchContainer uiSearchContainer = getAncestorOfType(UISearchContainer.class);
    /*
     *  Close popup window when finish choose category
     */
    UIPopupWindow uiPopup = uiSearchContainer.findComponentById(UISearchContainer.CATEGORY_POPUP);
    uiPopup.setRendered(false);
    uiPopup.setShow(false);
  }

  public UIFormDateTimeInput getUIFormDateTimeInput(String name) {
    return findComponentById(name);
  }
}
