/*
 * 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 "net/instaweb/rewriter/public/fake_filter.h"
#include <memory>

#include "net/instaweb/rewriter/cached_result.pb.h"
#include "net/instaweb/rewriter/public/image_url_encoder.h"
#include "net/instaweb/rewriter/public/resource.h"
#include "net/instaweb/rewriter/public/resource_slot.h"
#include "net/instaweb/rewriter/public/resource_tag_scanner.h"
#include "net/instaweb/rewriter/public/rewrite_context.h"
#include "net/instaweb/rewriter/public/rewrite_driver.h"
#include "net/instaweb/rewriter/public/rewrite_result.h"
#include "net/instaweb/rewriter/public/server_context.h"
#include "pagespeed/kernel/base/function.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
#include "pagespeed/kernel/base/timer.h"
#include "pagespeed/kernel/html/html_element.h"
#include "pagespeed/kernel/http/content_type.h"
#include "pagespeed/kernel/thread/scheduler.h"

namespace net_instaweb {

FakeFilter::Context::~Context() {}

void FakeFilter::Context::RewriteSingle(const ResourcePtr& input,
                                        const OutputResourcePtr& output) {
  if (filter_->exceed_deadline()) {
    // Wake up 1us past the deadline.
    int64 wakeup_us = Driver()->scheduler()->timer()->NowUs() +
                      (Timer::kMsUs * GetRewriteDeadlineAlarmMs() + 1);
    Function* closure =
        MakeFunction(this, &Context::DoRewriteSingle, input, output);
    Driver()->scheduler()->AddAlarmAtUs(wakeup_us, closure);
  } else {
    DoRewriteSingle(input, output);
  }
}

void FakeFilter::Context::DoRewriteSingle(const ResourcePtr input,
                                          OutputResourcePtr output) {
  RewriteResult result = kRewriteFailed;
  GoogleString rewritten;

  if (filter_->enabled()) {
    // TODO(jkarlin): Writing to the filter from a context is not thread
    // safe.
    filter_->IncRewrites();
    StrAppend(&rewritten, input->ExtractUncompressedContents(), ":",
              filter_->id());

    // Set the output type here to make sure that the CachedResult url
    // field has the correct extension for the type.
    const ContentType* output_type = &kContentTypeText;
    if (filter_->output_content_type() != NULL) {
      output_type = filter_->output_content_type();
    } else if (input->type() != NULL) {
      output_type = input->type();
    }
    ResourceVector rv = ResourceVector(1, input);
    if (Driver()->Write(rv, rewritten, output_type, input->charset(),
                        output.get())) {
      result = kRewriteOk;
    }
  }

  RewriteDone(result, 0);
}

GoogleString FakeFilter::Context::UserAgentCacheKey(
    const ResourceContext* resource_context) const {
  if (resource_context != NULL) {
    return ImageUrlEncoder::CacheKeyFromResourceContext(*resource_context);
  }
  return "";
}

FakeFilter::~FakeFilter() {}

void FakeFilter::StartElementImpl(HtmlElement* element) {
  resource_tag_scanner::UrlCategoryVector attributes;
  resource_tag_scanner::ScanElement(element, rewrite_options(), &attributes);
  for (int i = 0, n = attributes.size(); i < n; ++i) {
    if (attributes[i].category == category_) {
      ResourcePtr input_resource(CreateInputResourceOrInsertDebugComment(
          attributes[i].url->DecodedValueOrNull(),
          RewriteDriver::InputRole::kUnknown, element));
      if (input_resource.get() == NULL) {
        return;
      }
      ResourceSlotPtr slot(
          driver()->GetSlot(input_resource, element, attributes[i].url));
      RewriteContext* context = MakeRewriteContext();
      context->AddSlot(slot);
      driver()->InitiateRewrite(context);
    }
  }
}

RewriteContext* FakeFilter::MakeNestedRewriteContext(
    RewriteContext* parent, const ResourceSlotPtr& slot) {
  ResourceContext* resource_context = new ResourceContext;
  if (parent != NULL && parent->resource_context() != NULL) {
    resource_context->CopyFrom(*parent->resource_context());
  }
  RewriteContext* context =
      MakeFakeContext(NULL, parent, resource_context);
  context->AddSlot(slot);
  return context;
}

void FakeFilter::ClearStats() {
  num_rewrites_ = 0;
  num_calls_to_encode_user_agent_ = 0;
}

void FakeFilter::EncodeUserAgentIntoResourceContext(
    ResourceContext* context) const {
  ImageUrlEncoder::SetWebpAndMobileUserAgent(*driver(), context);
  ++num_calls_to_encode_user_agent_;
}

}  // namespace net_instaweb
