/*
 * JBoss, Home of Professional Open Source
 * Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
 * as indicated by the @authors tag. All rights reserved.
 * See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.aesh.terminal.http.tty;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.aesh.terminal.tty.TtyTestBase;
import org.junit.After;

import javax.websocket.ClientEndpointConfig;
import javax.websocket.CloseReason;
import javax.websocket.ContainerProvider;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;

/**
 * @author <a href="mailto:[email protected]">Julien Viet</a>
 */
  public abstract class WebsocketTtyTestBase extends TtyTestBase {

  private Endpoint endpoint;
  private Session session;
  private PipedReader in;

  @After
  public void afterWebsocket() throws Exception {
    if (session != null) {
      try {
        session.close();
      } finally {
        in = null;
        session = null;
        endpoint = null;
      }
    }
  }

  @Override
  public boolean checkDisconnected() {
    return session == null || !session.isOpen();
  }

  @Override
  protected void assertConnect(String term) throws Exception {
    if (endpoint != null) {
      throw failure("Already a session");
    }
    CountDownLatch latch = new CountDownLatch(1);
    PipedWriter out = new PipedWriter();
    in = new PipedReader(out);
    endpoint = new Endpoint() {
      @Override
      public void onOpen(Session session, EndpointConfig endpointConfig) {
        session.addMessageHandler(new MessageHandler.Whole<String>() {
          @Override
          public void onMessage(String message) {
            try {
              out.write(message);
            } catch (IOException e) {
              e.printStackTrace();
            }
          }
        });
        latch.countDown();
      }
      @Override
      public void onClose(Session sess, CloseReason closeReason) {
        session = null;
        endpoint = null;
        in = null;
      }
      @Override
      public void onError(Session session, Throwable thr) {
      }
    };
    ClientEndpointConfig clientEndpointConfig = ClientEndpointConfig.Builder.create().build();
    WebSocketContainer webSocketContainer = ContainerProvider.getWebSocketContainer();
    session = webSocketContainer.connectToServer(endpoint, clientEndpointConfig, new URI("http://localhost:8080/ws"));
    latch.await();
  }

  @Override
  protected void assertWrite(String s) throws Exception {
    Map<String, String> msg = new HashMap<>();
    msg.put("action", "read");
    msg.put("data", s);
    ObjectMapper mapper = new ObjectMapper();
    String json = mapper.writeValueAsString(msg);
    session.getBasicRemote().sendText(json);
  }

  @Override
  protected String assertReadString(int len) throws Exception {
    char[] buf = new char[len];
    while (len > 0) {
      int count = in.read(buf, buf.length - len, len);
      if (count == -1) {
        throw failure("Could not read enough");
      }
      len -= count;
    }
    return new String(buf);
  }

  @Override
  protected void assertWriteln(String s) throws Exception {
    assertWrite(s + "\r");
  }

  @Override
  protected void assertDisconnect(boolean clean) throws Exception {
    if (clean) {
      session.close();
    } else {
      // No way ???
      session.close();
    }
  }

  @Override
  protected void resize(int width, int height) throws Exception {
    Map<String, Object> msg = new HashMap<>();
    msg.put("action", "resize");
    msg.put("cols", width);
    msg.put("rows", height);
    ObjectMapper mapper = new ObjectMapper();
    String json = mapper.writeValueAsString(msg);
    session.getBasicRemote().sendText(json);
  }

  @Override
  public void testDifferentCharset() throws Exception {
    // Don't test, charset in this case is always UTF-8 for text frames
  }
}