/*
 * 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.
 */


#include <cstdlib>         // for malloc
#include <algorithm>       // for min
#include "base/logging.h"  // for DCHECK
#include "pagespeed/kernel/base/circular_buffer.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_writer.h"
#include "pagespeed/kernel/base/string_util.h"

namespace net_instaweb {

class MessageHandler;

CircularBuffer* CircularBuffer::Create(const int capacity) {
  int total = Sizeof(capacity);
  CircularBuffer* cb = static_cast<CircularBuffer*>(malloc(total));
  cb->capacity_ = capacity;
  cb->wrapped_ = false;
  cb->offset_ = 0;
  return cb;
}

CircularBuffer* CircularBuffer::Init(bool parent, void* block,
                                     const int block_size,
                                     const int capacity) {
  // Check if the pre-allocated block has right size for CircularBuffer.
  DCHECK(block_size == Sizeof(capacity));
  CircularBuffer* cb = static_cast<CircularBuffer*>(block);
  if (parent) {
    // In root process, initialize the variables.
    cb->capacity_ = capacity;
    cb->wrapped_ = false;
    cb->offset_ = 0;
  }
  return cb;
}

void CircularBuffer::Clear() {
  offset_ = 0;
  wrapped_ = false;
}

bool CircularBuffer::Write(const StringPiece& message) {
  const char* data = message.data();
  int size = message.size();
  // Left-truncate the message if its size is larger than buffer.
  if (size > capacity_) {
    data += size - capacity_;
    size = capacity_;
    memcpy(buffer_, data, size);
    offset_ = 0;
    wrapped_ = true;
    return true;
  }
  // Otherwise, start to write the message at offset.
  if (offset_ == capacity_) {
    offset_ = 0;
    wrapped_ = true;
  }
  int rest = capacity_ - offset_;
  int len = std::min(rest, size);
  memcpy(buffer_ + offset_, data, len);
  offset_ += len;
  // If available size < message size < buffer capacity,
  // write the rest of the data at the beginning of the buffer.
  if (len < size) {
    memcpy(buffer_, data + len, size - len);
    offset_ = size - len;
    wrapped_ = true;
  }
  return true;
}

GoogleString CircularBuffer::ToString(MessageHandler* handler) {
  GoogleString result;
  StringWriter writer(&result);
  writer.Write(FirstChunk(), handler);
  writer.Write(SecondChunk(), handler);
  return result;
}

StringPiece CircularBuffer::FirstChunk() {
  if (!wrapped_) {
    return StringPiece();
  }
  return StringPiece(buffer_ + offset_, capacity_ - offset_);
}

StringPiece CircularBuffer::SecondChunk() {
  return StringPiece(buffer_, offset_);
}

}  // namespace net_instaweb
