package collide.demo.view;

import collide.client.util.Elements;
import collide.gwtc.GwtCompileStatus;
import collide.gwtc.GwtcController;
import collide.gwtc.ui.GwtCompilePlace;
import collide.gwtc.ui.GwtCompilerShell;
import collide.gwtc.ui.GwtStatusListener;
import collide.gwtc.view.GwtcModuleControlView;
import collide.plugin.client.standalone.StandaloneConstants;
import collide.plugin.client.terminal.TerminalLogView;
import com.google.collide.client.code.FileContent;
import com.google.collide.client.search.awesomebox.host.AwesomeBoxComponentHost;
import com.google.collide.client.ui.panel.MultiPanel;
import com.google.collide.client.ui.panel.PanelContent;
import com.google.collide.client.ui.panel.PanelModel;
import com.google.collide.client.ui.panel.PanelModel.Builder;
import com.google.collide.client.util.PathUtil;
import com.google.collide.client.workspace.Header;
import com.google.collide.client.workspace.WorkspaceShell;
import com.google.collide.dto.CompileResponse;
import com.google.collide.dto.CompileResponse.CompilerState;
import com.google.collide.dto.client.DtoClientImpls.GwtRecompileImpl;
import com.google.collide.mvp.ShowableUiComponent;
import com.google.collide.mvp.UiComponent;
import elemental.client.Browser;
import elemental.dom.Document;
import elemental.dom.Element;
import elemental.html.DivElement;
import elemental.html.IFrameElement;
import elemental.util.Collections;
import elemental.util.MapFromStringTo;
import xapi.log.X_Log;
import xapi.util.api.RemovalHandler;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.user.client.Window;

class ControllerView extends com.google.collide.client.ui.panel.MultiPanel.View<PanelModel>
{

  Element header, core;
  public ControllerView(Element element, boolean detached) {
    super(element, detached);
    header = Browser.getDocument().createDivElement();
    element.appendChild(header);
    core = Browser.getDocument().createDivElement();
    element.appendChild(core);
  }

  @Override
  public Element getContentElement() {
    return core;
  }
  @Override
  public Element getHeaderElement() {
    return header;
  }
}

