/**
 *  BlueCove - Java library for Bluetooth
 *  Copyright (C) 2007-2008 Vlad Skarzhevskyy
 *
 *  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.
 *
 *  @author vlads
 *  @version $Id: OBEXServerOperation.java 2643 2008-12-23 00:24:46Z skarzhevskyy $
 */
package com.intel.bluetooth.obex;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import javax.obex.HeaderSet;
import javax.obex.Operation;
import javax.obex.ResponseCodes;

import com.intel.bluetooth.DebugLog;

abstract class OBEXServerOperation implements Operation, OBEXOperation {

	protected OBEXServerSessionImpl session;

	protected HeaderSet receivedHeaders;

	protected OBEXHeaderSetImpl sendHeaders;

	protected boolean isClosed = false;

	protected boolean isAborted = false;

	protected boolean finalPacketReceived = false;

	protected boolean requestEnded = false;

	protected boolean errorReceived = false;

	protected boolean incommingDataReceived = false;

	protected OBEXOperationOutputStream outputStream;

	protected boolean outputStreamOpened = false;

	protected OBEXOperationInputStream inputStream;

	protected boolean inputStreamOpened = false;

	protected OBEXServerOperation(OBEXServerSessionImpl session, OBEXHeaderSetImpl receivedHeaders) throws IOException {
		this.session = session;
		this.receivedHeaders = receivedHeaders;
		if (receivedHeaders.hasAuthenticationChallenge()) {
			sendHeaders = OBEXSessionBase.createOBEXHeaderSetImpl();
			this.session.handleAuthenticationChallenge(receivedHeaders, sendHeaders);
		}
	}

	boolean exchangeRequestPhasePackets() throws IOException {
		session.writePacket(OBEXOperationCodes.OBEX_RESPONSE_CONTINUE, null);
		return readRequestPacket();
	}

	protected abstract boolean readRequestPacket() throws IOException;

	void writeResponse(int responseCode) throws IOException {
		DebugLog.debug0x("server operation reply final", responseCode);
		session.writePacket(responseCode, sendHeaders);
		sendHeaders = null;
		if (responseCode == ResponseCodes.OBEX_HTTP_OK) {
			while ((!finalPacketReceived) && (!session.isClosed())) {
				DebugLog.debug("server waits to receive final packet");
				readRequestPacket();
				if (!errorReceived) {
					session.writePacket(responseCode, null);
				}
			}
		} else {
			DebugLog.debug("sent final reply");
		}
	}

	protected void processIncommingData(HeaderSet dataHeaders, boolean eof) throws IOException {
		// If this operation closing
		if (this.inputStream == null) {
			return;
		}
		byte[] data = (byte[]) dataHeaders.getHeader(OBEXHeaderSetImpl.OBEX_HDR_BODY);
		if (data == null) {
			data = (byte[]) dataHeaders.getHeader(OBEXHeaderSetImpl.OBEX_HDR_BODY_END);
			if (data != null) {
				eof = true;
			}
		}
		if (data != null) {
			incommingDataReceived = true;
			DebugLog.debug("server received Data eof: " + eof + " len:", data.length);
			inputStream.appendData(data, eof);
		} else if (eof) {
			inputStream.appendData(null, eof);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.obex.Operation#abort()
	 */
	public void abort() throws IOException {
		throw new IOException("Can't abort server operation");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.obex.Operation#getReceivedHeaders()
	 */
	public HeaderSet getReceivedHeaders() throws IOException {
		return OBEXHeaderSetImpl.cloneHeaders(receivedHeaders);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.obex.Operation#getResponseCode()
	 */
	public int getResponseCode() throws IOException {
		throw new IOException("Operation object was created by an OBEX server");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.obex.Operation#sendHeaders(javax.obex.HeaderSet)
	 */
	public void sendHeaders(HeaderSet headers) throws IOException {
		if (headers == null) {
			throw new NullPointerException("headers are null");
		}
		OBEXHeaderSetImpl.validateCreatedHeaderSet(headers);
		if (isClosed) {
			throw new IOException("operation closed");
		}
		if (sendHeaders != null) {
			OBEXHeaderSetImpl.appendHeaders(sendHeaders, headers);
		} else {
			sendHeaders = (OBEXHeaderSetImpl) headers;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.microedition.io.ContentConnection#getEncoding() <code>getEncoding()</code> will always return
	 * <code>null</code>
	 */
	public String getEncoding() {
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.microedition.io.ContentConnection#getLength() <code>getLength()</code> will return the length
	 * specified by the OBEX Length header or -1 if the OBEX Length header was not included.
	 */
	public long getLength() {
		Long len;
		try {
			len = (Long) receivedHeaders.getHeader(HeaderSet.LENGTH);
		} catch (IOException e) {
			return -1;
		}
		if (len == null) {
			return -1;
		}
		return len.longValue();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.microedition.io.ContentConnection#getType() <code>getType()</code> will return the value specified in
	 * the OBEX Type header or <code>null</code> if the OBEX Type header was not included.
	 */
	public String getType() {
		try {
			return (String) receivedHeaders.getHeader(HeaderSet.TYPE);
		} catch (IOException e) {
			return null;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.microedition.io.InputConnection#openDataInputStream()
	 */
	public DataInputStream openDataInputStream() throws IOException {
		return new DataInputStream(openInputStream());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.microedition.io.OutputConnection#openDataOutputStream()
	 */
	public DataOutputStream openDataOutputStream() throws IOException {
		return new DataOutputStream(openOutputStream());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.microedition.io.Connection#close()
	 */
	public void close() throws IOException {
		this.isClosed = true;
	}

	public boolean isClosed() {
		return this.isClosed;
	}

	public boolean isIncommingDataReceived() {
		return this.incommingDataReceived;
	}

	public boolean isErrorReceived() {
		return this.errorReceived;
	}
}
