// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef NET_FLIP_FLIP_STREAM_H_
#define NET_FLIP_FLIP_STREAM_H_

#include <string>
#include <list>

#include "base/basictypes.h"
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
#include "base/singleton.h"
#include "net/base/bandwidth_metrics.h"
#include "net/base/completion_callback.h"
#include "net/base/io_buffer.h"
#include "net/base/load_log.h"
#include "net/flip/flip_framer.h"
#include "net/flip/flip_protocol.h"

namespace net {

class FlipSession;
class HttpRequestInfo;
class HttpResponseInfo;
class UploadData;
class UploadDataStream;

// The FlipStream is used by the FlipSession to represent each stream known
// on the FlipSession.
// Streams can be created either by the client or by the server.  When they
// are initiated by the client, both the FlipSession and client object (such as
// a FlipNetworkTransaction) will maintain a reference to the stream.  When
// initiated by the server, only the FlipSession will maintain any reference,
// until such a time as a client object requests a stream for the path.
class FlipStream : public base::RefCounted<FlipStream> {
 public:
  // FlipStream constructor
  FlipStream(FlipSession* session, flip::FlipStreamId stream_id, bool pushed,
             LoadLog* log);

  // Ideally I'd use two abstract classes as interfaces for these two sections,
  // but since we're ref counted, I can't make both abstract classes inherit
  // from RefCounted or we'll have two separate ref counts for the same object.
  // TODO(willchan): Consider using linked_ptr here orcreating proxy wrappers
  // for FlipStream to provide the appropriate interface.

  // ===================================================
  // Interface for [Http|Flip]NetworkTransaction to use.

  // Sends the request.  If |upload_data| is non-NULL, sends that in the request
  // body.  |callback| is used when this completes asynchronously.  Note that
  // the actual SYN_STREAM packet will have already been sent by this point.
  // Also note that FlipStream takes ownership of |upload_data|.
  int SendRequest(UploadDataStream* upload_data,
                  HttpResponseInfo* response,
                  CompletionCallback* callback);

  // Reads the response headers.  Returns a net error code.
  int ReadResponseHeaders(CompletionCallback* callback);

  // Reads the response body.  Returns a net error code or the number of bytes
  // read.
  int ReadResponseBody(
      IOBuffer* buf, int buf_len, CompletionCallback* callback);

  // Cancels the stream.  Note that this does not immediately cause deletion of
  // the stream.  This function is used to cancel any callbacks from being
  // invoked.  TODO(willchan): It should also free up any memory associated with
  // the stream, such as IOBuffers.
  void Cancel();

  // Returns the number of bytes uploaded.
  uint64 GetUploadProgress() const;

  const HttpResponseInfo* GetResponseInfo() const;

  // Is this stream a pushed stream from the server.
  bool pushed() const { return pushed_; }

  // =================================
  // Interface for FlipSession to use.

  flip::FlipStreamId stream_id() const { return stream_id_; }
  void set_stream_id(flip::FlipStreamId stream_id) { stream_id_ = stream_id; }

  // For pushed streams, we track a path to identify them.
  const std::string& path() const { return path_; }
  void set_path(const std::string& path) { path_ = path; }

  int priority() const { return priority_; }
  void set_priority(int priority) { priority_ = priority; }

  // Called by the FlipSession when a response (e.g. a SYN_REPLY) has been
  // received for this stream.  |path| is the path of the URL for a server
  // initiated stream, otherwise is empty.
  void OnResponseReceived(const HttpResponseInfo& response);

  // Called by the FlipSession when response data has been received for this
  // stream.  This callback may be called multiple times as data arrives
  // from the network, and will never be called prior to OnResponseReceived.
  // |buffer| contains the data received.  The stream must copy any data
  //          from this buffer before returning from this callback.
  // |length| is the number of bytes received or an error.
  //         A zero-length count does not indicate end-of-stream.
  // Returns true on success and false on error.
  bool OnDataReceived(const char* buffer, int bytes);

  // Called by the FlipSession when a write has completed.  This callback
  // will be called multiple times for each write which completes.  Writes
  // include the SYN_STREAM write and also DATA frame writes.
  // |result| is the number of bytes written or a net error code.
  void OnWriteComplete(int status);

  // Called by the FlipSession when the request is finished.  This callback
  // will always be called at the end of the request and signals to the
  // stream that the stream has no more network events.  No further callbacks
  // to the stream will be made after this call.
  // |status| is an error code or OK.
  void OnClose(int status);

  bool cancelled() const { return cancelled_; }

 private:
  friend class base::RefCounted<FlipStream>;

  enum State {
    STATE_NONE,
    STATE_SEND_HEADERS,
    STATE_SEND_HEADERS_COMPLETE,
    STATE_SEND_BODY,
    STATE_SEND_BODY_COMPLETE,
    STATE_READ_HEADERS,
    STATE_READ_HEADERS_COMPLETE,
    STATE_READ_BODY,
    STATE_READ_BODY_COMPLETE,
    STATE_DONE
  };

  ~FlipStream();

  // Try to make progress sending/receiving the request/response.
  int DoLoop(int result);

  // Call the user callback.
  void DoCallback(int rv);

  // The implementations of each state of the state machine.
  int DoSendHeaders();
  int DoSendHeadersComplete(int result);
  int DoSendBody();
  int DoSendBodyComplete(int result);
  int DoReadHeaders();
  int DoReadHeadersComplete(int result);
  int DoReadBody();
  int DoReadBodyComplete(int result);

  // Update the histograms.  Can safely be called repeatedly, but should only
  // be called after the stream has completed.
  void UpdateHistograms();

  flip::FlipStreamId stream_id_;
  std::string path_;
  int priority_;
  const bool pushed_;
  // We buffer the response body as it arrives asynchronously from the stream.
  // TODO(mbelshe):  is this infinite buffering?
  std::list<scoped_refptr<IOBufferWithSize> > response_body_;
  bool download_finished_;
  ScopedBandwidthMetrics metrics_;

  scoped_refptr<FlipSession> session_;

  HttpResponseInfo* response_;
  scoped_ptr<UploadDataStream> request_body_stream_;

  bool response_complete_;  // TODO(mbelshe): fold this into the io_state.
  State io_state_;

  // Since we buffer the response, we also buffer the response status.
  // Not valid until response_complete_ is true.
  int response_status_;

  CompletionCallback* user_callback_;

  // User provided buffer for the ReadResponseBody() response.
  scoped_refptr<IOBuffer> user_buffer_;
  int user_buffer_len_;

  bool cancelled_;

  scoped_refptr<LoadLog> load_log_;

  base::TimeTicks send_time_;
  base::TimeTicks recv_first_byte_time_;
  base::TimeTicks recv_last_byte_time_;
  int send_bytes_;
  int recv_bytes_;
  bool histograms_recorded_;

  DISALLOW_COPY_AND_ASSIGN(FlipStream);
};

}  // namespace net

#endif  // NET_FLIP_FLIP_STREAM_H_