/*
    Kopete Groupwise Protocol
    coreprotocol.h- the core GroupWise protocol

    Copyright (c) 2004      SUSE Linux AG	     http://www.suse.com

    Based on Iris, Copyright (C) 2003  Justin Karneges <justin@affinix.com>

    Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>

    *************************************************************************
    *                                                                       *
    * This library is free software; you can redistribute it and/or         *
    * modify it under the terms of the GNU Lesser General Public            *
    * License as published by the Free Software Foundation; either          *
    * version 2 of the License, or (at your option) any later version.      *
    *                                                                       *
    *************************************************************************
*/

#ifndef GW_CORE_PROTOCOL_H
#define GW_CORE_PROTOCOL_H

#include <QLatin1Char>
#include <qobject.h>

#include "gwfield.h"

class EventProtocol;
class ResponseProtocol;
class Request;
class Transfer;

/**
 * This class handles transforming data between structured high level messages and encoded bytes that are sent
 * and received over the network.
 *
 * 0) FIELD ARRAYS
 * ---------------
 * This is relevant to both input and output handling.
 * Requests (out) and Responses (in) are messages containing, after a HTTP header, a series of 'Fields'.
 * A message may contain a flat series of Fields, or each Field may mark the start of a nested array of more Fields.
 * In this case the Field's value is the length of the following nested array.
 * The length of the top level Field series is not given.  The message ends when there are no more Fields expected as part of a nested array,
 * and is marked by a terminator.
 * The encoding used for Fields differs for Requests and Responses, and is described below.
 *
 * 1) INPUT
 * --------
 * The input functionality is a finite state machine that processes the stream of data from the GroupWise server.
 * Since the server may arbitrarily truncate or run together protocol level messages, we buffer the incoming data stream,
 * parsing it into individual messages that are removed from the buffer and passed back to the ClientStream, which propagates
 * them to higher layers.
 *
 * Incoming data may be in either of two formats; a Response or an Event.
 * All binary data is Little Endian on the network.
 *
 * 1.1) INPUT MESSAGE 'SPECIES'
 *
 * 1.1.1) Events
 *
 * Events are independently occurring notifications generated by the server or by the activity of other users.
 * Events are represented on the wire in binary format:
 *
 * BYTE            1
 * 0       8       6....
 * AAAABBBBCCCCCCCCC....DDDDDDDD.....
 * AAAA is a UINT32 giving the type of event
 * BBBB is a UINT32 giving the length of the event source,
 * CCCC... is the event source, a UTF8 encoded string, which is observed to be zero terminated
 * DDDD... is event dependent binary data, which frequently consists of the conference the event relates to,
 *         conference flags describing the logging, chat security and closed status, and message data.
 *
 * As the DDDD portion is irregularly structured, it must be processed knowing the semantics of the event type.
 * See the @ref EventProtocol documentation.
 *
 * Event message data is always a UINT32 giving the message length, then a message in RTF format.
 * The message length may be zero.
 *
 * 1.1.2) Responses
 * Responses are the server's response to client Requests.  Each Request generates one Response.  Requests and Responses are regularly structured
 * and can be parsed/generated without any knowledge of their content.
 * Responses consist of text/line oriented standard HTTP headers, followed by a binary payload.  The payload is a series of Fields as described above,
 * and the terminator following the last field is a null (0x0) byte.
 *
 * TODO: Add Field structure format: type, tag, method, flags, and value.  see ResponseProtocol::readFields() for reference if this is incomplete.
 *
 * 1.3) INPUT PROCESSING IMPLEMENTATION
 * CoreProtocol input handling operates on an event driven basis.  It starts processing when it receives data via @ref addIncomingData(),
 * and emits @ref incomingData() as each complete message is parsed in off the wire.
 * Each call to addIncomingData() may result in zero or more incomingData() signals
 *
 * 2) REQUESTS
 * -----------
 * The output functionality is an encoding function that transforms outgoing Requests into the wire request format
 * - a HTTP POST made up of the request operation type as the path, followed by a series of (repeated) variables that form the arguments.
 * Order of the arguments is significant!
 * Argument values are URL-encoded with spaces encoded as + rather than %20.
 * The terminator used is a CRLF pair ("\r\n").
 * HTTP headers are only used in a login operation, where they contain a Host: hostname:port line.
 * Headers are separated from the arguments by a blank line (only CRLF) as usual.
 *
 * 3) USER MESSAGE BODY TEXT REPRESENTATION
 * -----------------------------------
 * Message text sent by users (found in both Requests and Events) is generally formatted as Rich Text Format.
 * Text portions of the RTF may be be encoded in
 * any of three ways -
 *      ascii text,
 *      latin1 as hexadecimal,
 *      escaped unicode code points (encoded/escaped as \uUNICODEVALUE?, with or without a space between the end of the unicode value and the ? )
 * Outgoing messages may contain rich text, and additionally the plain text encoded as UTF8, but this plain payload is apparently ignored by the server
 *
 */
class CoreProtocol : public QObject
{
    Q_OBJECT
public:
    enum State {
        NeedMore, Available, NoData
    };

    CoreProtocol();

    virtual ~CoreProtocol();
    /**
     * Debug output
     */
    static void debug(const QString &str);

    /**
     * Reset the protocol, clear buffers
     */
    void reset();

    /**
     * Accept data from the network, and buffer it into a useful message
     * @param incomingBytes Raw data in wire format.
     */
    void addIncomingData(const QByteArray &incomingBytes);

    /**
     * @return the incoming transfer or 0 if none is available.
     */
    Transfer *incomingTransfer();

    /**
     * Convert a request into an outgoing transfer
     * emits @ref outgoingData() with each part of the transfer
     */
    void outgoingTransfer(Request *outgoing);

    /**
     * Get the state of the protocol
     */
    int state();

Q_SIGNALS:
    /**
     * Emitted as the core protocol converts fields to wire ready data
     */
    void outgoingData(const QByteArray &);
    /**
     * Emitted when there is incoming data, parsed into a Transfer
     */
    void incomingData();
protected Q_SLOTS:
    /**
     * Just a debug method to test emitting to the socket, atm - should go to the ClientStream
     */
    void slotOutgoingData(const QByteArray &);

protected:
    /**
     * Check that there is data to read, and set the protocol's state if there isn't any.
     */
    bool okToProceed();
    /**
     * Convert incoming wire data into a Transfer object and queue it
     * @return number of bytes from the input that were parsed into a Transfer
     */
    int wireToTransfer(QByteArray &wire);
    /**
     * Convert fields to a wire representation.  Emits outgoingData as each field is written.
     * Calls itself recursively to process nested fields, hence
     * @param depth Current depth of recursion.  Don't use this parameter yourself!
     */
    void fieldsToWire(Field::FieldList fields, int depth = 0);
    /**
     * encodes a method number (usually supplied as a #defined symbol) to a char
     */
    QLatin1Char encode_method(quint8 method);
private:
    QByteArray m_in;    // buffer containing unprocessed bytes we received
    QDataStream *m_din; // contains the packet currently being parsed
    int m_error;
    Transfer *m_inTransfer; // the transfer that is being received
    int m_state;        // represents the protocol's overall state
    EventProtocol *m_eventProtocol;
    ResponseProtocol *m_responseProtocol;
};

#endif
