// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ui.jcef;

import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.ui.JBColor;
import org.cef.CefClient;
import org.cef.browser.CefBrowser;
import org.cef.browser.CefFrame;
import org.cef.handler.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

/**
 * @author tav
 */
public class JCEFHtmlPanel implements Disposable {
  @NotNull
  private final JPanel myPanelWrapper;
  private static final CefClient ourCefClient;
  private static final Map<CefBrowser, JCEFHtmlPanel> ourCefBrowser2Panel = new HashMap<>();
  // browser demands some valid URL for loading html content
  private final static String ourUrl = JCEFHtmlPanel.class.getResource(JCEFHtmlPanel.class.getSimpleName() + ".class").toExternalForm();

  private @NotNull final CefBrowser myCefBrowser;
  private boolean myIsCefBrowserCreated;
  private @Nullable String myHtml;

  // workaround for the UI garbage issue: keep the browser component min until the browser loads html
  private static final boolean USE_SIZE_WORKAROUND = SystemInfo.isWindows;

  static {
    ourCefClient = JBCefApp.getInstance().createClient();
    ourCefClient.addLifeSpanHandler(new CefLifeSpanHandlerAdapter() {
      @Override
      public void onAfterCreated(CefBrowser browser) {
        JCEFHtmlPanel panel = ourCefBrowser2Panel.get(browser);
        if (panel != null) {
          panel.myIsCefBrowserCreated = true;
          if (panel.myHtml != null) {
            browser.loadString(panel.myHtml, ourUrl);
            panel.myHtml = null;
          }
        }
      }
    });
    ourCefClient.addLoadHandler(new CefLoadHandlerAdapter() {
      @Override
      public void onLoadEnd(CefBrowser browser, CefFrame frame, int i) {
        if (USE_SIZE_WORKAROUND) {
          JCEFHtmlPanel panel = ourCefBrowser2Panel.get(browser);
          if (panel != null && browser.getURL() != null && panel.isHtmlLoaded()) {
            browser.getUIComponent().setSize(panel.myPanelWrapper.getSize());
            panel.myPanelWrapper.revalidate();
          }
        }
      }
      @Override
      public void onLoadingStateChange(CefBrowser browser, boolean isLoading, boolean canGoBack, boolean canGoForward) {
        JCEFHtmlPanel panel = ourCefBrowser2Panel.get(browser);
        if (panel != null) {
          panel.onLoadingStateChange(browser, isLoading, canGoBack, canGoForward);
        }
      }
    });
    ourCefClient.addFocusHandler(new CefFocusHandlerAdapter() {
      @Override
      public boolean onSetFocus(CefBrowser browser, FocusSource source) {
        if (source == FocusSource.FOCUS_SOURCE_NAVIGATION) return true;
        // Workaround: JCEF doesn't change current focus on the client side.
        // Clear the focus manually and this will report focus loss to the client
        // and will let focus return to the client on mouse click.
        // tav [todo]: the opposite is inadequate
        KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner();
        return false;
      }
    });
    Disposer.register(ApplicationManager.getApplication(), () -> {
      ourCefBrowser2Panel.clear();
      ourCefClient.dispose();
    });
  }

  public JCEFHtmlPanel() {
    myPanelWrapper = new JPanel(new BorderLayout());
    myPanelWrapper.setBackground(JBColor.background());

    myCefBrowser = ourCefClient.createBrowser("about:blank", false, false);
    ourCefBrowser2Panel.put(myCefBrowser, this);

    if (USE_SIZE_WORKAROUND) {
      myPanelWrapper.setLayout(null);
      myCefBrowser.getUIComponent().setSize(1, 1);
      myPanelWrapper.addComponentListener(new ComponentAdapter() {
        @Override
        public void componentResized(ComponentEvent e) {
          if (isHtmlLoaded()) {
            myCefBrowser.getUIComponent().setSize(myPanelWrapper.getSize());
          }
        }
      });
    }
    myPanelWrapper.add(myCefBrowser.getUIComponent(), BorderLayout.CENTER);
  }

  private boolean isHtmlLoaded() {
    // return ourUrl.equals(myCefBrowser.getURL()); 99% match due to protocols
    return myCefBrowser.getURL().contains(JCEFHtmlPanel.class.getSimpleName());
  }

  @NotNull
  public JComponent getComponent() {
    return myPanelWrapper;
  }

  public CefBrowser getBrowser() {
    return myCefBrowser;
  }

  public void setHtml(@NotNull String html) {
    final String htmlToRender = prepareHtml(html);
    if (!myIsCefBrowserCreated) {
      myHtml = htmlToRender;
    }
    else {
      myCefBrowser.loadString(htmlToRender, ourUrl);
    }
  }

  @NotNull
  protected String prepareHtml(@NotNull String html) {
    return html;
  }

  protected void onLoadingStateChange(CefBrowser browser, boolean isLoading, boolean canGoBack, boolean canGoForward) {
  }

  @Override
  public void dispose() {
    ourCefBrowser2Panel.remove(myCefBrowser);
    myCefBrowser.close(true);
  }
}

@@ -1525,16 +1525,24 @@
 ROUND = 
+false;//
 SystemIn
@@ -1554,16 +1554,41 @@
 Windows;
+ // %5Btav%5D todo: remove it
 %0A%0A  stat

Disable size workaround