// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/favicon/ui_bundled/favicon_attributes_provider.h"

#import "base/check.h"
#import "base/functional/bind.h"
#import "base/strings/sys_string_conversions.h"
#import "base/strings/utf_string_conversions.h"
#import "base/task/cancelable_task_tracker.h"
#import "components/favicon/core/fallback_url_util.h"
#import "components/favicon/core/large_icon_service.h"
#import "components/favicon_base/fallback_icon_style.h"
#import "components/favicon_base/favicon_types.h"
#import "ios/chrome/browser/favicon/model/large_icon_cache.h"
#import "ios/chrome/browser/favicon/ui_bundled/favicon_attributes_with_payload.h"
#import "skia/ext/skia_utils_ios.h"
#import "url/gurl.h"

namespace {

// Called when processing the large icon is completed.
void OnLargeIconResultAndCache(
    const GURL& url,
    base::WeakPtr<LargeIconCache> cache,
    void (^favicon_block)(const favicon_base::LargeIconResult&),
    const favicon_base::LargeIconResult& result) {
  favicon_block(result);

  if (cache && (result.bitmap.is_valid() || result.fallback_icon_style)) {
    cache->SetCachedResult(url, result);
  }
}

}  // namespace

@interface FaviconAttributesProvider () {
  // Used to cancel tasks for the LargeIconService.
  base::CancelableTaskTracker _faviconTaskTracker;
}

@end

@implementation FaviconAttributesProvider
@synthesize largeIconService = _largeIconService;
@synthesize minSize = _minSize;
@synthesize faviconSize = _faviconSize;
@synthesize cache = _cache;

- (instancetype)initWithFaviconSize:(CGFloat)faviconSize
                     minFaviconSize:(CGFloat)minFaviconSize
                   largeIconService:
                       (favicon::LargeIconService*)largeIconService {
  DCHECK(largeIconService);
  self = [super init];
  if (self) {
    _faviconSize = faviconSize;
    _minSize = minFaviconSize;
    _largeIconService = largeIconService;
  }

  return self;
}

- (void)fetchFaviconAttributesForURL:(const GURL&)URL
                          completion:(void (^)(FaviconAttributes*))completion {
  GURL blockURL(URL);
  void (^faviconBlock)(const favicon_base::LargeIconResult&) =
      ^(const favicon_base::LargeIconResult& result) {
        FaviconAttributesWithPayload* attributes = nil;
        if (result.bitmap.is_valid()) {
          scoped_refptr<base::RefCountedMemory> data =
              result.bitmap.bitmap_data.get();
          UIImage* favicon =
              [UIImage imageWithData:[NSData dataWithBytes:data->front()
                                                    length:data->size()]];
          attributes =
              [FaviconAttributesWithPayload attributesWithImage:favicon];
          attributes.iconType = result.bitmap.icon_type;
        } else if (result.fallback_icon_style) {
          UIColor* backgroundColor = skia::UIColorFromSkColor(
              result.fallback_icon_style->background_color);
          UIColor* textColor =
              skia::UIColorFromSkColor(result.fallback_icon_style->text_color);
          NSString* monogram =
              base::SysUTF16ToNSString(favicon::GetFallbackIconText(blockURL));
          attributes = [FaviconAttributesWithPayload
              attributesWithMonogram:monogram
                           textColor:textColor
                     backgroundColor:backgroundColor
              defaultBackgroundColor:result.fallback_icon_style->
                                     is_default_background_color];
        }
        DCHECK(attributes);
        completion(attributes);
      };

  base::WeakPtr<LargeIconCache> cacheWeakPtr;
  if (self.cache) {
    std::unique_ptr<favicon_base::LargeIconResult> cachedResult =
        self.cache->GetCachedResult(URL);
    cacheWeakPtr = self.cache->GetWeakPtr();
    if (cachedResult) {
      faviconBlock(*cachedResult);
    }
  }

  // Always call LargeIconService in case the favicon was updated.
  CGFloat faviconSize = [UIScreen mainScreen].scale * self.faviconSize;
  CGFloat minFaviconSize = [UIScreen mainScreen].scale * self.minSize;
  self.largeIconService->GetLargeIconRawBitmapOrFallbackStyleForPageUrl(
      URL, minFaviconSize, faviconSize,
      base::BindRepeating(&OnLargeIconResultAndCache, URL, cacheWeakPtr,
                          faviconBlock),
      &_faviconTaskTracker);
}
@end
