/*
 * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
 *
 * The contents of this file are subject to the CCM Public
 * License (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the
 * License at http://www.redhat.com/licenses/ccmpl.html.
 *
 * Software distributed under the License is distributed on an
 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
 * or implied. See the License for the specific language
 * governing rights and limitations under the License.
 *
 */
package com.arsdigita.cms.search;

import com.arsdigita.cms.ContentType;
import com.arsdigita.search.FilterType;
import com.arsdigita.search.FilterSpecification;
import com.arsdigita.search.filters.CategoryFilterSpecification;
import com.arsdigita.search.filters.DateRangeFilterSpecification;
import com.arsdigita.search.filters.PartyFilterSpecification;
import com.arsdigita.search.intermedia.BaseQueryEngine;
import com.arsdigita.persistence.DataQuery;
import com.arsdigita.persistence.Filter;
import com.arsdigita.categorization.Category;
import com.arsdigita.kernel.PartyCollection;

import com.arsdigita.cms.search.ContentTypeFilterSpecification;
import com.arsdigita.cms.search.ContentTypeFilterType;

import java.util.List;
import java.util.ArrayList;
import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;

import java.math.BigDecimal;

public class IntermediaQueryEngine extends BaseQueryEngine {

    public IntermediaQueryEngine() {
        addColumn("i.version", "version");
        addColumn("p.launch_date", "launch_date");
        addColumn("audited.last_modified", "last_modified");
        addColumn("audited.modifying_user", "modifying_user");
        addColumn("audited.creation_date", "creation_date");
        addColumn("audited.creation_user", "creation_user");
        addTable("cms_items", "i");
        addTable("cms_pages", "p");
        addTable("acs_auditing", "audited");
        addCondition("c.object_id = i.item_id");
        addCondition("c.object_id = p.item_id");
        addCondition("c.object_id = audited.object_id");
    }

    protected void addFilter(DataQuery query,
                             FilterSpecification filter) {
        super.addFilter(query, filter);

        FilterType type = filter.getType();
        
        if (ContentTypeFilterType.KEY.equals(type.getKey())) {
            addContentTypeFilter(query, (ContentTypeFilterSpecification)filter);
        } else if (VersionFilterType.KEY.equals(type.getKey())) {
            addVersionFilter(query, (VersionFilterSpecification)filter);
        } else if (LaunchDateFilterType.KEY.equals(type.getKey())) {
            addLaunchDateFilter(query, (DateRangeFilterSpecification)filter);
        } else if (LastModifiedDateFilterType.KEY.equals(type.getKey())) {
            addLastModifiedDateFilter(query, (DateRangeFilterSpecification)filter);
        } else if (CreationDateFilterType.KEY.equals(type.getKey())) {
            addCreationDateFilter(query, (DateRangeFilterSpecification)filter);
        } else if (LastModifiedUserFilterType.KEY.equals(type.getKey())) {
            addPartyFilter(query, (PartyFilterSpecification)filter,
                           "modifying_user");
        } else if (CreationUserFilterType.KEY.equals(type.getKey())) {
            addPartyFilter(query, (PartyFilterSpecification)filter,
                           "creation_user");
        }
    }

    protected void addVersionFilter(DataQuery query,
                                    VersionFilterSpecification filter) {
        Filter f = query.addFilter("version = :version");
        f.set("version", filter.getVersion());
    }

    protected void addLaunchDateFilter(DataQuery query,
                                       DateRangeFilterSpecification filter) {
        addDateRangeFilter(query, filter, "launch_date");
    }


    protected void addLastModifiedDateFilter(DataQuery query,
                                         DateRangeFilterSpecification filter) {
        // TODO: https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=113394
        // this will not work until the above bug is fixed
        addDateRangeFilter(query, filter, "last_modified");
    }

    protected void addCreationDateFilter(DataQuery query,
                                         DateRangeFilterSpecification filter) {
        // TODO: https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=113394
        // this will not work until the above bug is fixed
        addDateRangeFilter(query, filter, "creation_date");
    }

