// Copyright (c) 2014 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.

#include <stdint.h>

#include "gpu/command_buffer/service/gl_utils.h"
#include "gpu/command_buffer/service/program_manager.h"
#include "gpu/command_buffer/service/valuebuffer_manager.h"

namespace gpu {
namespace gles2 {

SubscriptionRefSet::Observer::~Observer() {
}

SubscriptionRefSet::SubscriptionRefSet() {
}

SubscriptionRefSet::~SubscriptionRefSet() {
  // Make sure no valuebuffers are still holding references to targets
  DCHECK(reference_set_.empty());
}

void SubscriptionRefSet::AddSubscription(unsigned int target) {
  RefSet::iterator it = reference_set_.find(target);
  if (it == reference_set_.end()) {
    reference_set_.insert(std::make_pair(target, 1));
    FOR_EACH_OBSERVER(Observer, observers_, OnAddSubscription(target));
  } else {
    ++it->second;
  }
}

void SubscriptionRefSet::RemoveSubscription(unsigned int target) {
  RefSet::iterator it = reference_set_.find(target);
  DCHECK(it != reference_set_.end());
  if (it->second == 1) {
    reference_set_.erase(it);
    FOR_EACH_OBSERVER(Observer, observers_, OnRemoveSubscription(target));
  } else {
    --it->second;
  }
}

void SubscriptionRefSet::AddObserver(Observer* observer) {
  observers_.AddObserver(observer);
}

void SubscriptionRefSet::RemoveObserver(Observer* observer) {
  observers_.RemoveObserver(observer);
}

Valuebuffer::Valuebuffer(ValuebufferManager* manager, unsigned int client_id)
    : manager_(manager), client_id_(client_id), has_been_bound_(false) {
  manager_->StartTracking(this);
  active_state_map_ = new ValueStateMap();
}

Valuebuffer::~Valuebuffer() {
  if (manager_) {
    for (SubscriptionSet::const_iterator it = subscriptions_.begin();
        it != subscriptions_.end(); ++it) {
      manager_->NotifyRemoveSubscription(*it);
    }
    manager_->StopTracking(this);
    manager_ = NULL;
  }
}

void Valuebuffer::AddSubscription(unsigned int subscription) {
  if (subscriptions_.find(subscription) == subscriptions_.end()) {
    subscriptions_.insert(subscription);
    manager_->NotifyAddSubscription(subscription);
  }
}

void Valuebuffer::RemoveSubscription(unsigned int subscription) {
  SubscriptionSet::iterator it = subscriptions_.find(subscription);
  if (subscriptions_.find(subscription) != subscriptions_.end()) {
    subscriptions_.erase(it);
    manager_->NotifyRemoveSubscription(subscription);
  }
}

bool Valuebuffer::IsSubscribed(unsigned int subscription) {
  return subscriptions_.find(subscription) != subscriptions_.end();
}

const ValueState* Valuebuffer::GetState(unsigned int target) const {
  return active_state_map_->GetState(target);
}

void Valuebuffer::UpdateState(const ValueStateMap* pending_state) {
  DCHECK(pending_state);
  for (SubscriptionSet::const_iterator it = subscriptions_.begin();
       it != subscriptions_.end(); ++it) {
    const ValueState *state = pending_state->GetState(*it);
    if (state != NULL) {
      active_state_map_->UpdateState(*it, *state);
    }
  }
}

ValuebufferManager::ValuebufferManager(SubscriptionRefSet* ref_set,
                                       ValueStateMap* state_map)
    : valuebuffer_count_(0),
      pending_state_map_(state_map),
      subscription_ref_set_(ref_set) {
}

ValuebufferManager::~ValuebufferManager() {
  DCHECK(valuebuffer_map_.empty());
  // If this triggers, that means something is keeping a reference to
  // a Valuebuffer belonging to this.
  CHECK_EQ(valuebuffer_count_, 0u);
}

void ValuebufferManager::Destroy() {
  valuebuffer_map_.clear();
}

void ValuebufferManager::StartTracking(Valuebuffer* /* valuebuffer */) {
  ++valuebuffer_count_;
}

void ValuebufferManager::StopTracking(Valuebuffer* /* valuebuffer */) {
  --valuebuffer_count_;
}

void ValuebufferManager::NotifyAddSubscription(unsigned int target) {
  subscription_ref_set_->AddSubscription(target);
}
void ValuebufferManager::NotifyRemoveSubscription(unsigned int target) {
  subscription_ref_set_->RemoveSubscription(target);
}

void ValuebufferManager::CreateValuebuffer(unsigned int client_id) {
  scoped_refptr<Valuebuffer> valuebuffer(new Valuebuffer(this, client_id));
  std::pair<ValuebufferMap::iterator, bool> result =
      valuebuffer_map_.insert(std::make_pair(client_id, valuebuffer));
  DCHECK(result.second);
}

Valuebuffer* ValuebufferManager::GetValuebuffer(unsigned int client_id) {
  ValuebufferMap::iterator it = valuebuffer_map_.find(client_id);
  return it != valuebuffer_map_.end() ? it->second.get() : NULL;
}

void ValuebufferManager::RemoveValuebuffer(unsigned int client_id) {
  ValuebufferMap::iterator it = valuebuffer_map_.find(client_id);
  if (it != valuebuffer_map_.end()) {
    Valuebuffer* valuebuffer = it->second.get();
    valuebuffer->MarkAsDeleted();
    valuebuffer_map_.erase(it);
  }
}

void ValuebufferManager::UpdateValuebufferState(Valuebuffer* valuebuffer) {
  DCHECK(valuebuffer);
  valuebuffer->UpdateState(pending_state_map_.get());
}

uint32_t ValuebufferManager::ApiTypeForSubscriptionTarget(unsigned int target) {
  switch (target) {
    case GL_MOUSE_POSITION_CHROMIUM:
      return Program::kUniform2i;
  }
  NOTREACHED() << "Unhandled uniform subscription target " << target;
  return Program::kUniformNone;
}

}  // namespace gles2
}  // namespace gpu