public class DemoView
extends MultiPanel<PanelModel, ControllerView>
{


  private Element browser, editor, compiler, headerEl;
  private ShowableUiComponent<?> bar;
  private final SplitPanel middleBar;
  private final SplitPanel bottomBar;
  private final SplitPanel verticalSplit;
  private Header header;

  public DemoView() {
    super(new ControllerView(findBody(), true));

    middleBar = new SplitPanel(false);
    bottomBar = new SplitPanel(false);
    verticalSplit = new SplitPanel(true);

    headerEl = Browser.getDocument().createDivElement();
    headerEl.getStyle().setHeight(58, "px");
    Browser.getDocument().getBody().appendChild(headerEl);
    Element el = getView().getElement();
    el.getStyle().setTop(58, "px");

    verticalSplit.addChild(middleBar.getElement(), 0.6);
    verticalSplit.addChild(bottomBar.getElement(), 0.4);
    el.appendChild(verticalSplit.getElement());

    bar = new ShowableUiComponent<View<?>>() {
      // We aren't using the toolbar just yet.
      @Override
      public void doHide() {
      }
      @Override
      public void doShow() {
      }
    };


    browser = createElement(-1, 350);
    browser.setId(StandaloneConstants.FILES_PANEL);

    editor = createElement(-1, -1);
    editor.setId(StandaloneConstants.WORKSPACE_PANEL);

    compiler = Browser.getDocument().createDivElement();
    bottomBar.addChild(compiler, 650);

    attachHandlers();

  }

  protected void attachHandlers() {
    Window.addResizeHandler(new ResizeHandler() {
      @Override
      public void onResize(ResizeEvent event) {
        verticalSplit.refresh();
        middleBar.refresh();
        bottomBar.refresh();
      }
    });
  }

  private Element createElement(int index, double width) {
    DivElement el = Browser.getDocument().createDivElement();
    middleBar.addChild(el, width);
    return el;
  }

  private static Element findBody() {
    Document doc = Browser.getDocument();
    Element body = doc.getElementById("gwt_root");
    return body == null ? doc.getBody() : body;
  }

  private void append(Element element) {
    if (element.hasClassName("middle")) {
      middleBar.addChild(wrapChild(element), 0.25);
    } else {
      bottomBar.addChild(wrapChild(element), -1);
    }
  }
  public void append(UiComponent<?> element) {
    Element el = wrapChild(element.getView().getElement());
    if (element instanceof WorkspaceShell) {
      browser.appendChild(el);
    } else if (element instanceof GwtCompilerShell) {
      compiler.appendChild(el);
    } else if (element instanceof Header) {
      header = (Header)element;
      headerEl.appendChild(element.getView().getElement());
    } else if (element instanceof TabPanel) {
      append(((TabPanel) element).getContentElement());
    } else {
      X_Log.warn("Unknown element type",element);
      getView().getElement().appendChild(el);
    }
  }

  private Element wrapChild(Element element) {
    return element;
  }

  FileContent file;

  @Override
  public void setContent(PanelContent panelContent) {
    setContent(panelContent, null);
  }

  @Override
  public void setContent(PanelContent panelContent, PanelModel settings) {
    X_Log.info("PanelController content set",panelContent);
    if (panelContent instanceof FileContent) {
      if (file != null) {
        PathUtil path = file.filePath();
        if (path != null && !path.equals(((FileContent) panelContent).filePath())){
            minimizeFile(path);
          }
          file.onContentDestroyed();
      }
      file = (FileContent)panelContent;

      // Ideally, we would reuse our uibinder instead of just clearing this
      editor.getFirstChildElement().getLastElementChild().setInnerHTML("");
      editor.getFirstChildElement().getLastElementChild().appendChild(file.getContentElement());
    } else if (panelContent instanceof TerminalLogView) {
      // TODO use a tab panel wrapper for log viewers

      bottomBar.addChild(panelContent.getContentElement(), 500, 1);
    } else if (panelContent instanceof GwtCompilerShell){
//      Element el = wrapChild(((UiComponent<?>)panelContent).getView().getElement());
//      bottomBar.addChild(el, 0.3);
    } else if (panelContent instanceof UiComponent){
      append((UiComponent<?>)panelContent);
    } else {
      X_Log.warn("Unhandled panel type: ",panelContent.getClass(), panelContent);
      append(panelContent.getContentElement());
      throw new RuntimeException();
    }
    panelContent.onContentDisplayed();
  }

  public void minimizeFile(final PathUtil path) {
    DivElement el = Browser.getDocument().createDivElement();
  }

  @Override
  public ShowableUiComponent<?> getToolBar() {
    return bar;
  }

  private static final class GwtCompileState {
    IFrameElement el;
    GwtCompileStatus status = GwtCompileStatus.Pending;
    Type logLevel = Type.ALL;
    public GwtcModuleControlView header;
  }

  private final MapFromStringTo<GwtCompileState> compileStates = Collections.mapFromStringTo();
  private GwtCompilerShell gwt;

  public RemovalHandler openIframe(final String id, final String url) {

    final GwtCompileState gwtc = getCompileState(id);
    IFrameElement iframe = gwtc.el;
    if (iframe == null) {
      DivElement sizer = Elements.createDivElement();
      sizer.getStyle().setPosition("absolute");
      sizer.getStyle().setLeft("0px");
      sizer.getStyle().setRight("10px");
      sizer.getStyle().setTop("50px");
      sizer.getStyle().setBottom("20px");

      gwtc.el = iframe = Browser.getDocument().createIFrameElement();
      iframe.getStyle().setWidth("100%");
      iframe.getStyle().setHeight("100%");

      iframe.setAttribute("sandbox", "allow-same-origin allow-scripts allow-top-navigation");
      iframe.setSrc(url);
      sizer.appendChild(iframe);

      final RemovalHandler[] remover = new RemovalHandler[1];
      gwtc.header = GwtcModuleControlView.create(new GwtcController() {
        @Override
        public void onReloadClicked() {
          GwtCompilePlace.PLACE.fireRecompile(id);
        }
        @Override
        public void onCloseClicked() {
          if (remover[0] != null) {
            removeCompileState(id);
            remover[0].remove();
            remover[0] = null;
          }
        }
        @Override
        public void onRefreshClicked() {
          gwtc.el.setSrc(url);
        }
      });

      Element wrapper = gwtc.header.getElement();
      gwtc.header.setHeader(id);
      wrapper.appendChild(sizer);
      wrapper.getStyle().setOverflow("hidden");
      remover[0] = middleBar.addChild(wrapper, 450, 2);
    } else {
      iframe.setSrc("about:blank");
      iframe.setSrc(url);
    }
    iframe.scrollIntoViewIfNeeded(true);
    return new RemovalHandler() {
      @Override
      public void remove() {
        removeCompileState(id);
      }
    };
  }

  protected GwtCompileState getCompileState(String id) {
    GwtCompileState state = compileStates.get(id);
    if (state == null) {
      state = new GwtCompileState();
      compileStates.put(id, state);
    }
    return state;
  }
  protected void removeCompileState(String id) {
    compileStates.remove(id);
    // TODO kill any active compiles?
  }

  @Override
  public Builder<PanelModel> newBuilder() {
    return defaultBuilder();
  }

  public void initGwt(final GwtCompilerShell gwt) {
    this.gwt = gwt;

    gwt.addCompileStateListener(new GwtStatusListener() {

      @Override
      @SuppressWarnings("incomplete-switch")
      public void onLogLevelChange(String module, Type level) {
        GwtCompileState gwtc = getCompileState(module);
        gwtc.logLevel = level;
        switch (level) {
          case ERROR:
            gwtc.status = GwtCompileStatus.Fail;
            if (gwtc.header != null) {
              gwtc.header.setCompileStatus(gwtc.status);
            }
            break;
          case WARN:
            gwtc.status = GwtCompileStatus.Warn;
            if (gwtc.header != null) {
              gwtc.header.setCompileStatus(gwtc.status);
            }
            break;
        }
      }

      @Override
      public void onGwtStatusUpdate(CompileResponse status) {
        GwtCompileState gwtc = getCompileState(status.getModule());
        CompilerState state = status.getCompilerStatus();
        X_Log.info(getClass(), "State change",status.getModule(),state);
        switch (state) {
          case FAILED:
            gwtc.status = GwtCompileStatus.Fail;
            if (gwtc.header != null) {
              gwtc.header.setCompileStatus(gwtc.status);
            }
            break;
          case FINISHED:
          case SERVING:
            if (gwtc.logLevel.ordinal() == Type.ERROR.ordinal()) {
              gwtc.status = GwtCompileStatus.PartialSuccess;
            } else {
              gwtc.status = GwtCompileStatus.Success;
            }
            gwtc.logLevel = Type.WARN;
            if (gwtc.header != null) {
              gwtc.header.setCompileStatus(gwtc.status);
            }
            break;
          case RUNNING:
            gwtc.status = GwtCompileStatus.Good;
            if (gwt.isAutoOpen()) {
              GwtRecompileImpl value = gwt.getValue();
              String key = status.getStaticName() == null ? value.getModule() : status.getStaticName();
              gwt.setMessageKey(value.getModule(), key);
              gwt.getView().getDelegate().openIframe(key, value.getPort());
            }
            if (gwtc.header != null) {
              gwtc.header.setCompileStatus(gwtc.status);
            }
          case BLOCKING:
          case UNLOADED:
        }
      }
    });
    append(gwt);
  }

  public AwesomeBoxComponentHost getAwesomeBoxComponentHost() {
    return header.getAwesomeBoxComponentHost();
  }

}
