// Copyright (c) 2009 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 "base/logging.h"
#include "base/mac/mac_util.h"
#include "base/string_util.h"
#include "base/sys_string_conversions.h"
#include "base/time.h"
#include "chrome/browser/browser_process.h"
#import "chrome/browser/ui/cocoa/about_ipc_controller.h"

#if defined(IPC_MESSAGE_LOG_ENABLED)

@implementation CocoaLogData

- (id)initWithLogData:(const IPC::LogData&)data {
  if ((self = [super init])) {
    data_ = data;
    // data_.message_name may not have been filled in if it originated
    // somewhere other than the browser process.
    IPC::Logging::GetMessageText(data_.type, &data_.message_name, NULL, NULL);
  }
  return self;
}

- (NSString*)time {
  base::Time t = base::Time::FromInternalValue(data_.sent);
  base::Time::Exploded exploded;
  t.LocalExplode(&exploded);
  return [NSString stringWithFormat:@"%02d:%02d:%02d.%03d",
                   exploded.hour, exploded.minute,
                   exploded.second, exploded.millisecond];
}

- (NSString*)channel {
  return base::SysUTF8ToNSString(data_.channel);
}

- (NSString*)message {
  if (data_.message_name == "") {
    int high = data_.type >> 12;
    int low = data_.type - (high<<12);
    return [NSString stringWithFormat:@"type=(%d,%d) 0x%x,0x%x",
                     high, low, high, low];
  }
  else {
    return base::SysUTF8ToNSString(data_.message_name);
  }
}

- (NSString*)flags {
  return base::SysUTF8ToNSString(data_.flags);
}

- (NSString*)dispatch {
  base::Time sent = base::Time::FromInternalValue(data_.sent);
  int64 delta = (base::Time::FromInternalValue(data_.receive) -
                 sent).InMilliseconds();
  return [NSString stringWithFormat:@"%d", delta ? (int)delta : 0];
}

- (NSString*)process {
  base::TimeDelta delta = (base::Time::FromInternalValue(data_.dispatch) -
                           base::Time::FromInternalValue(data_.receive));
  int64 t = delta.InMilliseconds();
  return [NSString stringWithFormat:@"%d", t ? (int)t : 0];
}

- (NSString*)parameters {
  return base::SysUTF8ToNSString(data_.params);
}

@end

namespace {
AboutIPCController* gSharedController = nil;
}

@implementation AboutIPCController

+ (AboutIPCController*)sharedController {
  if (gSharedController == nil)
    gSharedController = [[AboutIPCController alloc] init];
  return gSharedController;
}

- (id)init {
  NSString* nibpath = [base::mac::MainAppBundle() pathForResource:@"AboutIPC"
                                                          ofType:@"nib"];
  if ((self = [super initWithWindowNibPath:nibpath owner:self])) {
    // Default to all on
    appCache_ = view_ = utilityHost_ = viewHost_ = plugin_ =
      npObject_ = devTools_ = pluginProcessing_ = userString1_ =
      userString2_ = userString3_ = YES;
  }
  return self;
}

- (void)dealloc {
  if (gSharedController == self)
    gSharedController = nil;
  if (g_browser_process)
    g_browser_process->SetIPCLoggingEnabled(false);  // just in case...
  IPC::Logging::GetInstance()->SetConsumer(NULL);
  [super dealloc];
}

- (void)awakeFromNib {
  // Running Chrome with the --ipc-logging switch might cause it to
  // be enabled before the about:ipc window comes up; accomodate.
  [self updateVisibleRunState];

  // We are now able to display information, so let'er rip.
  bridge_.reset(new AboutIPCBridge(self));
  IPC::Logging::GetInstance()->SetConsumer(bridge_.get());
}

// Delegate callback.  Closing the window means there is no more need
// for the me, the controller.
- (void)windowWillClose:(NSNotification*)notification {
  [self autorelease];
}

- (void)updateVisibleRunState {
  if (IPC::Logging::GetInstance()->Enabled())
    [startStopButton_ setTitle:@"Stop"];
  else
    [startStopButton_ setTitle:@"Start"];
}

- (IBAction)startStop:(id)sender {
  g_browser_process->SetIPCLoggingEnabled(
      !IPC::Logging::GetInstance()->Enabled());
  [self updateVisibleRunState];
}

- (IBAction)clear:(id)sender {
  [dataController_ setContent:[NSMutableArray array]];
  [eventCount_ setStringValue:@"0"];
  [filteredEventCount_ setStringValue:@"0"];
  filteredEventCounter_ = 0;
}

// Return YES if we should filter this out; else NO.
// Just to be clear, [@"any string" hasPrefix:@""] returns NO.
- (BOOL)filterOut:(CocoaLogData*)data {
  NSString* name = [data message];
  if ((appCache_) && [name hasPrefix:@"AppCache"])
    return NO;
  if ((view_) && [name hasPrefix:@"ViewMsg"])
    return NO;
  if ((utilityHost_) && [name hasPrefix:@"UtilityHost"])
    return NO;
  if ((viewHost_) && [name hasPrefix:@"ViewHost"])
    return NO;
  if ((plugin_) && [name hasPrefix:@"PluginMsg"])
    return NO;
  if ((npObject_) && [name hasPrefix:@"NPObject"])
    return NO;
  if ((devTools_) && [name hasPrefix:@"DevTools"])
    return NO;
  if ((pluginProcessing_) && [name hasPrefix:@"PluginProcessing"])
    return NO;
  if ((userString1_) && ([name hasPrefix:[userStringTextField1_ stringValue]]))
    return NO;
  if ((userString2_) && ([name hasPrefix:[userStringTextField2_ stringValue]]))
    return NO;
  if ((userString3_) && ([name hasPrefix:[userStringTextField3_ stringValue]]))
    return NO;

  // Special case the unknown type.
  if ([name hasPrefix:@"type="])
    return NO;

  return YES;  // filter out.
}

- (void)log:(CocoaLogData*)data {
  if ([self filterOut:data]) {
    [filteredEventCount_ setStringValue:[NSString stringWithFormat:@"%d",
                                                  ++filteredEventCounter_]];
    return;
  }
  [dataController_ addObject:data];
  NSUInteger count = [[dataController_ arrangedObjects] count];
  // Uncomment if you want scroll-to-end behavior... but seems expensive.
  // [tableView_ scrollRowToVisible:count-1];
  [eventCount_ setStringValue:[NSString stringWithFormat:@"%d", count]];
}

- (void)setDisplayViewMessages:(BOOL)display {
  view_ = display;
}

@end

#endif  // IPC_MESSAGE_LOG_ENABLED