/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.empire.jsf2.pageelements;

import java.sql.Connection;

import javax.faces.context.FacesContext;

import org.apache.empire.db.DBRecord;
import org.apache.empire.db.DBRowSet;
import org.apache.empire.exceptions.EmpireException;
import org.apache.empire.exceptions.InternalException;
import org.apache.empire.exceptions.InvalidArgumentException;
import org.apache.empire.exceptions.ObjectNotValidException;
import org.apache.empire.jsf2.app.FacesUtils;
import org.apache.empire.jsf2.pages.Page;
import org.apache.empire.jsf2.pages.PageDefinition;
import org.apache.empire.jsf2.pages.PageElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RecordPageElement<T extends DBRecord> extends PageElement
{
    private static final long   serialVersionUID = 1L;

    private static final Logger log              = LoggerFactory.getLogger(RecordPageElement.class);

    protected DBRowSet rowset;

    protected T record;
    
    /**
     * Creates a record page element for the given Table or View
     * @param page the page element
     * @param rowset Table or View
     * @param record the record object for this page element
     * @param propertyName the property name which is used to get and retrieve session information
     */
    public RecordPageElement(Page page, DBRowSet rowset, T record, String propertyName)
    {
        super(page, propertyName);
        // Set Rowset and Record
        this.rowset = rowset;
        this.record = record;
        // log
        log.debug("RecordPageSupport for {} created.", rowset.getName());
    }

    /**
     * Creates a record page element for the given Table or View
     * @param page the page element
     * @param rowset Table or View
     * @param record the record object for this page element
     */
    public RecordPageElement(Page page, DBRowSet rowset, T record)
    {
        this(page, rowset, record, getDefaultPropertyName(rowset));
    }
    
    public T getRecord()
    {
        return record;
    }
    
    public DBRowSet getRowSet()
    {
        return record.getRowSet();
    }
    
    public String getRecordIdParam()
    {
        if (!record.isValid())
            return null;
        // idParam
        Object[] key = record.getKeyValues();
        return getPage().getIdParamForKey(rowset, key);
    }

    @Override
    protected void onInitPage()
    {
        // Chance to init the page
        if (record.isValid()==false)
            reloadRecord();
    }

    /**
     * return true if the record can be restored from the session.
     * @param newRecord flag to detect session data for a new reaord otherwise for an existing record
     * @return true if information to restore the record is available on the session
     */
    public boolean canReloadRecord(boolean newRecord)
    {
        if (newRecord)
            return (getSessionObject(DBRecord.class)!=null);
        else
            return (getSessionObject(Object[].class)!=null); 
    }
    
    /**
     * loads the record identified by the supplied key from the database<BR>
     */
    public void reloadRecord()
    {
        Object[] recKey = getSessionObject(Object[].class);
        // Check Key
        if (recKey==null || recKey.length==0)
        {   // Invalid Record key
            T rec = (T)getSessionObject(DBRecord.class);
            if (rec!=null)
            {   // A new record
                record = rec;
                return;
            }    
            // Not Valid
            throw new ObjectNotValidException(record);
        }
        // Record laden
        Connection conn = getPage().getConnection(rowset.getDatabase()); 
        record.read(rowset, recKey, conn);
    }
    
    /**
     * loads the record identified by the supplied key from the database<BR>
     */
    public void loadRecord(Object[] recKey)
    {
        // Check Key
        if (recKey==null || recKey.length==0)
        {   // Invalid Record key
            throw new InvalidArgumentException("recKey", recKey);
        }
        // Put key on Session
        this.removeSessionObject(DBRecord.class);
        this.setSessionObject(Object[].class, recKey);
        // Record laden
        Connection conn = getPage().getConnection(rowset.getDatabase()); 
        record.read(rowset, recKey, conn);
    }

    /**
     * loads an existing record
     * @param idParam
     */
    public void loadRecord(String idParam)
    {
        Object[] key = getPage().getKeyFromParam(rowset, idParam);
        loadRecord(key);
    }
    
    /**
     * loads an existing record the the specified page
     * 
     * @param page
     * @param idParam
     */
    public void loadRecord(PageDefinition page, String idParam) {
        Object[] key = getPage().getKeyFromParam(page, rowset, idParam);
        loadRecord(key);
    }
    
    /**
     * creates a new record
     */
    public void createRecord()
    {
        Connection conn = getPage().getConnection(rowset.getDatabase()); 
        record.create(rowset, conn);
        // Put key on Session
        this.removeSessionObject(Object[].class);
        this.setSessionObject(DBRecord.class, record);
    }

    /**
     * updates or inserts the record in the database
     */
    public boolean updateRecord()
    {
        // Record laden
        try {
            // Check Key
            if (record.isValid()==false)
            {   // Invalid Record key
                throw new ObjectNotValidException(record); 
            }
            // Check Modified
            if (record.isModified()==false)
            {   // Not Modified
                return true; 
            }
            Connection conn = getPage().getConnection(rowset.getDatabase()); 
            record.update(conn);
            // Put key on Session
            this.removeSessionObject(DBRecord.class);
            this.setSessionObject(Object[].class, record.getKeyValues());
            return true; 
            // OK
        } catch(Exception e) {
            // Wrap exception
            if (!(e instanceof EmpireException))
                e = new InternalException(e);
            // Set error Message
            FacesContext fc = FacesUtils.getContext();
            String msg = FacesUtils.getTextResolver(fc).getExceptionMessage(e);
            FacesUtils.addErrorMessage(fc, msg);
            return false; 
        }
    }
    
    /**
     * deletes a record
     */
    public void deleteRecord()
    {
        // check valid
        if (!record.isValid())
            throw new ObjectNotValidException(record);
        // delete
        Connection conn = getPage().getConnection(rowset.getDatabase()); 
        record.delete(conn);
        // Put key on Session
        this.removeSessionObject(Object[].class);
        this.removeSessionObject(DBRecord.class);
    }
    
    /**
     * closes a record
     */
    public void closeRecord()
    {
        record.close();
        // Put key on Session
        this.removeSessionObject(Object[].class);
        this.removeSessionObject(DBRecord.class);
    }

}
