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

package org.chromium.sdk.internal.shellprotocol;

import java.io.IOException;

import org.chromium.sdk.Browser;
import org.chromium.sdk.BrowserTab;
import org.chromium.sdk.DebugEventListener;
import org.chromium.sdk.TabDebugEventListener;
import org.chromium.sdk.internal.shellprotocol.tools.ToolHandler;
import org.chromium.sdk.internal.shellprotocol.tools.ToolName;
import org.chromium.sdk.internal.shellprotocol.tools.ToolOutput;
import org.chromium.sdk.internal.shellprotocol.tools.protocol.input.Result;
import org.chromium.sdk.internal.shellprotocol.tools.protocol.output.MessageFactory;
import org.chromium.sdk.internal.shellprotocol.tools.v8debugger.ChromeDevToolSessionManager;
import org.chromium.sdk.internal.shellprotocol.tools.v8debugger.ChromeDevToolSessionManager.AttachmentFailureException;
import org.chromium.sdk.internal.transport.Connection;
import org.chromium.sdk.internal.transport.Message;
import org.chromium.sdk.internal.v8native.DebugSession;
import org.chromium.sdk.internal.v8native.JavascriptVmImpl;
import org.chromium.sdk.util.MethodIsBlockingException;

/**
 * A default, thread-safe implementation of the BrowserTab interface.
 */
public class BrowserTabImpl extends JavascriptVmImpl implements BrowserTab {

  /** Tab ID as reported by the DevTools server. */
  private final int tabId;

  /** The primary tab URL. */
  private volatile String url;

  private final SessionManager.Ticket<BrowserImpl.Session> connectionTicket;

  private final ChromeDevToolSessionManager devToolSessionManager;

  /** The listener to report debug events to. */
  private DebugEventListener debugEventListener = null;

  /** The listener to report browser-related debug events to. */
  private TabDebugEventListener tabDebugEventListener = null;

  public BrowserTabImpl(int tabId, Connection connection,
      SessionManager.Ticket<BrowserImpl.Session> ticket) throws IOException {
    this.tabId = tabId;
    this.connectionTicket = ticket;
    String tabIdString = String.valueOf(tabId);
    ChromeDevToolOutput chromeDevToolOutput = new ChromeDevToolOutput(tabIdString, connection);
    this.devToolSessionManager = new ChromeDevToolSessionManager(this, chromeDevToolOutput);

    ToolHandler toolHandler = devToolSessionManager.getToolHandler();
    // After this statement we are responsible for dismissing our ticket (we do it via eos message).
    getBrowserConnectionSession().registerTab(tabId, toolHandler,
        this.devToolSessionManager.getDebugSession());
  }

  public String getUrl() {
    return url;
  }

  public void setUrl(String newUrl) {
    url = newUrl;
  }

  public int getId() {
    return tabId;
  }

  @Override
  public DebugSession getDebugSession() {
    return devToolSessionManager.getDebugSession();
  }

  public synchronized TabDebugEventListener getTabDebugEventListener() {
    return tabDebugEventListener;
  }

  public Browser getBrowser() {
    return getBrowserConnectionSession().getBrowser();
  }

  public BrowserImpl.Session getBrowserConnectionSession() {
    return connectionTicket.getSession();
  }

  synchronized void attach(TabDebugEventListener listener)
      throws IOException, MethodIsBlockingException {
    this.tabDebugEventListener = listener;
    this.debugEventListener = listener.getDebugEventListener();

    boolean normalExit = false;
    try {
      Result result;
      try {
        result = devToolSessionManager.attachToTab();
      } catch (AttachmentFailureException e) {
        throw newIOException(null, e);
      }
      if (Result.OK != result) {
        throw new IOException("Failed to attach with result: " + result);
      }
      normalExit = true;
    } finally {
      if (!normalExit) {
        devToolSessionManager.cutTheLineMyself();
      }
    }
  }

  public boolean detach() {
    Result result = devToolSessionManager.detachFromTab();
    return Result.OK == result;
  }

  public boolean isAttached() {
    return devToolSessionManager.isAttachedForUi();
  }

  public void sessionTerminated() {
    //browserSession.sessionTerminated(this.tabId);
  }

  public ToolHandler getV8ToolHandler() {
    return devToolSessionManager.getToolHandler();
  }

  public ChromeDevToolSessionManager getSessionManager() {
    return devToolSessionManager;
  }

  public void handleEosFromToolService() {
    getBrowserConnectionSession().unregisterTab(tabId);
    connectionTicket.dismiss();
  }

  private static class ChromeDevToolOutput implements ToolOutput {
    private final String destination;
    private final Connection connection;

    ChromeDevToolOutput(String destination, Connection connection) {
      this.destination = destination;
      this.connection = connection;
    }

    public void send(String content) {
      Message message =
          MessageFactory.createMessage(ToolName.V8_DEBUGGER.value, destination, content);
      connection.send(message);
    }

    public void runInDispatchThread(Runnable callback) {
      connection.runInDispatchThread(callback);
    }
  }

  public DebugEventListener getDebugEventListener() {
    return debugEventListener;
  }
}
