// Copyright (c) 2011 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.
cr.define('gpu', function() {
  /**
   * This class provides a 'bridge' for communicating between javascript and the
   * browser. When run outside of WebUI, e.g. as a regular webpage, it provides
   * synthetic data to assist in testing.
   * @constructor
   */
  function BrowserBridge() {
    // If we are not running inside WebUI, output chrome.send messages
    // to the console to help with quick-iteration debugging.
    this.debugMode_ = (chrome.send === undefined && console.log);
    if (this.debugMode_) {
      var browserBridgeTests = document.createElement('script');
      browserBridgeTests.src = './gpu_internals/browser_bridge_tests.js';
      document.body.appendChild(browserBridgeTests);
    }

    this.nextRequestId_ = 0;
    this.pendingCallbacks_ = [];
    this.logMessages_ = [];

    // Tell c++ code that we are ready to receive GPU Info.
    if (!this.debugMode_) {
      chrome.send('browserBridgeInitialized');
      this.beginRequestClientInfo_();
      this.beginRequestLogMessages_();
    }
  }

  BrowserBridge.prototype = {
    __proto__: cr.EventTarget.prototype,

    applySimulatedData_: function applySimulatedData(data) {
      // set up things according to the simulated data
      this.gpuInfo_ = data.gpuInfo;
      this.gpuMemoryBufferInfo_ = data.gpuMemoryBufferInfo;
      this.clientInfo_ = data.clientInfo;
      this.logMessages_ = data.logMessages;
      cr.dispatchSimpleEvent(this, 'gpuInfoUpdate');
      cr.dispatchSimpleEvent(this, 'gpuMemoryBufferInfoUpdate');
      cr.dispatchSimpleEvent(this, 'clientInfoChange');
      cr.dispatchSimpleEvent(this, 'logMessagesChange');
    },

    /**
     * Returns true if the page is hosted inside Chrome WebUI
     * Helps have behavior conditional to emulate_webui.py
     */
    get debugMode() {
      return this.debugMode_;
    },

    /**
     * Sends a message to the browser with specified args. The
     * browser will reply asynchronously via the provided callback.
     */
    callAsync: function(submessage, args, callback) {
      var requestId = this.nextRequestId_;
      this.nextRequestId_ += 1;
      this.pendingCallbacks_[requestId] = callback;
      if (!args) {
        chrome.send('callAsync', [requestId.toString(), submessage]);
      } else {
        var allArgs = [requestId.toString(), submessage].concat(args);
        chrome.send('callAsync', allArgs);
      }
    },

    /**
     * Called by gpu c++ code when client info is ready.
     */
    onCallAsyncReply: function(requestId, args) {
      if (this.pendingCallbacks_[requestId] === undefined) {
        throw new Error('requestId ' + requestId + ' is not pending');
      }
      var callback = this.pendingCallbacks_[requestId];
      callback(args);
      delete this.pendingCallbacks_[requestId];
    },

    /**
     * Get gpuInfo data.
     */
    get gpuInfo() {
      return this.gpuInfo_;
    },

    /**
     * Called from gpu c++ code when GPU Info is updated.
     */
    onGpuInfoUpdate: function(gpuInfo) {
      this.gpuInfo_ = gpuInfo;
      cr.dispatchSimpleEvent(this, 'gpuInfoUpdate');
    },

    /**
     * Get gpuMemoryBufferInfo data.
     */
    get gpuMemoryBufferInfo() {
      return this.gpuMemoryBufferInfo_;
    },

    /**
     * Called from gpu c++ code when GpuMemoryBuffer Info is updated.
     */
    onGpuMemoryBufferInfoUpdate: function(gpuMemoryBufferInfo) {
      this.gpuMemoryBufferInfo_ = gpuMemoryBufferInfo;
      cr.dispatchSimpleEvent(this, 'gpuMemoryBufferInfoUpdate');
    },

    /**
     * This function begins a request for the ClientInfo. If it comes back
     * as undefined, then we will issue the request again in 250ms.
     */
    beginRequestClientInfo_: function() {
      this.callAsync('requestClientInfo', undefined, (function(data) {
        if (data === undefined) { // try again in 250 ms
          window.setTimeout(this.beginRequestClientInfo_.bind(this), 250);
        } else {
          this.clientInfo_ = data;
          cr.dispatchSimpleEvent(this, 'clientInfoChange');
        }
      }).bind(this));
    },

    /**
     * Returns information about the currently running Chrome build.
     */
    get clientInfo() {
      return this.clientInfo_;
    },

    /**
     * This function checks for new GPU_LOG messages.
     * If any are found, a refresh is triggered.
     */
    beginRequestLogMessages_: function() {
      this.callAsync('requestLogMessages', undefined,
          (function(messages) {
            if (messages.length != this.logMessages_.length) {
              this.logMessages_ = messages;
              cr.dispatchSimpleEvent(this, 'logMessagesChange');
            }
            // check again in 250 ms
            window.setTimeout(this.beginRequestLogMessages_.bind(this), 250);
          }).bind(this));
    },

    /**
     * Returns an array of log messages issued by the GPU process, if any.
     */
    get logMessages() {
      return this.logMessages_;
    },

    /**
     * Returns the value of the "Sandboxed" row.
     */
    isSandboxedForTesting : function() {
      for (i = 0; i < this.gpuInfo_.basic_info.length; ++i) {
        var info = this.gpuInfo_.basic_info[i];
        if (info.description == "Sandboxed")
          return info.value;
      }
      return false;
    }
  };

  return {
    BrowserBridge: BrowserBridge
  };
});