    private void addDateRangeFilter(DataQuery query,
                                    DateRangeFilterSpecification filter,
                                    String columnName) {
        Date start = filter.getStartDate();
        Date end = filter.getEndDate();
        // XXX The query should actually be the commented out code at the 
        // bottom of this method instead of the query that we are using
        // we have to comment it out because of a bug with persistence

        DateFormat format = new SimpleDateFormat("MM/dd/yyyy");

        if (start != null) {
            Filter f = query.addFilter(columnName + " >= :" + 
                                       columnName + "StartDate");
            // we truncate the value using java because we cannot do it
            // using sql because of the persistence bug
            Calendar startCal = GregorianCalendar.getInstance();
            startCal.setTime(start);
            /*
            Calendar truncCal = GregorianCalendar.getInstance();
            truncCal.set(Calendar.DAY_OF_YEAR, startCal.get(Calendar.DAY_OF_YEAR));
            truncCal.set(Calendar.YEAR, startCal.get(Calendar.YEAR));
            truncCal.set(Calendar.MINUTE, 0);
            truncCal.set(Calendar.HOUR_OF_DAY, 0);
            truncCal.set(Calendar.SECOND, 0);
            f.set(columnName + "StartDate", truncCal.getTime());
            */
            startCal.set(Calendar.MINUTE, 0);
            startCal.set(Calendar.HOUR_OF_DAY, 0);
            startCal.set(Calendar.SECOND, 0);
            f.set(columnName + "StartDate", startCal.getTime());
        }
        if (end != null) {
            Filter f = query.addFilter(columnName + " < :" + 
                                       columnName + "EndDate");
            // we truncate the value using java because we cannot do it
            // using sql because of the persistence bug
            Calendar endCal = GregorianCalendar.getInstance();
            endCal.setTime(end);
            endCal.set(Calendar.MINUTE, 0);
            endCal.set(Calendar.HOUR_OF_DAY, 0);
            endCal.set(Calendar.SECOND, 0);
            f.set(columnName + "EndDate", endCal.getTime());
        }

        /*
        DateFormat format = new SimpleDateFormat("MM/dd/yyyy");

        if (start != null) {
            // XXX persistence bug - can't deal with functions on fields
            Filter f = query.addFilter("trunc(" + columnName + ") >= to_date('" + 
                                       format.format(start) +
                                       "','MM/DD/YYYY')");            
        }
        if (end != null) {
            // XXX persistence bug - can't deal with functions on fields
            Filter f = query.addFilter("trunc(" + columnName + ") <= to_date('" + 
                                       format.format(end) +
                                       "','MM/DD/YYYY')");
        }
        */
    }

    protected void addPartyFilter(DataQuery query,
                                  PartyFilterSpecification filter,
                                  String columnName) {
        PartyCollection parties = filter.getParties();
        if (parties == null) {
            return;
        }
        List partyIDs = new ArrayList();
        while (parties.next()) {
            partyIDs.add(parties.getID());
        }

        if (partyIDs.size() == 0) {
            // no parties match so we make sure to return no results
            query.addFilter("1=2");
        } else {
            Filter f = query.addFilter(columnName + " in :" + 
                                       columnName + "parties");
            f.set(columnName + "parties", partyIDs);
        }
    }

    protected void addContentTypeFilter(DataQuery query,
                                        ContentTypeFilterSpecification filter) {
        List l = new ArrayList();
        ContentType[] types = filter.getTypes();
        if (types == null || types.length == 0) {
            return;
        }

        for (int i = 0 ; i < types.length ; i++) {
            ContentType type = types[i];
            l.add(type.getAssociatedObjectType());
        }
        Filter f = query.addFilter("object_type in :types");
        f.set("types", l);
    }

    // Override to use query that takes account of bundles :-(
    protected void addCategoryFilter(DataQuery query,
                                     CategoryFilterSpecification filter) {
        Category[] categories = filter.getCategories();
        if (categories != null && categories.length > 0) {
            List ids = new ArrayList();
            for (int i = 0 ; i < categories.length ; i++) {
                ids.add(categories[i].getID());
            }

            Filter f = query.addInSubqueryFilter(
                "object_id",
                "id",
                "com.arsdigita.cms.searchCategoryItems");
            
            f.set("ids", ids);
        }
    }

}
