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

#ifndef NET_INSTAWEB_REWRITER_PUBLIC_DEPENDENCY_TRACKER_H_
#define NET_INSTAWEB_REWRITER_PUBLIC_DEPENDENCY_TRACKER_H_

#include <memory>

#include "net/instaweb/rewriter/dependencies.pb.h"
#include "pagespeed/kernel/base/basictypes.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
#include "pagespeed/kernel/base/thread_annotations.h"

namespace net_instaweb {

class AbstractMutex;
class RewriteDriver;
class ServerContext;

// Helper for keeping track of what resources a page depends on --- it helps
// decode information saved in property cache, and to assemble information
// collected from the actual page to update it.
//
// The Register/Report methods are thread-safe.
// TODO(morlovich): Might need merging strategy for stability.
class DependencyTracker {
 public:
  // Note: you must also call SetServerContext on this before operation.
  explicit DependencyTracker(RewriteDriver* driver);
  ~DependencyTracker();

  // This needs to be called to help initialize locking.
  void SetServerContext(ServerContext* server_context);

  // Must be called when parsing pages, after pcache has read in.
  void Start();

  // Must be called after the last flush window has been processed, so we
  // know no further processing.
  void FinishedParsing();

  // Notices the tracker that some filter may be trying to compute a dependency
  // asynchronously. This returns an ID which should then be passed to
  // ReportDependencyCandidate();
  int RegisterDependencyCandidate();

  // Reports result of a dependency computation. You must call
  // ReportDependencyCandidate() for every RegisterDependencyCandidate()
  // call, but it can report that the lead didn't checkout by setting dep to
  // nullptr.
  //
  // 'id' must be the result of the corresponding RegisterDependencyCandidate()
  //      call.
  // 'dep' is the dependency summary for the resource in question, and may be
  //       nullptr if there doesn't seem to be something we want to push there
  //       (e.g. it got removed due to combining). *dep will be copied in,
  //       so only needs to be alive for the duration of the call.
  void ReportDependencyCandidate(int id, const Dependency* dep);

  // This is temporary, nicer API coming later.
  const Dependencies* read_in_info() const { return read_in_info_.get(); }

 private:
  void Clear() LOCKS_EXCLUDED(mutex_);
  void ClearLockHeld() EXCLUSIVE_LOCKS_REQUIRED(mutex_);
  void WriteToPropertyCacheIfDone() EXCLUSIVE_LOCKS_REQUIRED(mutex_);

  RewriteDriver* driver_;

  std::unique_ptr<AbstractMutex> mutex_;

  // Info we read in from property cache --- used to make decisions about
  // the current page.
  std::unique_ptr<Dependencies> read_in_info_;

  // Things we compute on the current page.
  // This uses std::map so we can get a stable sort in document order.
  std::map<int, Dependency> computed_info_ GUARDED_BY(mutex_);

  int next_id_ GUARDED_BY(mutex_);
  int outstanding_candidates_ GUARDED_BY(mutex_);

  // Called on ... so we know when we can finally commit results to property
  // cache once number of outstanding candidates goes to 0.
  bool saw_end_ GUARDED_BY(mutex_);

  DISALLOW_COPY_AND_ASSIGN(DependencyTracker);
};


// Compares two Dependency objects based on the order_key field.
class DependencyOrderCompator {
 public:
  bool operator()(const Dependency& a, const Dependency& b);
};

}  // namespace net_instaweb

#endif  // NET_INSTAWEB_REWRITER_PUBLIC_DEPENDENCY_TRACKER_H_
