/*
 * 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.ode.daohib.bpel;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Set;

import javax.xml.namespace.QName;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.ode.bpel.dao.MessageDAO;
import org.apache.ode.bpel.dao.MessageExchangeDAO;
import org.apache.ode.bpel.dao.PartnerLinkDAO;
import org.apache.ode.bpel.dao.ProcessDAO;
import org.apache.ode.bpel.dao.ProcessInstanceDAO;
import org.apache.ode.bpel.iapi.Scheduler;
import org.apache.ode.daohib.SessionManager;
import org.apache.ode.daohib.bpel.hobj.HCorrelatorMessage;
import org.apache.ode.daohib.bpel.hobj.HMessage;
import org.apache.ode.daohib.bpel.hobj.HMessageExchange;
import org.apache.ode.daohib.bpel.hobj.HProcess;
import org.apache.ode.daohib.bpel.hobj.HProcessInstance;
import org.apache.ode.utils.DOMUtils;
import org.apache.ode.utils.stl.CollectionsX;
import org.apache.ode.utils.stl.UnaryFunctionEx;
import org.hibernate.LockMode;
import org.hibernate.exception.LockAcquisitionException;
import org.w3c.dom.Element;

public class MessageExchangeDaoImpl extends HibernateDao implements
        MessageExchangeDAO {
    @SuppressWarnings("unused")
    private static final Logger __log = LoggerFactory.getLogger(MessageExchangeDaoImpl.class);
    
    private HMessageExchange _hself;

    // Used when provided process and instance aren't hibernate implementations. The relation
    // therefore can't be persisted. Used for in-mem DAOs so that doesn't matter much. 
    private ProcessDAO _externalProcess;
    private ProcessInstanceDAO _externalInstance;

    public MessageExchangeDaoImpl(SessionManager sm, HMessageExchange mex) {
        super(sm, mex);
        entering("MessageExchangeDaoImpl.MessageExchangeDaoImpl");
        _hself = mex;
    }

    public String getMessageExchangeId() {
        return _hself.getId().toString();
    }

    public MessageDAO getResponse() {
        entering("MessageExchangeDaoImpl.getResponse");
        return _hself.getResponse() == null ? null : new MessageDaoImpl(_sm, _hself.getResponse());
    }

    public Date getCreateTime() {
        return _hself.getInsertTime();
    }

    public void setCreateTime(Date createTime) {
        _hself.setInsertTime(createTime);
    }

    public MessageDAO getRequest() {
        entering("MessageExchangeDaoImpl.getRequest");
        return _hself.getRequest() == null ? null : new MessageDaoImpl(_sm, _hself.getRequest());
    }

    public String getOperation() {
        return _hself.getOperationName();
    }

    public QName getPortType() {
        return _hself.getPortType() == null ? null : QName.valueOf(_hself.getPortType());
    }

    public void setPortType(QName porttype) {
        entering("MessageExchangeDaoImpl.setPortType");
        _hself.setPortType(porttype == null ? null : porttype.toString());
        update();
    }

    public void setStatus(String status) {
        entering("MessageExchangeDaoImpl.setStatus");
        _hself.setState(status);
        update();
    }

    public String getStatus() {
        return _hself.getState();
    }

    public MessageDAO createMessage(QName type) {
        entering("MessageExchangeDaoImpl.createMessage");
        HMessage message = new HMessage();
        message.setType(type == null ? null : type.toString());
        message.setCreated(new Date());
        message.setMessageExchange(_hself);
        getSession().save(message);
        return new MessageDaoImpl(_sm, message);

    }

    public void setRequest(MessageDAO msg) {
        entering("MessageExchangeDaoImpl.setRequest");
        _hself.setRequest(msg == null ? null : (HMessage) ((MessageDaoImpl) msg).getHibernateObj());
        update();
    }

    public void setResponse(MessageDAO msg) {
        entering("MessageExchangeDaoImpl.setResponse");
        _hself.setResponse(msg == null ? null : (HMessage) ((MessageDaoImpl) msg).getHibernateObj());
        update();
    }

    public int getPartnerLinkModelId() {
        return _hself.getPartnerLinkModelId();
    }

    public void setPartnerLinkModelId(int modelId) {
        entering("MessageExchangeDaoImpl.setPartnerLinkModelId");
        _hself.setPartnerLinkModelId(modelId);
        update();
    }

    public String getCorrelationId() {
        return _hself.getClientKey();
    }

    public void setCorrelationId(String clientKey) {
        entering("MessageExchangeDaoImpl.setCorrelationId");
        _hself.setClientKey(clientKey);
        update();
    }

    public void setPattern(String pattern) {
        entering("MessageExchangeDaoImpl.setPattern");
        _hself.setPattern(pattern);
        update();

    }

    public void setOperation(String opname) {
        entering("MessageExchangeDaoImpl.setOperation");
        _hself.setOperationName(opname);
        update();
    }

    public void setEPR(Element source) {
        entering("MessageExchangeDaoImpl.setEPR");
        if (source == null)
            _hself.setEndpoint(null);
        else {
            _hself.setEndpoint(DOMUtils.domToBytes(source));
        }

        getSession().saveOrUpdate(_hself);

    }

    public Element getEPR() {
        entering("MessageExchangeDaoImpl.getEPR");
        byte[] endpoint = _hself.getEndpoint();
        if (endpoint == null)
            return null;
        try {
            return DOMUtils.stringToDOM(endpoint);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void setCallbackEPR(Element source) {
        entering("MessageExchangeDaoImpl.setCallbackEPR");
        if (source == null)
            _hself.setCallbackEndpoint(null);
        else {
        	_hself.setCallbackEndpoint(DOMUtils.domToBytes(source));
        }

        getSession().saveOrUpdate(_hself);

    }

    public Element getCallbackEPR() {
        entering("MessageExchangeDaoImpl.getCallbackEPR");
        byte[] endpoint = _hself.getCallbackEndpoint();
        if (endpoint == null)
            return null;
        try {
            return DOMUtils.stringToDOM(endpoint);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public String getPattern() {
        return _hself.getPattern();
    }

    public String getChannel() {
        return _hself.getChannelName();
    }

    public void setChannel(String channel) {
        entering("MessageExchangeDaoImpl.setChannel");
        _hself.setChannelName(channel);
        update();
    }

    public boolean getPropagateTransactionFlag() {
        // TODO Auto-generated method stub
        return false;
    }

    public QName getFault() {
        return _hself.getFault() == null ? null : QName.valueOf(_hself.getFault());
    }

    public void setFault(QName faultType) {
        entering("MessageExchangeDaoImpl.setFault");
        _hself.setFault(faultType == null ? null : faultType.toString());
        update();
    }

    public String getFaultExplanation() {
        return _hself.getFaultExplanation();
    }

    public void setFaultExplanation(String explanation) {
        entering("MessageExchangeDaoImpl.setFaultExplanation");
        _hself.setFaultExplanation(explanation);
        update();
    }

    public void setCorrelationStatus(String cstatus) {
        entering("MessageExchangeDaoImpl.setCorrelationStatus");
        _hself.setCorrelationStatus(cstatus);
        update();
    }

    public String getCorrelationStatus() {
        return _hself.getCorrelationStatus();
    }

    public ProcessDAO getProcess() {
        entering("MessageExchangeDaoImpl.getProcess");
        if (_externalProcess != null) return _externalProcess;
        else return _hself.getProcess() == null ? null : new ProcessDaoImpl(_sm, _hself.getProcess());
    }

    public void setProcess(ProcessDAO process) {
        entering("MessageExchangeDaoImpl.setProcess");
        if (process == null || process instanceof ProcessDaoImpl) {
        _hself.setProcess(process == null ? null : (HProcess) ((ProcessDaoImpl) process).getHibernateObj());
        update();
        } else {
            _externalProcess = process;
    }
    }

    public void setInstance(ProcessInstanceDAO instance) {
        entering("MessageExchangeDaoImpl.setInstance");
        if (instance == null || instance instanceof ProcessInstanceDaoImpl) {
        _hself.setInstance(instance == null ? null : (HProcessInstance) ((ProcessInstanceDaoImpl) instance)
                .getHibernateObj());
        update();
        } else {
            _externalInstance = instance;
    }

    }

    public ProcessInstanceDAO getInstance() {
        entering("MessageExchangeDaoImpl.getInstance");
        if (_externalInstance != null) return _externalInstance;
        else return _hself.getInstance() == null ? null : new ProcessInstanceDaoImpl(_sm, _hself.getInstance());
    }

    public char getDirection() {
        return _hself.getDirection();
    }

    public QName getCallee() {
        String callee = _hself.getCallee();
        return callee == null ? null : QName.valueOf(callee);
    }

    public void setCallee(QName callee) {
        entering("MessageExchangeDaoImpl.setCallee");
        _hself.setCallee(callee == null ? null : callee.toString());
        update();
    }

    public String getProperty(String key) {
        entering("MessageExchangeDaoImpl.getProperty");
        return _hself.getProperties().get(key);
    }

    public void setProperty(String key, String value) {
        entering("MessageExchangeDaoImpl.setProperty");
        _hself.getProperties().put(key, value);
        update();
    }

    public void setPartnerLink(PartnerLinkDAO plinkDAO) {
        entering("MessageExchangeDaoImpl.setPartnerLink");
        _hself.setPartnerLink(((PartnerLinkDAOImpl) plinkDAO)._self);
        update();
    }

    public PartnerLinkDAO getPartnerLink() {
        entering("MessageExchangeDaoImpl.getPartnerLink");
        return new PartnerLinkDAOImpl(_sm, _hself.getPartnerLink());
    }

    public Set<String> getPropertyNames() {
        entering("MessageExchangeDaoImpl.getPropertyNames");
        return Collections.unmodifiableSet(_hself.getProperties().keySet());
    }

    public String getPipedMessageExchangeId() {
        return _hself.getPipedMessageExchangeId();
    }

    public void setPipedMessageExchangeId(String mexId) {
        entering("MessageExchangeDaoImpl.setPipedMessageExchangeId");
        _hself.setPipedMessageExchangeId(mexId);
    }

    public int getSubscriberCount() {
        return _hself.getSubscriberCount();
    }
    
    public void setSubscriberCount(int subscriberCount) {
        _hself.setSubscriberCount(subscriberCount);        
    }
    
    public void release(boolean doClean) {
        if( doClean ) {
            deleteMessages();
        }
    }

    @SuppressWarnings("unchecked")
    public void releasePremieMessages() {
        deleteByIds(HCorrelatorMessage.class, getSession().getNamedQuery(HCorrelatorMessage.SELECT_CORMESSAGE_IDS_BY_MEX).setParameter("mex", _hself).list());
    }

    public void incrementSubscriberCount() {
        _hself.incrementSubscriberCount();
    }
    
    @SuppressWarnings("unchecked")
    public void deleteMessages() {
        deleteByIds(HCorrelatorMessage.class, getSession().getNamedQuery(HCorrelatorMessage.SELECT_CORMESSAGE_IDS_BY_MEX).setParameter("mex", _hself).list());
          
        getSession().delete(_hself);
        // This deletes endpoint LData, callbackEndpoint LData, request HMessage, response HMessage, HMessageExchangeProperty 
    }

    public boolean lockPremieMessages() {
        try {
            return getSession().getNamedQuery(HCorrelatorMessage.SELECT_CORMESSAGE_BY_MEX).setLockMode("m", LockMode.UPGRADE).setParameter("mex", _hself).list().size() > 0;
        } catch (LockAcquisitionException e) {
            throw new Scheduler.JobProcessorException(e, true);
        }
    }

}
